├── .github ├── dependabot.yml └── workflows │ └── pipeline.yml ├── .gitignore ├── .goreleaser.yaml ├── LICENSE ├── Makefile ├── README.md ├── flag.go ├── flag_test.go ├── go.mod ├── go.sum ├── main.go ├── pkg ├── cert │ ├── asn.go │ ├── cert.go │ ├── cert_test.go │ ├── extensions.go │ ├── location.go │ ├── location_test.go │ ├── testdata │ │ ├── bundle.pem │ │ ├── cert.pem │ │ ├── intermediate_same_issuer_and_subject.pem │ │ └── root_with_authority_key_id.pem │ └── util_test.go └── print │ ├── expiry.go │ ├── expiry_test.go │ ├── location.go │ └── pem.go └── vendor ├── github.com ├── davecgh │ └── go-spew │ │ ├── LICENSE │ │ └── spew │ │ ├── bypass.go │ │ ├── bypasssafe.go │ │ ├── common.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── dump.go │ │ ├── format.go │ │ └── spew.go ├── pmezard │ └── go-difflib │ │ ├── LICENSE │ │ └── difflib │ │ └── difflib.go └── stretchr │ └── testify │ ├── LICENSE │ ├── assert │ ├── assertion_compare.go │ ├── assertion_format.go │ ├── assertion_format.go.tmpl │ ├── assertion_forward.go │ ├── assertion_forward.go.tmpl │ ├── assertion_order.go │ ├── assertions.go │ ├── doc.go │ ├── errors.go │ ├── forward_assertions.go │ ├── http_assertions.go │ └── yaml │ │ ├── yaml_custom.go │ │ ├── yaml_default.go │ │ └── yaml_fail.go │ └── require │ ├── doc.go │ ├── forward_requirements.go │ ├── require.go │ ├── require.go.tmpl │ ├── require_forward.go │ ├── require_forward.go.tmpl │ └── requirements.go ├── gopkg.in └── yaml.v3 │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── emitterc.go │ ├── encode.go │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go └── modules.txt /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | groups: 8 | go: 9 | patterns: 10 | - '*' 11 | 12 | - package-ecosystem: github-actions 13 | directory: /.github 14 | schedule: 15 | interval: daily 16 | groups: 17 | github-actions: 18 | patterns: 19 | - '*' 20 | -------------------------------------------------------------------------------- /.github/workflows/pipeline.yml: -------------------------------------------------------------------------------- 1 | name: pipeline 2 | 3 | on: [push] 4 | 5 | jobs: 6 | go: 7 | uses: pete911/github-actions/.github/workflows/go.yml@main 8 | go-release: 9 | needs: 10 | - go 11 | permissions: 12 | contents: write 13 | uses: pete911/github-actions/.github/workflows/go-releaser.yml@main 14 | secrets: 15 | PUBLIC_REPO_TOKEN: ${{ secrets.PUBLIC_REPO_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /certinfo 2 | 3 | dist/ 4 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - env: 3 | - CGO_ENABLED=0 4 | goos: 5 | - linux 6 | - windows 7 | - darwin 8 | goarch: 9 | - amd64 10 | - arm64 11 | ldflags: 12 | - -X main.Version={{.Version}} 13 | 14 | archives: 15 | - formats: [ 'tar.gz' ] 16 | format_overrides: 17 | - goos: windows 18 | formats: [ 'zip' ] 19 | checksum: 20 | name_template: 'checksums.txt' 21 | snapshot: 22 | version_template: "{{ incpatch .Version }}-next" 23 | changelog: 24 | sort: asc 25 | filters: 26 | exclude: 27 | - '^docs:' 28 | - '^test:' 29 | release: 30 | github: 31 | owner: pete911 32 | name: certinfo 33 | brews: 34 | - repository: 35 | owner: pete911 36 | name: homebrew-tap 37 | token: "{{ .Env.GITHUB_TOKEN }}" 38 | name: certinfo 39 | homepage: "https://github.com/pete911/certinfo" 40 | description: "Print x509 certificate info." 41 | directory: Formula 42 | install: | 43 | bin.install "certinfo" 44 | test: | 45 | assert_match /Usage/, shell_output("#{bin}/certinfo -h", 0) 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Peter Reisinger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION ?= dev 2 | 3 | .DEFAULT_GOAL := build 4 | 5 | test: 6 | go fmt ./... 7 | go vet ./... 8 | go clean -testcache && go test -cover ./... 9 | .PHONY:test 10 | 11 | build: test 12 | go build -ldflags "-X main.Version=${VERSION}" -mod vendor 13 | .PHONY:build 14 | 15 | install: test 16 | go install -ldflags "-X main.Version=${VERSION}" -mod vendor 17 | .PHONY:install 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # print x509 certificate info 2 | 3 | [![pipeline](https://github.com/pete911/certinfo/actions/workflows/pipeline.yml/badge.svg)](https://github.com/pete911/certinfo/actions/workflows/pipeline.yml) 4 | 5 | Similar to `openssl x509 -in -text` command, but handles chains, multiple files and TCP addresses. TLS/SSL 6 | version prints as well when using TCP address argument. 7 | 8 | ## usage 9 | 10 | ```shell script 11 | certinfo [flags] [| ...] 12 | ``` 13 | 14 | **file** argument can be: 15 | - **local file path** `certinfo ` 16 | - **TCP network address** `certinfo ` e.g. `certinfo google.com:443` 17 | - **stdin** `echo "" | certinfo` 18 | 19 | ``` 20 | +-------------------------------------------------------------------------------------------------------------------+ 21 | | optional flags | 22 | +---------------+---------------------------------------------------------------------------------------------------+ 23 | | -chains | whether to print verified chains as well | 24 | | -expiry | print expiry of certificates | 25 | | -extensions | whether to print extensions | 26 | | -insecure | whether a client verifies the server's certificate chain and host name (only applicable for host) | 27 | | -issuer-like | print certificates with subject field containing supplied string | 28 | | -no-duplicate | do not print duplicate certificates | 29 | | -no-expired | do not print expired certificates | 30 | | -pem | whether to print pem as well | 31 | | -pem-only | whether to print only pem (useful for downloading certs from host) | 32 | | -server-name | verify the hostname on the returned certificates, useful for testing SNI | 33 | | -sort-expiry | sort certificates by expiration date | 34 | | -subject-like | print certificates with issuer field containing supplied string | 35 | | -version | certinfo version | 36 | | -help | help | 37 | +---------------+---------------------------------------------------------------------------------------------------+ 38 | ``` 39 | 40 | If you need to run against multiple hosts, it is faster to execute command with multiple arguments e.g. 41 | `certinfo -insecure -expiry google.com:443 amazon.com:443 ...` rather than executing command multiple times. Args are 42 | executed concurrently and much faster. 43 | 44 | Flags can be set as env. variable as well (`CERTINFO_=true` e.g. `CERTINFO_INSECURE=true`) and can be then 45 | overridden with a flag. 46 | 47 | ## download 48 | 49 | - [binary](https://github.com/pete911/certinfo/releases) 50 | 51 | ## build/install 52 | 53 | ### brew 54 | 55 | - add tap `brew tap pete911/tap` 56 | - install `brew install certinfo` 57 | 58 | ### go 59 | 60 | [go](https://golang.org/dl/) has to be installed. 61 | - build `make build` 62 | - install `make install` 63 | 64 | ## release 65 | 66 | Releases are published when the new tag is created e.g. 67 | `git tag -m "add super cool feature" v1.0.0 && git push --follow-tags` 68 | 69 | ## examples 70 | 71 | ### remove expired and malformed certs 72 | 73 | - `--pem-only` flag returns only pem blocks that can be parsed and are type of certificate 74 | - `--no-expired` flag removes expired certificates 75 | 76 | `certinfo --pem-only --no-expired .pem > .pem` 77 | 78 | ### info/verbose 79 | 80 | `certinfo vault.com:443` 81 | ``` 82 | --- [vault.com:443 TLS 1.2] --- 83 | Version: 3 84 | Serial Number: 16280914906313700456 85 | Signature Algorithm: SHA256-RSA 86 | Type: end-entity 87 | Issuer: CN=Go Daddy Secure Certificate Authority - G2,OU=http://certs.godaddy.com/repository/,O=GoDaddy.com\, Inc.,L=Scottsdale,ST=Arizona,C=US 88 | Validity 89 | Not Before: Mar 24 10:44:12 2022 UTC 90 | Not After : Mar 19 13:04:10 2023 UTC 91 | Subject: CN=*.vault.com 92 | DNS Names: *.vault.com, vault.com 93 | IP Addresses: 94 | Authority Key Id: 40c2bd278ecc348330a233d7fb6cb3f0b42c80ce 95 | Subject Key Id : 6b8c8d1da18cbb8cd64437ed0a9c8a0fef673821 96 | Key Usage: Digital Signature, Key Encipherment 97 | Ext Key Usage: Server Auth, Client Auth 98 | CA: false 99 | 100 | Version: 3 101 | Serial Number: 7 102 | Signature Algorithm: SHA256-RSA 103 | Type: intermediate 104 | Issuer: CN=Go Daddy Root Certificate Authority - G2,O=GoDaddy.com\, Inc.,L=Scottsdale,ST=Arizona,C=US 105 | Validity 106 | Not Before: May 3 07:00:00 2011 UTC 107 | Not After : May 3 07:00:00 2031 UTC 108 | Subject: CN=Go Daddy Secure Certificate Authority - G2,OU=http://certs.godaddy.com/repository/,O=GoDaddy.com\, Inc.,L=Scottsdale,ST=Arizona,C=US 109 | DNS Names: 110 | IP Addresses: 111 | Authority Key Id: 3a9a8507106728b6eff6bd05416e20c194da0fde 112 | Subject Key Id : 40c2bd278ecc348330a233d7fb6cb3f0b42c80ce 113 | Key Usage: Cert Sign, CRL Sign 114 | Ext Key Usage: 115 | CA: true 116 | 117 | Version: 3 118 | Serial Number: 1828629 119 | Signature Algorithm: SHA256-RSA 120 | Type: intermediate 121 | Issuer: OU=Go Daddy Class 2 Certification Authority,O=The Go Daddy Group\, Inc.,C=US 122 | Validity 123 | Not Before: Jan 1 07:00:00 2014 UTC 124 | Not After : May 30 07:00:00 2031 UTC 125 | Subject: CN=Go Daddy Root Certificate Authority - G2,O=GoDaddy.com\, Inc.,L=Scottsdale,ST=Arizona,C=US 126 | DNS Names: 127 | IP Addresses: 128 | Authority Key Id: d2c4b0d291d44c1171b361cb3da1fedda86ad4e3 129 | Subject Key Id : 3a9a8507106728b6eff6bd05416e20c194da0fde 130 | Key Usage: Cert Sign, CRL Sign 131 | Ext Key Usage: 132 | CA: true 133 | 134 | --- 1 verified chains --- 135 | ``` 136 | 137 | ### info/expiry 138 | 139 | `certinfo -expiry google.com:443` 140 | ``` 141 | --- [google.com:443 TLS 1.3] --- 142 | Subject: CN=*.google.com 143 | Expiry: 2 months 4 days 14 hours 41 minutes 144 | 145 | Subject: CN=GTS CA 1C3,O=Google Trust Services LLC,C=US 146 | Expiry: 4 years 6 months 19 days 5 hours 29 minutes 147 | 148 | Subject: CN=GTS Root R1,O=Google Trust Services LLC,C=US 149 | Expiry: 4 years 10 months 17 days 4 hours 29 minutes 150 | ``` 151 | 152 | ### show certificate with specific subject 153 | This example shows AWS RDS certificates for specific region (we can also see AWS started using 100 years expiration) 154 | - show only eu-west-2 certs `curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem | certinfo -issuer-like eu-west-2` 155 | - download only eu-west-2 certs `curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem | certinfo -issuer-like eu-west-2 -pem-only > rds-eu-west-2.pem` 156 | 157 | ### verify SNI certificates 158 | Specific host can be set by `server-name` flag. This is useful if we need to verify that load balancer is correctly 159 | using certificates for different hosts: `certinfo -server-name ` e.g. 160 | `certinfo -server-name tabletmag.com cname.vercel-dns.com:443` (tabletmag certificate behind vercel). 161 | 162 | ### local root certs 163 | 164 | - linux `ls -d /etc/ssl/certs/* | grep '.pem' | xargs certinfo -expiry` 165 | - mac `cat /etc/ssl/cert.pem | certinfo -expiry` 166 | -------------------------------------------------------------------------------- /flag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | type Flags struct { 11 | Usage func() 12 | Expiry bool 13 | NoDuplicate bool 14 | NoExpired bool 15 | SortExpiry bool 16 | SubjectLike string 17 | IssuerLike string 18 | ServerName string 19 | Insecure bool 20 | Chains bool 21 | Extensions bool 22 | Pem bool 23 | PemOnly bool 24 | Verbose bool 25 | Version bool 26 | Args []string 27 | } 28 | 29 | func ParseFlags() (Flags, error) { 30 | 31 | var flags Flags 32 | flagSet := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 33 | flagSet.BoolVar(&flags.Expiry, "expiry", getBoolEnv("CERTINFO_EXPIRY", false), 34 | "print expiry of certificates") 35 | flagSet.BoolVar(&flags.NoDuplicate, "no-duplicate", getBoolEnv("CERTINFO_NO_DUPLICATE", false), 36 | "do not print duplicate certificates") 37 | flagSet.BoolVar(&flags.NoExpired, "no-expired", getBoolEnv("CERTINFO_NO_EXPIRED", false), 38 | "do not print expired certificates") 39 | flagSet.BoolVar(&flags.SortExpiry, "sort-expiry", getBoolEnv("CERTINFO_SORT_EXPIRY", false), 40 | "sort certificates by expiration date") 41 | flagSet.StringVar(&flags.SubjectLike, "subject-like", getStringEnv("CERTINFO_SUBJECT_LIKE", ""), 42 | "print certificates with issuer field containing supplied string") 43 | flagSet.StringVar(&flags.IssuerLike, "issuer-like", getStringEnv("CERTINFO_ISSUER_LIKE", ""), 44 | "print certificates with subject field containing supplied string") 45 | flagSet.StringVar(&flags.ServerName, "server-name", getStringEnv("CERTINFO_SERVER_NAME", ""), 46 | "verify the hostname on the returned certificates, useful for testing SNI") 47 | flagSet.BoolVar(&flags.Insecure, "insecure", getBoolEnv("CERTINFO_INSECURE", false), 48 | "whether a client verifies the server's certificate chain and host name (only applicable for host)") 49 | flagSet.BoolVar(&flags.Chains, "chains", getBoolEnv("CERTINFO_CHAINS", false), 50 | "whether to print verified chains as well (only applicable for host)") 51 | flagSet.BoolVar(&flags.Extensions, "extensions", getBoolEnv("CERTINFO_EXTENSIONS", false), 52 | "whether to print extensions") 53 | flagSet.BoolVar(&flags.Pem, "pem", getBoolEnv("CERTINFO_PEM", false), 54 | "whether to print pem as well") 55 | flagSet.BoolVar(&flags.PemOnly, "pem-only", getBoolEnv("CERTINFO_PEM_ONLY", false), 56 | "whether to print only pem (useful for downloading certs from host)") 57 | flagSet.BoolVar(&flags.Verbose, "verbose", getBoolEnv("CERTINFO_VERBOSE", false), 58 | "verbose logging") 59 | flagSet.BoolVar(&flags.Version, "version", getBoolEnv("CERTINFO_VERSION", false), 60 | "certinfo version") 61 | 62 | flagSet.Usage = func() { 63 | fmt.Fprint(flagSet.Output(), "Usage: certinfo [flags] [| ...]\n") 64 | flagSet.PrintDefaults() 65 | } 66 | flags.Usage = flagSet.Usage 67 | 68 | if err := flagSet.Parse(os.Args[1:]); err != nil { 69 | return Flags{}, err 70 | } 71 | flags.Args = flagSet.Args() 72 | return flags, nil 73 | } 74 | 75 | func getStringEnv(envName string, defaultValue string) string { 76 | 77 | if env, ok := os.LookupEnv(envName); ok { 78 | return env 79 | } 80 | return defaultValue 81 | } 82 | 83 | func getBoolEnv(envName string, defaultValue bool) bool { 84 | 85 | env, ok := os.LookupEnv(envName) 86 | if !ok { 87 | return defaultValue 88 | } 89 | 90 | if intValue, err := strconv.ParseBool(env); err == nil { 91 | return intValue 92 | } 93 | return defaultValue 94 | } 95 | -------------------------------------------------------------------------------- /flag_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestParseFlags(t *testing.T) { 12 | 13 | t.Run("given empty args and env vars then flags are set to default values", func(t *testing.T) { 14 | 15 | setInput(t, nil, nil) 16 | 17 | flags, err := ParseFlags() 18 | require.NoError(t, err) 19 | 20 | assert.False(t, flags.Expiry) 21 | assert.False(t, flags.Insecure) 22 | assert.False(t, flags.Chains) 23 | assert.False(t, flags.Pem) 24 | assert.False(t, flags.PemOnly) 25 | assert.False(t, flags.Version) 26 | assert.Empty(t, flags.Args) 27 | }) 28 | 29 | t.Run("given args are set and env vars empty then flags are set to provided args", func(t *testing.T) { 30 | 31 | setInput(t, []string{"flag", 32 | "-expiry=true", 33 | "-insecure=true", 34 | "-chains=true", 35 | "-chains=true", 36 | "-pem=true", 37 | "-pem-only=true", 38 | "-version=true", 39 | }, nil) 40 | 41 | flags, err := ParseFlags() 42 | require.NoError(t, err) 43 | 44 | assert.True(t, flags.Expiry) 45 | assert.True(t, flags.Insecure) 46 | assert.True(t, flags.Chains) 47 | assert.True(t, flags.Pem) 48 | assert.True(t, flags.PemOnly) 49 | assert.True(t, flags.Version) 50 | assert.Empty(t, flags.Args) 51 | }) 52 | 53 | t.Run("given args are not set and env vars are set then flags are set to provided env vars", func(t *testing.T) { 54 | 55 | setInput(t, []string{"flag"}, map[string]string{ 56 | "CERTINFO_EXPIRY": "true", 57 | "CERTINFO_INSECURE": "true", 58 | "CERTINFO_CHAINS": "true", 59 | "CERTINFO_PEM": "true", 60 | "CERTINFO_PEM_ONLY": "true", 61 | "CERTINFO_VERSION": "true", 62 | }) 63 | 64 | flags, err := ParseFlags() 65 | require.NoError(t, err) 66 | 67 | assert.True(t, flags.Expiry) 68 | assert.True(t, flags.Insecure) 69 | assert.True(t, flags.Chains) 70 | assert.True(t, flags.Pem) 71 | assert.True(t, flags.PemOnly) 72 | assert.True(t, flags.Version) 73 | assert.Empty(t, flags.Args) 74 | }) 75 | 76 | t.Run("given args are set and env vars are set then flags are set to provided args", func(t *testing.T) { 77 | 78 | setInput(t, []string{"flag", 79 | "-insecure=true", 80 | "-chains=true", 81 | "-pem=false", 82 | "-version=false", 83 | }, map[string]string{ 84 | "CERTINFO_EXPIRY": "true", 85 | "CERTINFO_CHAINS": "true", 86 | "CERTINFO_PEM": "true", 87 | "CERTINFO_PEM_ONLY": "true", 88 | "CERTINFO_VERSION": "true", 89 | }) 90 | 91 | flags, err := ParseFlags() 92 | require.NoError(t, err) 93 | 94 | assert.True(t, flags.Expiry) 95 | assert.True(t, flags.Insecure) 96 | assert.True(t, flags.Chains) 97 | assert.False(t, flags.Pem) 98 | assert.True(t, flags.PemOnly) 99 | assert.False(t, flags.Version) 100 | assert.Empty(t, flags.Args) 101 | }) 102 | } 103 | 104 | // --- helper functions --- 105 | 106 | func setInput(t *testing.T, args []string, env map[string]string) { 107 | 108 | osArgs := os.Args 109 | if args == nil { 110 | args = []string{"test"} 111 | } 112 | 113 | os.Args = args 114 | for k, v := range env { 115 | os.Setenv(k, v) 116 | } 117 | 118 | t.Cleanup(func() { 119 | os.Args = osArgs 120 | for k := range env { 121 | os.Unsetenv(k) 122 | } 123 | }) 124 | } 125 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pete911/certinfo 2 | 3 | go 1.24 4 | 5 | require github.com/stretchr/testify v1.10.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pete911/certinfo/pkg/cert" 6 | "github.com/pete911/certinfo/pkg/print" 7 | "log/slog" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | ) 13 | 14 | var Version = "dev" 15 | 16 | func main() { 17 | 18 | flags, err := ParseFlags() 19 | if err != nil { 20 | fmt.Println(err.Error()) 21 | os.Exit(1) 22 | } 23 | setLogger(flags.Verbose) 24 | 25 | if flags.Version { 26 | fmt.Println(Version) 27 | os.Exit(0) 28 | } 29 | 30 | certificatesFiles := LoadCertificatesLocations(flags) 31 | if flags.NoExpired { 32 | certificatesFiles = certificatesFiles.RemoveExpired() 33 | } 34 | if flags.NoDuplicate { 35 | certificatesFiles = certificatesFiles.RemoveDuplicates() 36 | } 37 | if flags.SubjectLike != "" { 38 | certificatesFiles = certificatesFiles.SubjectLike(flags.SubjectLike) 39 | } 40 | if flags.IssuerLike != "" { 41 | certificatesFiles = certificatesFiles.IssuerLike(flags.IssuerLike) 42 | } 43 | if flags.SortExpiry { 44 | certificatesFiles = certificatesFiles.SortByExpiry() 45 | } 46 | if flags.Expiry { 47 | print.Expiry(certificatesFiles) 48 | return 49 | } 50 | if flags.PemOnly { 51 | print.Pem(certificatesFiles, flags.Chains) 52 | return 53 | } 54 | print.Locations(certificatesFiles, flags.Chains, flags.Pem, flags.Extensions) 55 | } 56 | 57 | func setLogger(verbose bool) { 58 | level := slog.LevelError 59 | if verbose { 60 | level = slog.LevelDebug 61 | } 62 | slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level}))) 63 | } 64 | 65 | func LoadCertificatesLocations(flags Flags) cert.CertificateLocations { 66 | 67 | var certificateLocations cert.CertificateLocations 68 | if len(flags.Args) > 0 { 69 | certificateLocations = append(certificateLocations, loadFromArgs(flags.Args, flags.ServerName, flags.Insecure)...) 70 | } 71 | 72 | if isStdin() { 73 | certificateLocations = append(certificateLocations, cert.LoadCertificateFromStdin()) 74 | } 75 | 76 | if len(certificateLocations) > 0 { 77 | return certificateLocations 78 | } 79 | 80 | // no stdin and no args 81 | flags.Usage() 82 | os.Exit(0) 83 | return nil 84 | } 85 | 86 | func loadFromArgs(args []string, serverName string, insecure bool) cert.CertificateLocations { 87 | 88 | out := make(chan cert.CertificateLocation) 89 | go func() { 90 | var wg sync.WaitGroup 91 | for _, arg := range args { 92 | wg.Add(1) 93 | go func() { 94 | defer wg.Done() 95 | if isTCPNetworkAddress(arg) { 96 | out <- cert.LoadCertificatesFromNetwork(arg, serverName, insecure) 97 | return 98 | } 99 | out <- cert.LoadCertificatesFromFile(arg) 100 | }() 101 | } 102 | wg.Wait() 103 | close(out) 104 | }() 105 | 106 | // load certificates from the channel 107 | certsByArgs := make(map[string]cert.CertificateLocation) 108 | for location := range out { 109 | certsByArgs[location.Path] = location 110 | } 111 | 112 | // sort certificates by input arguments 113 | var certsSortedByArgs cert.CertificateLocations 114 | for _, arg := range args { 115 | certsSortedByArgs = append(certsSortedByArgs, certsByArgs[arg]) 116 | } 117 | return certsSortedByArgs 118 | } 119 | 120 | func isTCPNetworkAddress(arg string) bool { 121 | 122 | parts := strings.Split(arg, ":") 123 | if len(parts) != 2 { 124 | return false 125 | } 126 | if _, err := strconv.Atoi(parts[1]); err != nil { 127 | return false 128 | } 129 | return true 130 | } 131 | 132 | func isStdin() bool { 133 | 134 | info, err := os.Stdin.Stat() 135 | if err != nil { 136 | fmt.Printf("checking stdin: %v\n", err) 137 | return false 138 | } 139 | 140 | if (info.Mode() & os.ModeCharDevice) == 0 { 141 | return true 142 | } 143 | return false 144 | } 145 | -------------------------------------------------------------------------------- /pkg/cert/cert.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "bytes" 5 | "crypto/x509" 6 | "crypto/x509/pkix" 7 | "encoding/asn1" 8 | "encoding/hex" 9 | "encoding/pem" 10 | "errors" 11 | "fmt" 12 | "log/slog" 13 | "slices" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | const certificateBlockType = "CERTIFICATE" 19 | 20 | var ( 21 | // order is important! 22 | keyUsages = []string{ 23 | "Digital Signature", 24 | "Content Commitment", 25 | "Key Encipherment", 26 | "Data Encipherment", 27 | "Key Agreement", 28 | "Cert Sign", 29 | "CRL Sign", 30 | "Encipher Only", 31 | "Decipher Only", 32 | } 33 | // order is important! 34 | extKeyUsages = []string{ 35 | "Any", 36 | "Server Auth", 37 | "Client Auth", 38 | "Code Signing", 39 | "Email Protection", 40 | "IPSEC End System", 41 | "IPSEC Tunnel", 42 | "IPSEC User", 43 | "Time Stamping", 44 | "OCSP Signing", 45 | "Microsoft Server Gated Crypto", 46 | "Netscape Server Gated Crypto", 47 | "Microsoft Commercial Code Signing", 48 | "Microsoft Kernel Code Signing", 49 | } 50 | ) 51 | 52 | type Certificates []Certificate 53 | 54 | func (c Certificates) RemoveExpired() Certificates { 55 | var out Certificates 56 | for i := range c { 57 | if !c[i].IsExpired() { 58 | out = append(out, c[i]) 59 | } 60 | } 61 | return out 62 | } 63 | 64 | func (c Certificates) RemoveDuplicates() Certificates { 65 | var out Certificates 66 | savedSet := map[string]struct{}{} 67 | for i := range c { 68 | stringPem := string(c[i].ToPEM()) 69 | if _, ok := savedSet[stringPem]; !ok { 70 | savedSet[stringPem] = struct{}{} 71 | out = append(out, c[i]) 72 | } 73 | } 74 | return out 75 | } 76 | 77 | func (c Certificates) SubjectLike(subject string) Certificates { 78 | var out Certificates 79 | for i := range c { 80 | if strings.Contains(c[i].SubjectString(), subject) { 81 | out = append(out, c[i]) 82 | } 83 | } 84 | return out 85 | } 86 | 87 | func (c Certificates) IssuerLike(issuer string) Certificates { 88 | var out Certificates 89 | for i := range c { 90 | if strings.Contains(c[i].x509Certificate.Issuer.String(), issuer) { 91 | out = append(out, c[i]) 92 | } 93 | } 94 | return out 95 | } 96 | 97 | func (c Certificates) SortByExpiry() Certificates { 98 | slices.SortFunc(c, func(a, b Certificate) int { 99 | return a.x509Certificate.NotAfter.Compare(b.x509Certificate.NotAfter) 100 | }) 101 | return c 102 | } 103 | 104 | type Certificate struct { 105 | // position of certificate in the chain, starts with 1 106 | position int 107 | x509Certificate *x509.Certificate 108 | err error 109 | } 110 | 111 | func FromX509Certificates(cs []*x509.Certificate) Certificates { 112 | 113 | var certificates Certificates 114 | for i, c := range cs { 115 | certificates = append(certificates, Certificate{position: i, x509Certificate: c}) 116 | } 117 | return certificates 118 | } 119 | 120 | // FromBytes converts raw certificate bytes to certificate, if the supplied data is cert bundle (or chain) 121 | // all the certificates will be returned 122 | func FromBytes(data []byte) (Certificates, error) { 123 | 124 | var block *pem.Block 125 | var certificates Certificates 126 | var i int 127 | for { 128 | i++ 129 | block, data = pem.Decode(data) 130 | if block == nil { 131 | return nil, errors.New("cannot find any PEM block") 132 | } 133 | certificates = append(certificates, fromPemBlock(i, block)) 134 | if len(data) == 0 { 135 | break 136 | } 137 | } 138 | return certificates, nil 139 | } 140 | 141 | func fromPemBlock(position int, block *pem.Block) Certificate { 142 | 143 | if block.Type != certificateBlockType { 144 | return Certificate{position: position, err: fmt.Errorf("cannot parse %s block", block.Type)} 145 | } 146 | certificate, err := x509.ParseCertificate(block.Bytes) 147 | if err != nil { 148 | return Certificate{position: position, err: err} 149 | } 150 | return Certificate{position: position, x509Certificate: certificate} 151 | } 152 | 153 | func (c Certificate) IsExpired() bool { 154 | 155 | if c.err != nil { 156 | return false 157 | } 158 | return time.Now().After(c.x509Certificate.NotAfter) 159 | } 160 | 161 | func (c Certificate) ToPEM() []byte { 162 | 163 | if c.err != nil { 164 | return nil 165 | } 166 | 167 | return pem.EncodeToMemory(&pem.Block{ 168 | Type: certificateBlockType, 169 | Bytes: c.x509Certificate.Raw, 170 | }) 171 | } 172 | 173 | func (c Certificate) SubjectString() string { 174 | 175 | if c.err != nil { 176 | return fmt.Sprintf("ERROR: block at position %d: %v", c.position, c.err) 177 | } 178 | var subject pkix.RDNSequence 179 | if _, err := asn1.Unmarshal(c.x509Certificate.RawSubject, &subject); err != nil { 180 | return fmt.Sprintf("ERROR: asn1 unmarshal subject: %v", err) 181 | } 182 | return subject.String() 183 | } 184 | 185 | func (c Certificate) Error() error { 186 | if c.err != nil { 187 | return fmt.Errorf("ERROR: block at position %d: %v", c.position, c.err) 188 | } 189 | return nil 190 | } 191 | 192 | func (c Certificate) DNSNames() []string { 193 | if c.x509Certificate == nil { 194 | // this is called with -expiry flag as well, this call does not check if there is cert error 195 | // so we need to check for nil 196 | return nil 197 | } 198 | return c.x509Certificate.DNSNames 199 | } 200 | 201 | func (c Certificate) IPAddresses() []string { 202 | var ips []string 203 | for _, ip := range c.x509Certificate.IPAddresses { 204 | ips = append(ips, fmt.Sprintf("%s", ip)) 205 | } 206 | return ips 207 | } 208 | 209 | func (c Certificate) Version() int { 210 | return c.x509Certificate.Version 211 | } 212 | 213 | func (c Certificate) SerialNumber() string { 214 | return formatHexArray(c.x509Certificate.SerialNumber.Bytes()) 215 | } 216 | 217 | func (c Certificate) SignatureAlgorithm() string { 218 | return c.x509Certificate.SignatureAlgorithm.String() 219 | } 220 | 221 | func (c Certificate) Issuer() string { 222 | return c.x509Certificate.Issuer.String() 223 | } 224 | 225 | func (c Certificate) NotBefore() time.Time { 226 | return c.x509Certificate.NotBefore 227 | } 228 | 229 | func (c Certificate) NotAfter() time.Time { 230 | return c.x509Certificate.NotAfter 231 | } 232 | 233 | func (c Certificate) AuthorityKeyId() string { 234 | if c.x509Certificate.AuthorityKeyId != nil { 235 | return formatHexArray(c.x509Certificate.AuthorityKeyId) 236 | } 237 | return "" 238 | } 239 | 240 | func (c Certificate) SubjectKeyId() string { 241 | if c.x509Certificate.SubjectKeyId != nil { 242 | return formatHexArray(c.x509Certificate.SubjectKeyId) 243 | } 244 | return "" 245 | } 246 | 247 | func (c Certificate) IsCA() bool { 248 | return c.x509Certificate.IsCA 249 | } 250 | 251 | func (c Certificate) KeyUsage() []string { 252 | var out []string 253 | for i, v := range keyUsages { 254 | bitmask := 1 << i 255 | if (int(c.x509Certificate.KeyUsage) & bitmask) == 0 { 256 | continue 257 | } 258 | out = append(out, v) 259 | } 260 | return out 261 | } 262 | 263 | // ExtKeyUsage extended key usage string representation 264 | func (c Certificate) ExtKeyUsage() []string { 265 | 266 | var extendedKeyUsageString []string 267 | for _, v := range c.x509Certificate.ExtKeyUsage { 268 | extendedKeyUsageString = append(extendedKeyUsageString, extKeyUsages[v]) 269 | } 270 | return extendedKeyUsageString 271 | } 272 | 273 | func (c Certificate) Type() string { 274 | if c.x509Certificate.AuthorityKeyId == nil || bytes.Equal(c.x509Certificate.AuthorityKeyId, c.x509Certificate.SubjectKeyId) { 275 | return "root" 276 | } 277 | 278 | if c.x509Certificate.IsCA { 279 | return "intermediate" 280 | } 281 | return "end-entity" 282 | } 283 | func (c Certificate) Extensions() []Extension { 284 | var out []Extension 285 | for _, v := range c.x509Certificate.Extensions { 286 | name, value, err := parseExtension(v) 287 | if err != nil { 288 | // log error and set error as value 289 | slog.Error(fmt.Sprintf("certificate at position %d: extension %s (%s): %v", c.position, name, v.Id.String(), err)) 290 | value = []string{err.Error()} 291 | } 292 | out = append(out, Extension{ 293 | Name: name, 294 | Oid: v.Id.String(), 295 | Critical: v.Critical, 296 | Values: value, 297 | }) 298 | } 299 | return out 300 | } 301 | 302 | func formatHexArray(b []byte) string { 303 | if len(b) == 0 { 304 | return "" 305 | } 306 | buf := make([]byte, 0, 3*len(b)) 307 | x := buf[1*len(b) : 3*len(b)] 308 | hex.Encode(x, b) 309 | for i := 0; i < len(x); i += 2 { 310 | buf = append(buf, x[i], x[i+1], ':') 311 | } 312 | return strings.ToUpper(string(buf[:len(buf)-1])) 313 | } 314 | -------------------------------------------------------------------------------- /pkg/cert/cert_test.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "crypto/x509" 5 | "github.com/stretchr/testify/assert" 6 | "github.com/stretchr/testify/require" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestFromBytes(t *testing.T) { 12 | t.Run("given valid PEM certificate, then certificate is loaded", func(t *testing.T) { 13 | certificates := loadTestCertificates(t, "cert.pem") 14 | require.Equal(t, 1, len(certificates)) 15 | assert.Equal(t, 1, certificates[0].position) 16 | assert.Equal(t, "CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US", certificates[0].SubjectString()) 17 | assert.Nil(t, certificates[0].err) 18 | }) 19 | 20 | t.Run("given valid PEM bundle, then all certificates are loaded", func(t *testing.T) { 21 | certificates := loadTestCertificates(t, "bundle.pem") 22 | require.Equal(t, 2, len(certificates)) 23 | assert.Equal(t, "CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US", certificates[0].SubjectString()) 24 | assert.Equal(t, "CN=GTS Root R1,O=Google Trust Services LLC,C=US", certificates[1].SubjectString()) 25 | }) 26 | } 27 | 28 | func TestCertificates_RemoveDuplicates(t *testing.T) { 29 | t.Run("given duplicate PEM certificate, when remove duplicates is called, then they are removed", func(t *testing.T) { 30 | certificates := loadTestCertificates(t, "bundle.pem", "bundle.pem") 31 | 32 | require.Equal(t, 4, len(certificates)) 33 | noDuplicates := certificates.RemoveDuplicates() 34 | require.Equal(t, 2, len(noDuplicates)) 35 | }) 36 | } 37 | 38 | func TestCertificates_SortByExpiry(t *testing.T) { 39 | t.Run("given multiple certificates, when they have different expiry, then they are sorted", func(t *testing.T) { 40 | certificates := Certificates{ 41 | // using version to validate tests 42 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(0, 6, 3), Version: 1}}, 43 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(1, 6, 2), Version: 3}}, 44 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(1, 6, 21), Version: 4}}, 45 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(1, 3, 3), Version: 2}}, 46 | } 47 | 48 | sortedCertificates := certificates.SortByExpiry() 49 | require.Equal(t, 4, len(sortedCertificates)) 50 | assert.Equal(t, 1, sortedCertificates[0].x509Certificate.Version) 51 | assert.Equal(t, 2, sortedCertificates[1].x509Certificate.Version) 52 | assert.Equal(t, 3, sortedCertificates[2].x509Certificate.Version) 53 | assert.Equal(t, 4, sortedCertificates[3].x509Certificate.Version) 54 | }) 55 | } 56 | 57 | func Test_rootIdentification(t *testing.T) { 58 | t.Run("given certificate issuer is identical to subject but authority key id is set then identify as root", func(t *testing.T) { 59 | certificate := loadTestCertificates(t, "root_with_authority_key_id.pem") 60 | require.Len(t, certificate, 1) 61 | require.Equal(t, certificate[0].x509Certificate.RawSubject, certificate[0].x509Certificate.RawIssuer) 62 | require.NotEmpty(t, certificate[0].x509Certificate.AuthorityKeyId) 63 | require.Equal(t, "root", certificate[0].Type()) 64 | }) 65 | 66 | t.Run("given certificate authority key id is unset then identify as root", func(t *testing.T) { 67 | certificate := loadTestCertificates(t, "cert.pem") 68 | require.Len(t, certificate, 1) 69 | assert.Len(t, certificate[0].x509Certificate.AuthorityKeyId, 0) 70 | assert.True(t, certificate[0].x509Certificate.IsCA) 71 | require.Equal(t, "root", certificate[0].Type()) 72 | }) 73 | } 74 | 75 | func Test_intermediateIdentification(t *testing.T) { 76 | t.Run("given intermediate certificate issuer is identical to subject but authority and subject keys are different then identify as intermediate", func(t *testing.T) { 77 | certificate := loadTestCertificates(t, "intermediate_same_issuer_and_subject.pem") 78 | require.Len(t, certificate, 1) 79 | require.Equal(t, certificate[0].x509Certificate.RawSubject, certificate[0].x509Certificate.RawIssuer) 80 | require.NotEmpty(t, certificate[0].x509Certificate.AuthorityKeyId) 81 | require.Equal(t, "intermediate", certificate[0].Type()) 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /pkg/cert/extensions.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "crypto/x509/pkix" 5 | "encoding/asn1" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | type Extension struct { 11 | Name string 12 | Oid string 13 | Critical bool 14 | Values []string 15 | } 16 | 17 | func parseExtension(in pkix.Extension) (string, []string, error) { 18 | if fn, ok := extensionsByOid[in.Id.String()]; ok { 19 | return fn(in.Value) 20 | } 21 | return "-N/A-", []string{in.Id.String()}, nil 22 | } 23 | 24 | var extensionsByOid = map[string]func(in []byte) (string, []string, error){ 25 | "2.5.29.35": parseAuthorityKeyIdentifier, 26 | "2.5.29.14": parseSubjectKeyIdentifier, 27 | "2.5.29.15": parseKeyUsage, 28 | "2.5.29.32": parseCertificatePolicies, 29 | //"2.5.29.33": parsePolicyMappings, 30 | "2.5.29.17": parseSubjectAltName, 31 | //"2.5.29.18": parseIssuerAlternativeName, 32 | //"2.5.29.9": parseSubjectDirectoryAttributes, 33 | "2.5.29.19": parseBasicConstraints, 34 | //"2.5.29.30": parseNameConstraints, 35 | //"2.5.29.36": parsePolicyConstraints, 36 | "2.5.29.37": parseExtendedKeyUsage, 37 | "2.5.29.31": parseCRLDistributionPoints, 38 | //"2.5.29.54": parseInhibitAnyPolicy, 39 | //"2.5.29.46": parseFreshestCRL, 40 | // private internet extensions 41 | "1.3.6.1.5.5.7.1.1": parseAuthorityInformationAccess, 42 | //"1.3.6.1.5.5.7.11": parseSubjectInformationAccess, 43 | "1.3.6.1.4.1.11129.2.4.2": parseSignedCertificateTimestampList, 44 | } 45 | 46 | // AuthorityKeyIdentifier ::= SEQUENCE { 47 | // keyIdentifier [0] KeyIdentifier OPTIONAL, 48 | // authorityCertIssuer [1] GeneralNames OPTIONAL, 49 | // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } 50 | // -- authorityCertIssuer and authorityCertSerialNumber MUST both 51 | // -- be present or both be absent 52 | func parseAuthorityKeyIdentifier(in []byte) (string, []string, error) { 53 | name := "Authority Key Identifier" 54 | out, err := ToAuthorityKeyIdentifier(in) 55 | if err != nil { 56 | return name, nil, err 57 | } 58 | 59 | fields := []string{formatHexArray(out.KeyIdentifier)} 60 | if out.AuthorityCertIssuer != nil { 61 | v := strings.Join(out.AuthorityCertIssuer, ", ") 62 | fields = append(fields, fmt.Sprintf("Authority Cert. Issuer: %s", v)) 63 | } 64 | if out.AuthorityCertSerialNumber != 0 { 65 | fields = append(fields, fmt.Sprintf("Authority Cert SN: %d", out.AuthorityCertSerialNumber)) 66 | } 67 | return name, fields, nil 68 | } 69 | 70 | // SubjectKeyIdentifier ::= KeyIdentifier 71 | func parseSubjectKeyIdentifier(in []byte) (string, []string, error) { 72 | name := "Subject Key Identifier" 73 | out := asn1.RawValue{Tag: asn1.TagOctetString} 74 | if _, err := asn1.Unmarshal(in, &out); err != nil { 75 | return name, nil, err 76 | } 77 | return name, []string{formatHexArray(out.Bytes)}, nil 78 | } 79 | 80 | // KeyUsage ::= BIT STRING { 81 | // digitalSignature (0), 82 | // nonRepudiation (1), -- recent editions of X.509 have 83 | // -- renamed this bit to contentCommitment 84 | // keyEncipherment (2), 85 | // dataEncipherment (3), 86 | // keyAgreement (4), 87 | // keyCertSign (5), 88 | // cRLSign (6), 89 | // encipherOnly (7), 90 | // decipherOnly (8) } 91 | func parseKeyUsage(in []byte) (string, []string, error) { 92 | name := "Key Usage" 93 | out, err := ToKeyUsage(in) 94 | if err != nil { 95 | return name, nil, err 96 | } 97 | return name, out, nil 98 | } 99 | 100 | func parseCertificatePolicies(in []byte) (string, []string, error) { 101 | name := "Certificate Policies" 102 | out, err := ToCertificatePolicies(in) 103 | if err != nil { 104 | return name, nil, err 105 | } 106 | return name, out, nil 107 | } 108 | 109 | func parseSubjectAltName(in []byte) (string, []string, error) { 110 | name := "Subject Alt. Name" 111 | out, err := ToGeneralNames(in) 112 | if err != nil { 113 | return name, nil, err 114 | } 115 | return name, out, nil 116 | } 117 | 118 | func parseExtendedKeyUsage(in []byte) (string, []string, error) { 119 | name := "Extended Key Usage" 120 | out, err := ToExtendedKeyUsage(in) 121 | if err != nil { 122 | return name, nil, err 123 | } 124 | return name, out, nil 125 | } 126 | 127 | // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint 128 | // 129 | // DistributionPoint ::= SEQUENCE { 130 | // distributionPoint [0] DistributionPointName OPTIONAL, 131 | // reasons [1] ReasonFlags OPTIONAL, 132 | // cRLIssuer [2] GeneralNames OPTIONAL } 133 | // 134 | // DistributionPointName ::= CHOICE { 135 | // fullName [0] GeneralNames, 136 | // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } 137 | func parseCRLDistributionPoints(in []byte) (string, []string, error) { 138 | name := "CRL Distribution Points" 139 | out, err := ToCRLDistributionPoints(in) 140 | if err != nil { 141 | return name, nil, err 142 | } 143 | var points []string 144 | for _, v := range out { 145 | var point []string 146 | if len(v.DistributionPoint) != 0 { 147 | point = append(point, fmt.Sprintf("Distribution Point: %s", strings.Join(v.DistributionPoint, ", "))) 148 | } 149 | if len(v.Reasons) != 0 { 150 | point = append(point, fmt.Sprintf("Reasons: %s", strings.Join(v.Reasons, ", "))) 151 | } 152 | if len(v.CRLIssuer) != 0 { 153 | point = append(point, fmt.Sprintf("CRL Issuer: %s", strings.Join(v.CRLIssuer, ", "))) 154 | } 155 | if len(point) != 0 { 156 | points = append(points, strings.Join(point, " ")) 157 | } 158 | } 159 | return name, points, nil 160 | } 161 | 162 | // AuthorityInfoAccessSyntax ::= 163 | // SEQUENCE SIZE (1..MAX) OF AccessDescription 164 | // 165 | // AccessDescription ::= SEQUENCE { 166 | // accessMethod OBJECT IDENTIFIER, 167 | // accessLocation GeneralName } 168 | 169 | func parseAuthorityInformationAccess(in []byte) (string, []string, error) { 170 | name := "Authority Information Access" 171 | out, err := ToAuthorityInformationAccess(in) 172 | if err != nil { 173 | return name, nil, err 174 | } 175 | var fields []string 176 | for _, v := range out { 177 | fields = append(fields, fmt.Sprintf("%s - %s", v.AccessMethod, v.AccessLocation)) 178 | } 179 | return name, fields, nil 180 | } 181 | 182 | func parseSignedCertificateTimestampList(in []byte) (string, []string, error) { 183 | name := "CT Precertificate SCTs" 184 | return name, []string{"..."}, nil 185 | // TODO parse "Certificate Transparency", validate against openssl x509 output 186 | //out, err := ToSignedCertificateTimestampList(in) 187 | //if err != nil { 188 | // return name, nil, err 189 | //} 190 | //return name, []string{formatHexArray(out)}, nil 191 | } 192 | 193 | // BasicConstraints ::= SEQUENCE { 194 | // cA BOOLEAN DEFAULT FALSE, 195 | // pathLenConstraint INTEGER (0..MAX) OPTIONAL } 196 | func parseBasicConstraints(in []byte) (string, []string, error) { 197 | name := "Basic Constraints" 198 | out, err := ToBasicConstraints(in) 199 | if err != nil { 200 | return name, nil, err 201 | } 202 | 203 | fields := []string{fmt.Sprintf("CA: %t", out.CA)} 204 | if out.PathLenConstraint != 0 { 205 | fields = append(fields, fmt.Sprintf("PathLenConstraint: %d", out.PathLenConstraint)) 206 | } 207 | return name, fields, nil 208 | } 209 | -------------------------------------------------------------------------------- /pkg/cert/location.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "crypto/x509" 7 | "fmt" 8 | "io" 9 | "log/slog" 10 | "net" 11 | "os" 12 | "slices" 13 | "time" 14 | ) 15 | 16 | const tlsDialTimeout = 5 * time.Second 17 | 18 | type CertificateLocations []CertificateLocation 19 | 20 | func (c CertificateLocations) RemoveExpired() CertificateLocations { 21 | var out CertificateLocations 22 | for i := range c { 23 | out = append(out, c[i].RemoveExpired()) 24 | } 25 | return out 26 | } 27 | 28 | func (c CertificateLocations) RemoveDuplicates() CertificateLocations { 29 | var out CertificateLocations 30 | for i := range c { 31 | out = append(out, c[i].RemoveDuplicates()) 32 | } 33 | return out 34 | } 35 | 36 | func (c CertificateLocations) SubjectLike(subject string) CertificateLocations { 37 | var out CertificateLocations 38 | for i := range c { 39 | out = append(out, c[i].SubjectLike(subject)) 40 | } 41 | return out 42 | } 43 | 44 | func (c CertificateLocations) IssuerLike(issuer string) CertificateLocations { 45 | var out CertificateLocations 46 | for i := range c { 47 | out = append(out, c[i].IssuerLike(issuer)) 48 | } 49 | return out 50 | } 51 | 52 | func (c CertificateLocations) SortByExpiry() CertificateLocations { 53 | var out CertificateLocations 54 | // sort certificates in every location 55 | for i := range c { 56 | out = append(out, c[i].SortByExpiry()) 57 | } 58 | 59 | // sort locations by first certificate (they have been already sorted) 60 | slices.SortFunc(out, func(a, b CertificateLocation) int { 61 | if len(a.Certificates) == 0 && len(b.Certificates) == 0 { 62 | return 0 63 | } 64 | if len(a.Certificates) == 0 { 65 | return 1 66 | } 67 | if len(b.Certificates) == 0 { 68 | return -1 69 | } 70 | return a.Certificates[0].x509Certificate.NotAfter.Compare(b.Certificates[0].x509Certificate.NotAfter) 71 | }) 72 | return out 73 | } 74 | 75 | type CertificateLocation struct { 76 | TLSVersion uint16 // only applicable for network certificates 77 | Path string 78 | Error error 79 | Certificates Certificates 80 | } 81 | 82 | func (c CertificateLocation) Chains() ([]Certificates, error) { 83 | pool, err := x509.SystemCertPool() 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | // we are not verifying time and dns, because we want to work with -insecure flag as well 89 | // just to see what local chains are used for verification 90 | opts := x509.VerifyOptions{ 91 | Roots: pool, 92 | Intermediates: x509.NewCertPool(), 93 | } 94 | for _, cert := range c.Certificates { 95 | // do not just use index (index 0 leaf/end-entity, rest intermediate) like connection, 96 | // because we can deal with certs from a bundle file 97 | if cert.Type() == "intermediate" { 98 | opts.Intermediates.AddCert(cert.x509Certificate) 99 | } 100 | } 101 | 102 | var verifiedChains []Certificates 103 | for _, cert := range c.Certificates { 104 | if cert.Type() == "end-entity" { 105 | chains, err := cert.x509Certificate.Verify(opts) 106 | if err != nil { 107 | return nil, err 108 | } 109 | for _, chain := range chains { 110 | verifiedChains = append(verifiedChains, FromX509Certificates(chain)) 111 | } 112 | } 113 | } 114 | return verifiedChains, nil 115 | } 116 | 117 | func (c CertificateLocation) Name() string { 118 | return nameFormat(c.Path, c.TLSVersion) 119 | } 120 | 121 | func (c CertificateLocation) RemoveExpired() CertificateLocation { 122 | c.Certificates = c.Certificates.RemoveExpired() 123 | return c 124 | } 125 | 126 | func (c CertificateLocation) RemoveDuplicates() CertificateLocation { 127 | c.Certificates = c.Certificates.RemoveDuplicates() 128 | return c 129 | } 130 | 131 | func (c CertificateLocation) SubjectLike(subject string) CertificateLocation { 132 | c.Certificates = c.Certificates.SubjectLike(subject) 133 | return c 134 | } 135 | 136 | func (c CertificateLocation) IssuerLike(issuer string) CertificateLocation { 137 | c.Certificates = c.Certificates.IssuerLike(issuer) 138 | return c 139 | } 140 | 141 | func (c CertificateLocation) SortByExpiry() CertificateLocation { 142 | c.Certificates = c.Certificates.SortByExpiry() 143 | return c 144 | } 145 | 146 | func LoadCertificatesFromNetwork(addr string, serverName string, tlsSkipVerify bool) CertificateLocation { 147 | 148 | conn, err := tls.DialWithDialer(&net.Dialer{Timeout: tlsDialTimeout}, "tcp", addr, &tls.Config{ 149 | InsecureSkipVerify: tlsSkipVerify, 150 | ServerName: serverName, 151 | }) 152 | if err != nil { 153 | slog.Error(fmt.Sprintf("load certificate from network %s: %v", addr, err.Error())) 154 | return CertificateLocation{Path: addr, Error: err} 155 | } 156 | 157 | connectionState := conn.ConnectionState() 158 | x509Certificates := connectionState.PeerCertificates 159 | 160 | return CertificateLocation{ 161 | TLSVersion: conn.ConnectionState().Version, 162 | Path: addr, 163 | Certificates: FromX509Certificates(x509Certificates), 164 | } 165 | } 166 | 167 | func LoadCertificatesFromFile(fileName string) CertificateLocation { 168 | 169 | b, err := os.ReadFile(fileName) 170 | if err != nil { 171 | slog.Error(fmt.Sprintf("load certificate from file %s: %v", fileName, err.Error())) 172 | return CertificateLocation{Path: fileName, Error: err} 173 | } 174 | return loadCertificate(fileName, b) 175 | } 176 | 177 | func LoadCertificateFromStdin() CertificateLocation { 178 | 179 | content, err := io.ReadAll(os.Stdin) 180 | if err != nil { 181 | slog.Error(fmt.Sprintf("load certificate from stdin: %v", err.Error())) 182 | return CertificateLocation{Path: "stdin", Error: err} 183 | } 184 | return loadCertificate("stdin", content) 185 | } 186 | 187 | func loadCertificate(fileName string, data []byte) CertificateLocation { 188 | 189 | certificates, err := FromBytes(bytes.TrimSpace(data)) 190 | if err != nil { 191 | slog.Error(fmt.Sprintf("parse certificate %s bytes: %v", fileName, err.Error())) 192 | return CertificateLocation{Path: fileName, Error: err} 193 | } 194 | 195 | return CertificateLocation{ 196 | Path: fileName, 197 | Certificates: certificates, 198 | } 199 | } 200 | 201 | func nameFormat(name string, tlsVersion uint16) string { 202 | 203 | if tlsVersion == 0 { 204 | return name 205 | } 206 | return fmt.Sprintf("%s %s", name, tlsFormat(tlsVersion)) 207 | } 208 | 209 | func tlsFormat(tlsVersion uint16) string { 210 | 211 | switch tlsVersion { 212 | case 0: 213 | return "" 214 | case tls.VersionSSL30: 215 | return "SSLv3 - Deprecated!" 216 | case tls.VersionTLS10: 217 | return "TLS 1.0 - Deprecated!" 218 | case tls.VersionTLS11: 219 | return "TLS 1.1 - Deprecated!" 220 | case tls.VersionTLS12: 221 | return "TLS 1.2" 222 | case tls.VersionTLS13: 223 | return "TLS 1.3" 224 | default: 225 | return fmt.Sprintf("TLS Version %d (unknown)", tlsVersion) 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /pkg/cert/location_test.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "crypto/x509" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func Test_nameFormat(t *testing.T) { 15 | t.Run("given no tls version then name is returned", func(t *testing.T) { 16 | name := nameFormat("test name", 0) 17 | assert.Equal(t, "test name", name) 18 | }) 19 | 20 | t.Run("given unknown tls version then name and 'unknown' version is returned", func(t *testing.T) { 21 | name := nameFormat("test name", 67) 22 | assert.Equal(t, "test name TLS Version 67 (unknown)", name) 23 | }) 24 | 25 | t.Run("given TLS 1.2 tls version then name and 1.2 version is returned", func(t *testing.T) { 26 | name := nameFormat("test name", tls.VersionTLS12) 27 | assert.Equal(t, "test name TLS 1.2", name) 28 | }) 29 | } 30 | 31 | func Test_loadCertificate(t *testing.T) { 32 | t.Run("given valid certificate then cert location is loaded", func(t *testing.T) { 33 | certificate := loadTestFile(t, "cert.pem") 34 | cert := loadCertificate("test", certificate) 35 | require.Equal(t, 1, len(cert.Certificates)) 36 | assert.Equal(t, "CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US", cert.Certificates[0].SubjectString()) 37 | }) 38 | 39 | t.Run("given certificate with extra spaces then cert location is loaded", func(t *testing.T) { 40 | certificate := loadTestFile(t, "cert.pem") 41 | certificate = bytes.Join([][]byte{[]byte(" "), certificate}, []byte("")) 42 | cert := loadCertificate("test", certificate) 43 | require.Equal(t, 1, len(cert.Certificates)) 44 | assert.Equal(t, "CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US", cert.Certificates[0].SubjectString()) 45 | }) 46 | } 47 | 48 | func TestCertificateLocation_SortByExpiry(t *testing.T) { 49 | t.Run("given valid certificate in clipboard then cert is loaded", func(t *testing.T) { 50 | locations := CertificateLocations{ 51 | { 52 | Path: "three", 53 | Certificates: Certificates{ 54 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(3, 2, 3)}}, 55 | }, 56 | }, 57 | { 58 | Path: "one", 59 | Certificates: Certificates{ 60 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(1, 6, 2)}}, 61 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(1, 6, 21)}}, 62 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(0, 6, 3)}}, 63 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(1, 3, 3)}}, 64 | }, 65 | }, 66 | { 67 | Path: "four", 68 | }, 69 | { 70 | Path: "two", 71 | Certificates: Certificates{ 72 | {x509Certificate: &x509.Certificate{NotAfter: time.Now().AddDate(0, 7, 3)}}, 73 | }, 74 | }, 75 | } 76 | 77 | sortedLocations := locations.SortByExpiry() 78 | require.Equal(t, 4, len(sortedLocations)) 79 | assert.Equal(t, "one", sortedLocations[0].Path) 80 | assert.Equal(t, "two", sortedLocations[1].Path) 81 | assert.Equal(t, "three", sortedLocations[2].Path) 82 | assert.Equal(t, "four", sortedLocations[3].Path) 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /pkg/cert/testdata/bundle.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH 5 | MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT 6 | MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 7 | b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG 8 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI 9 | 2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx 10 | 1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ 11 | q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz 12 | tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ 13 | vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP 14 | BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV 15 | 5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY 16 | 1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 17 | NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG 18 | Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 19 | 8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe 20 | pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl 21 | MrY= 22 | -----END CERTIFICATE----- 23 | -----BEGIN CERTIFICATE----- 24 | MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH 25 | MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM 26 | QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy 27 | MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl 28 | cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB 29 | AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM 30 | f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX 31 | mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 32 | zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P 33 | fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc 34 | vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 35 | Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp 36 | zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO 37 | Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW 38 | k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ 39 | DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF 40 | lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV 41 | HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW 42 | Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 43 | d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z 44 | XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR 45 | gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 46 | d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv 47 | J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg 48 | DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM 49 | +SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy 50 | F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 51 | SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws 52 | E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl 53 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /pkg/cert/testdata/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH 5 | MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT 6 | MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 7 | b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG 8 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI 9 | 2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx 10 | 1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ 11 | q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz 12 | tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ 13 | vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP 14 | BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV 15 | 5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY 16 | 1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 17 | NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG 18 | Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 19 | 8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe 20 | pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl 21 | MrY= 22 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /pkg/cert/testdata/intermediate_same_issuer_and_subject.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDpzCCAo+gAwIBAgIUcIb+kg9o1L8z1boqCRj+wHtRlYYwDQYJKoZIhvcNAQEL 3 | BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwNTIyMjAwMzEzWhcNMjgw 4 | NTIwMjAwMzQzWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN 5 | AQEBBQADggEPADCCAQoCggEBAKLmE6eul1IaAfkN1KOxGeCHlmRghRJBmHUCKKZj 6 | B3wu9ACH2qV9fEiUf+8LunX7yTj/L1ez22Tj1Fnwz4UROH/cIr0/+sgy2ski/btZ 7 | oMkCbr/dv9oU5PNJKWFBJrNw8KaKiA3NCpri7YI7UdH0FDQu2xjZz11wQp2qlgcF 8 | LrtbiRzjANrjFu130BHOlU8d4laigQjFLv1t9NmzMaGyEXRSotHjK/0tUzdBbMBD 9 | ZAIyikzXQJh1uiqu36mc1pjYyByq3+JEL34FLMbxqt+sXpR+bVnFZ3TvmADwoGRY 10 | 1fk3wSmq774E8+wXRkIAqY2yRrAWKSOn8QUfToRpC21GOEECAwEAAaOB7DCB6TAO 11 | BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjjGsrYGp 12 | OVxRX17zeZP3olYePeQwHwYDVR0jBBgwFoAUczPHYaeWxmeyNxTEd4ELZX5M5+ow 13 | OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy 14 | MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw 15 | JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 16 | DQEBCwUAA4IBAQB8LSBYOJdoDHlESFjLKXSD5ZhydlKaNLdMV7F7EIG52MXMW9iS 17 | gBxkOWo5a8MfnD1QORWT36R4drJO1QVsfJf4bhinuhlaGFVOLBUzBi8VAmmBH4qe 18 | T2LorQxrWHf8WoycwZIzHc9KcT/xPMedo9NwdiYnPX8c6kpUWI0SMs8lTXnCZf9f 19 | NSyIV4ILPyGpLlKNjcTmCDa+8gQol7kgqDpyGSni/mf4MYaoU8CsTQdAak8IiiOT 20 | JsZ8ZfSqH5pXWDs1/15+owhITUWc5CsmMGjQ55H1YIKbkQrXW2NmDde5zCDicfFN 21 | 58SuYX+K9o7qZCwZoS5zc9eoE4tQuurQWU84 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /pkg/cert/testdata/root_with_authority_key_id.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j 5 | ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL 6 | MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 7 | LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug 8 | RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm 9 | +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW 10 | PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM 11 | xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB 12 | Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 13 | hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg 14 | EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF 15 | MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA 16 | FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec 17 | nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z 18 | eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF 19 | hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 20 | Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe 21 | vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep 22 | +OkuE6N36B9K 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /pkg/cert/util_test.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "bytes" 5 | "github.com/stretchr/testify/require" 6 | "os" 7 | "path/filepath" 8 | "testing" 9 | ) 10 | 11 | func loadTestCertificates(t *testing.T, files ...string) Certificates { 12 | var bundle [][]byte 13 | for _, f := range files { 14 | bundle = append(bundle, loadTestFile(t, f)) 15 | } 16 | certificates, err := FromBytes(bytes.Join(bundle, []byte("\n"))) 17 | require.NoError(t, err) 18 | return certificates 19 | } 20 | 21 | func loadTestFile(t *testing.T, file string) []byte { 22 | b, err := os.ReadFile(filepath.Join("testdata", file)) 23 | require.NoError(t, err) 24 | return b 25 | } 26 | -------------------------------------------------------------------------------- /pkg/print/expiry.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pete911/certinfo/pkg/cert" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | func Expiry(certificateLocations []cert.CertificateLocation) { 11 | 12 | for _, certificateLocation := range certificateLocations { 13 | if certificateLocation.Error != nil { 14 | fmt.Printf("--- [%s: %v] ---\n", certificateLocation.Name(), certificateLocation.Error) 15 | fmt.Println() 16 | continue 17 | } 18 | 19 | fmt.Printf("--- [%s] ---\n", certificateLocation.Name()) 20 | for _, certificate := range certificateLocation.Certificates { 21 | 22 | fmt.Printf("Subject: %s\n", certificate.SubjectString()) 23 | if len(certificate.DNSNames()) != 0 { 24 | fmt.Printf("DNS Names: %s\n", strings.Join(certificate.DNSNames(), ", ")) 25 | } 26 | fmt.Printf("Expiry: %s\n", expiryString(certificate)) 27 | fmt.Println() 28 | } 29 | } 30 | } 31 | 32 | func expiryString(certificate cert.Certificate) string { 33 | 34 | if certificate.Error() != nil { 35 | return "-" 36 | } 37 | expiry := formatExpiry(certificate.NotAfter()) 38 | if certificate.IsExpired() { 39 | return fmt.Sprintf("EXPIRED %s ago", expiry) 40 | } 41 | return expiry 42 | } 43 | 44 | func formatExpiry(t time.Time) string { 45 | 46 | year, month, day, hour, minute, _ := timeDiff(time.Now(), t) 47 | if year != 0 { 48 | return fmt.Sprintf("%d years %d months %d days %d hours %d minutes", year, month, day, hour, minute) 49 | } 50 | if month != 0 { 51 | return fmt.Sprintf("%d months %d days %d hours %d minutes", month, day, hour, minute) 52 | } 53 | if day != 0 { 54 | return fmt.Sprintf("%d days %d hours %d minutes", day, hour, minute) 55 | } 56 | if hour != 0 { 57 | return fmt.Sprintf("%d hours %d minutes", hour, minute) 58 | } 59 | return fmt.Sprintf("%d minutes", minute) 60 | } 61 | 62 | // copied from github.com/icza/gox/timex Diff function 63 | func timeDiff(a, b time.Time) (year, month, day, hour, min, sec int) { 64 | 65 | if a.Location() != b.Location() { 66 | b = b.In(a.Location()) 67 | } 68 | if a.After(b) { 69 | a, b = b, a 70 | } 71 | y1, M1, d1 := a.Date() 72 | y2, M2, d2 := b.Date() 73 | 74 | h1, m1, s1 := a.Clock() 75 | h2, m2, s2 := b.Clock() 76 | 77 | year = y2 - y1 78 | month = int(M2 - M1) 79 | day = d2 - d1 80 | hour = h2 - h1 81 | min = m2 - m1 82 | sec = s2 - s1 83 | 84 | // Normalize negative values 85 | if sec < 0 { 86 | sec += 60 87 | min-- 88 | } 89 | if min < 0 { 90 | min += 60 91 | hour-- 92 | } 93 | if hour < 0 { 94 | hour += 24 95 | day-- 96 | } 97 | if day < 0 { 98 | // days in month: 99 | t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC) 100 | day += 32 - t.Day() 101 | month-- 102 | } 103 | if month < 0 { 104 | month += 12 105 | year-- 106 | } 107 | 108 | return 109 | } 110 | -------------------------------------------------------------------------------- /pkg/print/expiry_test.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "strings" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func Test_expiryFormat(t *testing.T) { 11 | t.Run("given certificate expiry is more than a year then year is returned as well", func(t *testing.T) { 12 | v := formatExpiry(getTime(3, 2, 7, 5, 25)) 13 | assert.True(t, strings.HasPrefix(v, "3 years 2 months ")) 14 | }) 15 | 16 | t.Run("given certificate expiry is less than a year then year is not returned", func(t *testing.T) { 17 | v := formatExpiry(getTime(0, 2, 7, 5, 25)) 18 | assert.True(t, strings.HasPrefix(v, "2 months ")) 19 | }) 20 | 21 | t.Run("given certificate expiry is less than a month then year and month is not returned", func(t *testing.T) { 22 | v := formatExpiry(getTime(0, 0, 7, 5, 25)) 23 | assert.Equal(t, "7 days 5 hours 25 minutes", v) 24 | }) 25 | 26 | t.Run("given certificate expiry is less than a day then year, month and day is not returned", func(t *testing.T) { 27 | v := formatExpiry(getTime(0, 0, 0, 5, 25)) 28 | assert.Equal(t, "5 hours 25 minutes", v) 29 | }) 30 | 31 | t.Run("given certificate expiry is less than an hour then only minutes are returned", func(t *testing.T) { 32 | v := formatExpiry(getTime(0, 0, 0, 0, 25)) 33 | assert.Equal(t, "25 minutes", v) 34 | }) 35 | } 36 | 37 | func getTime(years, months, days, hours, minutes int) time.Time { 38 | return time.Now().AddDate(years, months, days). 39 | Add(time.Hour*time.Duration(hours) + time.Minute*time.Duration(minutes)) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/print/location.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pete911/certinfo/pkg/cert" 6 | "log/slog" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | func Locations(certificateLocations []cert.CertificateLocation, printChains, printPem, printExtensions bool) { 12 | 13 | for _, certificateLocation := range certificateLocations { 14 | if certificateLocation.Error != nil { 15 | slog.Error(fmt.Sprintf("%s: %v", certificateLocation.Name(), certificateLocation.Error)) 16 | fmt.Printf("--- [%s: %v] ---\n", certificateLocation.Name(), certificateLocation.Error) 17 | fmt.Println() 18 | continue 19 | } 20 | 21 | fmt.Printf("--- [%s] ---\n", certificateLocation.Name()) 22 | printCertificates(certificateLocation.Certificates, printPem, printExtensions) 23 | 24 | if printChains { 25 | chains, err := certificateLocation.Chains() 26 | if err != nil { 27 | slog.Error(fmt.Sprintf("chains for %s: %v", certificateLocation.Name(), certificateLocation.Error)) 28 | fmt.Printf("--- [chains for %s: %v] ---\n", certificateLocation.Name(), err) 29 | continue 30 | } 31 | 32 | if len(chains) == 1 { 33 | fmt.Printf("--- [%d chain for %s] ---\n", len(chains), certificateLocation.Name()) 34 | } else { 35 | fmt.Printf("--- [%d chains for %s] ---\n", len(chains), certificateLocation.Name()) 36 | } 37 | for i, chain := range chains { 38 | fmt.Printf(" -- [chain %d] -- \n", i+1) 39 | printCertificates(chain, printPem, printExtensions) 40 | } 41 | } 42 | } 43 | } 44 | 45 | func printCertificates(certs cert.Certificates, printPem, printExtensions bool) { 46 | 47 | for _, certificate := range certs { 48 | printCertificate(certificate, printExtensions) 49 | fmt.Println() 50 | if printPem { 51 | fmt.Println(string(certificate.ToPEM())) 52 | } 53 | } 54 | } 55 | 56 | func printCertificate(certificate cert.Certificate, printExtensions bool) { 57 | 58 | if certificate.Error() != nil { 59 | slog.Error(certificate.Error().Error()) 60 | fmt.Println(certificate.Error()) 61 | return 62 | } 63 | 64 | fmt.Printf("Version: %d\n", certificate.Version()) 65 | fmt.Printf("Serial Number: %s\n", certificate.SerialNumber()) 66 | fmt.Printf("Signature Algorithm: %s\n", certificate.SignatureAlgorithm()) 67 | fmt.Printf("Type: %s\n", certificate.Type()) 68 | fmt.Printf("Issuer: %s\n", certificate.Issuer()) 69 | fmt.Println("Validity") 70 | fmt.Printf(" Not Before: %s\n", validityFormat(certificate.NotBefore())) 71 | fmt.Printf(" Not After : %s\n", validityFormat(certificate.NotAfter())) 72 | fmt.Printf("Subject: %s\n", certificate.SubjectString()) 73 | fmt.Printf("DNS Names: %s\n", strings.Join(certificate.DNSNames(), ", ")) 74 | fmt.Printf("IP Addresses: %s\n", strings.Join(certificate.IPAddresses(), ", ")) 75 | fmt.Printf("Authority Key Id: %s\n", certificate.AuthorityKeyId()) 76 | fmt.Printf("Subject Key Id : %s\n", certificate.SubjectKeyId()) 77 | fmt.Printf("Key Usage: %s\n", strings.Join(certificate.KeyUsage(), ", ")) 78 | fmt.Printf("Ext Key Usage: %s\n", strings.Join(certificate.ExtKeyUsage(), ", ")) 79 | fmt.Printf("CA: %t\n", certificate.IsCA()) 80 | 81 | if !printExtensions { 82 | return 83 | } 84 | 85 | fmt.Println("Extensions:") 86 | for _, extension := range certificate.Extensions() { 87 | name := fmt.Sprintf("%s (%s)", extension.Name, extension.Oid) 88 | if extension.Critical { 89 | name = fmt.Sprintf("%s [critical]", name) 90 | } 91 | fmt.Printf(" %s\n", name) 92 | for _, line := range extension.Values { 93 | fmt.Printf(" %s\n", line) 94 | } 95 | } 96 | } 97 | 98 | func validityFormat(t time.Time) string { 99 | // format for NotBefore and NotAfter fields to make output similar to openssl 100 | return t.Format("Jan _2 15:04:05 2006 MST") 101 | } 102 | -------------------------------------------------------------------------------- /pkg/print/pem.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pete911/certinfo/pkg/cert" 6 | "log/slog" 7 | ) 8 | 9 | func Pem(certificateLocations []cert.CertificateLocation, printChains bool) { 10 | 11 | for _, certificateLocation := range certificateLocations { 12 | for _, certificate := range certificateLocation.Certificates { 13 | fmt.Print(string(certificate.ToPEM())) 14 | } 15 | 16 | if printChains { 17 | chains, err := certificateLocation.Chains() 18 | if err != nil { 19 | slog.Error(fmt.Sprintf("chains: %v", err)) 20 | continue 21 | } 22 | for _, chain := range chains { 23 | for _, c := range chain { 24 | fmt.Print(string(c.ToPEM())) 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // Go versions prior to 1.4 are disabled because they use a different layout 20 | // for interfaces which make the implementation of unsafeReflectValue more complex. 21 | // +build !js,!appengine,!safe,!disableunsafe,go1.4 22 | 23 | package spew 24 | 25 | import ( 26 | "reflect" 27 | "unsafe" 28 | ) 29 | 30 | const ( 31 | // UnsafeDisabled is a build-time constant which specifies whether or 32 | // not access to the unsafe package is available. 33 | UnsafeDisabled = false 34 | 35 | // ptrSize is the size of a pointer on the current arch. 36 | ptrSize = unsafe.Sizeof((*byte)(nil)) 37 | ) 38 | 39 | type flag uintptr 40 | 41 | var ( 42 | // flagRO indicates whether the value field of a reflect.Value 43 | // is read-only. 44 | flagRO flag 45 | 46 | // flagAddr indicates whether the address of the reflect.Value's 47 | // value may be taken. 48 | flagAddr flag 49 | ) 50 | 51 | // flagKindMask holds the bits that make up the kind 52 | // part of the flags field. In all the supported versions, 53 | // it is in the lower 5 bits. 54 | const flagKindMask = flag(0x1f) 55 | 56 | // Different versions of Go have used different 57 | // bit layouts for the flags type. This table 58 | // records the known combinations. 59 | var okFlags = []struct { 60 | ro, addr flag 61 | }{{ 62 | // From Go 1.4 to 1.5 63 | ro: 1 << 5, 64 | addr: 1 << 7, 65 | }, { 66 | // Up to Go tip. 67 | ro: 1<<5 | 1<<6, 68 | addr: 1 << 8, 69 | }} 70 | 71 | var flagValOffset = func() uintptr { 72 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 73 | if !ok { 74 | panic("reflect.Value has no flag field") 75 | } 76 | return field.Offset 77 | }() 78 | 79 | // flagField returns a pointer to the flag field of a reflect.Value. 80 | func flagField(v *reflect.Value) *flag { 81 | return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 82 | } 83 | 84 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 85 | // the typical safety restrictions preventing access to unaddressable and 86 | // unexported data. It works by digging the raw pointer to the underlying 87 | // value out of the protected value and generating a new unprotected (unsafe) 88 | // reflect.Value to it. 89 | // 90 | // This allows us to check for implementations of the Stringer and error 91 | // interfaces to be used for pretty printing ordinarily unaddressable and 92 | // inaccessible values such as unexported struct fields. 93 | func unsafeReflectValue(v reflect.Value) reflect.Value { 94 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 95 | return v 96 | } 97 | flagFieldPtr := flagField(&v) 98 | *flagFieldPtr &^= flagRO 99 | *flagFieldPtr |= flagAddr 100 | return v 101 | } 102 | 103 | // Sanity checks against future reflect package changes 104 | // to the type or semantics of the Value.flag field. 105 | func init() { 106 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 107 | if !ok { 108 | panic("reflect.Value has no flag field") 109 | } 110 | if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { 111 | panic("reflect.Value flag field has changed kind") 112 | } 113 | type t0 int 114 | var t struct { 115 | A t0 116 | // t0 will have flagEmbedRO set. 117 | t0 118 | // a will have flagStickyRO set 119 | a t0 120 | } 121 | vA := reflect.ValueOf(t).FieldByName("A") 122 | va := reflect.ValueOf(t).FieldByName("a") 123 | vt0 := reflect.ValueOf(t).FieldByName("t0") 124 | 125 | // Infer flagRO from the difference between the flags 126 | // for the (otherwise identical) fields in t. 127 | flagPublic := *flagField(&vA) 128 | flagWithRO := *flagField(&va) | *flagField(&vt0) 129 | flagRO = flagPublic ^ flagWithRO 130 | 131 | // Infer flagAddr from the difference between a value 132 | // taken from a pointer and not. 133 | vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") 134 | flagNoPtr := *flagField(&vA) 135 | flagPtr := *flagField(&vPtrA) 136 | flagAddr = flagNoPtr ^ flagPtr 137 | 138 | // Check that the inferred flags tally with one of the known versions. 139 | for _, f := range okFlags { 140 | if flagRO == f.ro && flagAddr == f.addr { 141 | return 142 | } 143 | } 144 | panic("reflect.Value read-only flag has changed semantics") 145 | } 146 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe !go1.4 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "io" 23 | "reflect" 24 | "sort" 25 | "strconv" 26 | ) 27 | 28 | // Some constants in the form of bytes to avoid string overhead. This mirrors 29 | // the technique used in the fmt package. 30 | var ( 31 | panicBytes = []byte("(PANIC=") 32 | plusBytes = []byte("+") 33 | iBytes = []byte("i") 34 | trueBytes = []byte("true") 35 | falseBytes = []byte("false") 36 | interfaceBytes = []byte("(interface {})") 37 | commaNewlineBytes = []byte(",\n") 38 | newlineBytes = []byte("\n") 39 | openBraceBytes = []byte("{") 40 | openBraceNewlineBytes = []byte("{\n") 41 | closeBraceBytes = []byte("}") 42 | asteriskBytes = []byte("*") 43 | colonBytes = []byte(":") 44 | colonSpaceBytes = []byte(": ") 45 | openParenBytes = []byte("(") 46 | closeParenBytes = []byte(")") 47 | spaceBytes = []byte(" ") 48 | pointerChainBytes = []byte("->") 49 | nilAngleBytes = []byte("") 50 | maxNewlineBytes = []byte("\n") 51 | maxShortBytes = []byte("") 52 | circularBytes = []byte("") 53 | circularShortBytes = []byte("") 54 | invalidAngleBytes = []byte("") 55 | openBracketBytes = []byte("[") 56 | closeBracketBytes = []byte("]") 57 | percentBytes = []byte("%") 58 | precisionBytes = []byte(".") 59 | openAngleBytes = []byte("<") 60 | closeAngleBytes = []byte(">") 61 | openMapBytes = []byte("map[") 62 | closeMapBytes = []byte("]") 63 | lenEqualsBytes = []byte("len=") 64 | capEqualsBytes = []byte("cap=") 65 | ) 66 | 67 | // hexDigits is used to map a decimal value to a hex digit. 68 | var hexDigits = "0123456789abcdef" 69 | 70 | // catchPanic handles any panics that might occur during the handleMethods 71 | // calls. 72 | func catchPanic(w io.Writer, v reflect.Value) { 73 | if err := recover(); err != nil { 74 | w.Write(panicBytes) 75 | fmt.Fprintf(w, "%v", err) 76 | w.Write(closeParenBytes) 77 | } 78 | } 79 | 80 | // handleMethods attempts to call the Error and String methods on the underlying 81 | // type the passed reflect.Value represents and outputes the result to Writer w. 82 | // 83 | // It handles panics in any called methods by catching and displaying the error 84 | // as the formatted value. 85 | func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { 86 | // We need an interface to check if the type implements the error or 87 | // Stringer interface. However, the reflect package won't give us an 88 | // interface on certain things like unexported struct fields in order 89 | // to enforce visibility rules. We use unsafe, when it's available, 90 | // to bypass these restrictions since this package does not mutate the 91 | // values. 92 | if !v.CanInterface() { 93 | if UnsafeDisabled { 94 | return false 95 | } 96 | 97 | v = unsafeReflectValue(v) 98 | } 99 | 100 | // Choose whether or not to do error and Stringer interface lookups against 101 | // the base type or a pointer to the base type depending on settings. 102 | // Technically calling one of these methods with a pointer receiver can 103 | // mutate the value, however, types which choose to satisify an error or 104 | // Stringer interface with a pointer receiver should not be mutating their 105 | // state inside these interface methods. 106 | if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { 107 | v = unsafeReflectValue(v) 108 | } 109 | if v.CanAddr() { 110 | v = v.Addr() 111 | } 112 | 113 | // Is it an error or Stringer? 114 | switch iface := v.Interface().(type) { 115 | case error: 116 | defer catchPanic(w, v) 117 | if cs.ContinueOnMethod { 118 | w.Write(openParenBytes) 119 | w.Write([]byte(iface.Error())) 120 | w.Write(closeParenBytes) 121 | w.Write(spaceBytes) 122 | return false 123 | } 124 | 125 | w.Write([]byte(iface.Error())) 126 | return true 127 | 128 | case fmt.Stringer: 129 | defer catchPanic(w, v) 130 | if cs.ContinueOnMethod { 131 | w.Write(openParenBytes) 132 | w.Write([]byte(iface.String())) 133 | w.Write(closeParenBytes) 134 | w.Write(spaceBytes) 135 | return false 136 | } 137 | w.Write([]byte(iface.String())) 138 | return true 139 | } 140 | return false 141 | } 142 | 143 | // printBool outputs a boolean value as true or false to Writer w. 144 | func printBool(w io.Writer, val bool) { 145 | if val { 146 | w.Write(trueBytes) 147 | } else { 148 | w.Write(falseBytes) 149 | } 150 | } 151 | 152 | // printInt outputs a signed integer value to Writer w. 153 | func printInt(w io.Writer, val int64, base int) { 154 | w.Write([]byte(strconv.FormatInt(val, base))) 155 | } 156 | 157 | // printUint outputs an unsigned integer value to Writer w. 158 | func printUint(w io.Writer, val uint64, base int) { 159 | w.Write([]byte(strconv.FormatUint(val, base))) 160 | } 161 | 162 | // printFloat outputs a floating point value using the specified precision, 163 | // which is expected to be 32 or 64bit, to Writer w. 164 | func printFloat(w io.Writer, val float64, precision int) { 165 | w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) 166 | } 167 | 168 | // printComplex outputs a complex value using the specified float precision 169 | // for the real and imaginary parts to Writer w. 170 | func printComplex(w io.Writer, c complex128, floatPrecision int) { 171 | r := real(c) 172 | w.Write(openParenBytes) 173 | w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) 174 | i := imag(c) 175 | if i >= 0 { 176 | w.Write(plusBytes) 177 | } 178 | w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) 179 | w.Write(iBytes) 180 | w.Write(closeParenBytes) 181 | } 182 | 183 | // printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' 184 | // prefix to Writer w. 185 | func printHexPtr(w io.Writer, p uintptr) { 186 | // Null pointer. 187 | num := uint64(p) 188 | if num == 0 { 189 | w.Write(nilAngleBytes) 190 | return 191 | } 192 | 193 | // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix 194 | buf := make([]byte, 18) 195 | 196 | // It's simpler to construct the hex string right to left. 197 | base := uint64(16) 198 | i := len(buf) - 1 199 | for num >= base { 200 | buf[i] = hexDigits[num%base] 201 | num /= base 202 | i-- 203 | } 204 | buf[i] = hexDigits[num] 205 | 206 | // Add '0x' prefix. 207 | i-- 208 | buf[i] = 'x' 209 | i-- 210 | buf[i] = '0' 211 | 212 | // Strip unused leading bytes. 213 | buf = buf[i:] 214 | w.Write(buf) 215 | } 216 | 217 | // valuesSorter implements sort.Interface to allow a slice of reflect.Value 218 | // elements to be sorted. 219 | type valuesSorter struct { 220 | values []reflect.Value 221 | strings []string // either nil or same len and values 222 | cs *ConfigState 223 | } 224 | 225 | // newValuesSorter initializes a valuesSorter instance, which holds a set of 226 | // surrogate keys on which the data should be sorted. It uses flags in 227 | // ConfigState to decide if and how to populate those surrogate keys. 228 | func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { 229 | vs := &valuesSorter{values: values, cs: cs} 230 | if canSortSimply(vs.values[0].Kind()) { 231 | return vs 232 | } 233 | if !cs.DisableMethods { 234 | vs.strings = make([]string, len(values)) 235 | for i := range vs.values { 236 | b := bytes.Buffer{} 237 | if !handleMethods(cs, &b, vs.values[i]) { 238 | vs.strings = nil 239 | break 240 | } 241 | vs.strings[i] = b.String() 242 | } 243 | } 244 | if vs.strings == nil && cs.SpewKeys { 245 | vs.strings = make([]string, len(values)) 246 | for i := range vs.values { 247 | vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) 248 | } 249 | } 250 | return vs 251 | } 252 | 253 | // canSortSimply tests whether a reflect.Kind is a primitive that can be sorted 254 | // directly, or whether it should be considered for sorting by surrogate keys 255 | // (if the ConfigState allows it). 256 | func canSortSimply(kind reflect.Kind) bool { 257 | // This switch parallels valueSortLess, except for the default case. 258 | switch kind { 259 | case reflect.Bool: 260 | return true 261 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 262 | return true 263 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 264 | return true 265 | case reflect.Float32, reflect.Float64: 266 | return true 267 | case reflect.String: 268 | return true 269 | case reflect.Uintptr: 270 | return true 271 | case reflect.Array: 272 | return true 273 | } 274 | return false 275 | } 276 | 277 | // Len returns the number of values in the slice. It is part of the 278 | // sort.Interface implementation. 279 | func (s *valuesSorter) Len() int { 280 | return len(s.values) 281 | } 282 | 283 | // Swap swaps the values at the passed indices. It is part of the 284 | // sort.Interface implementation. 285 | func (s *valuesSorter) Swap(i, j int) { 286 | s.values[i], s.values[j] = s.values[j], s.values[i] 287 | if s.strings != nil { 288 | s.strings[i], s.strings[j] = s.strings[j], s.strings[i] 289 | } 290 | } 291 | 292 | // valueSortLess returns whether the first value should sort before the second 293 | // value. It is used by valueSorter.Less as part of the sort.Interface 294 | // implementation. 295 | func valueSortLess(a, b reflect.Value) bool { 296 | switch a.Kind() { 297 | case reflect.Bool: 298 | return !a.Bool() && b.Bool() 299 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 300 | return a.Int() < b.Int() 301 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 302 | return a.Uint() < b.Uint() 303 | case reflect.Float32, reflect.Float64: 304 | return a.Float() < b.Float() 305 | case reflect.String: 306 | return a.String() < b.String() 307 | case reflect.Uintptr: 308 | return a.Uint() < b.Uint() 309 | case reflect.Array: 310 | // Compare the contents of both arrays. 311 | l := a.Len() 312 | for i := 0; i < l; i++ { 313 | av := a.Index(i) 314 | bv := b.Index(i) 315 | if av.Interface() == bv.Interface() { 316 | continue 317 | } 318 | return valueSortLess(av, bv) 319 | } 320 | } 321 | return a.String() < b.String() 322 | } 323 | 324 | // Less returns whether the value at index i should sort before the 325 | // value at index j. It is part of the sort.Interface implementation. 326 | func (s *valuesSorter) Less(i, j int) bool { 327 | if s.strings == nil { 328 | return valueSortLess(s.values[i], s.values[j]) 329 | } 330 | return s.strings[i] < s.strings[j] 331 | } 332 | 333 | // sortValues is a sort function that handles both native types and any type that 334 | // can be converted to error or Stringer. Other inputs are sorted according to 335 | // their Value.String() value to ensure display stability. 336 | func sortValues(values []reflect.Value, cs *ConfigState) { 337 | if len(values) == 0 { 338 | return 339 | } 340 | sort.Sort(newValuesSorter(values, cs)) 341 | } 342 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "io" 23 | "os" 24 | ) 25 | 26 | // ConfigState houses the configuration options used by spew to format and 27 | // display values. There is a global instance, Config, that is used to control 28 | // all top-level Formatter and Dump functionality. Each ConfigState instance 29 | // provides methods equivalent to the top-level functions. 30 | // 31 | // The zero value for ConfigState provides no indentation. You would typically 32 | // want to set it to a space or a tab. 33 | // 34 | // Alternatively, you can use NewDefaultConfig to get a ConfigState instance 35 | // with default settings. See the documentation of NewDefaultConfig for default 36 | // values. 37 | type ConfigState struct { 38 | // Indent specifies the string to use for each indentation level. The 39 | // global config instance that all top-level functions use set this to a 40 | // single space by default. If you would like more indentation, you might 41 | // set this to a tab with "\t" or perhaps two spaces with " ". 42 | Indent string 43 | 44 | // MaxDepth controls the maximum number of levels to descend into nested 45 | // data structures. The default, 0, means there is no limit. 46 | // 47 | // NOTE: Circular data structures are properly detected, so it is not 48 | // necessary to set this value unless you specifically want to limit deeply 49 | // nested data structures. 50 | MaxDepth int 51 | 52 | // DisableMethods specifies whether or not error and Stringer interfaces are 53 | // invoked for types that implement them. 54 | DisableMethods bool 55 | 56 | // DisablePointerMethods specifies whether or not to check for and invoke 57 | // error and Stringer interfaces on types which only accept a pointer 58 | // receiver when the current type is not a pointer. 59 | // 60 | // NOTE: This might be an unsafe action since calling one of these methods 61 | // with a pointer receiver could technically mutate the value, however, 62 | // in practice, types which choose to satisify an error or Stringer 63 | // interface with a pointer receiver should not be mutating their state 64 | // inside these interface methods. As a result, this option relies on 65 | // access to the unsafe package, so it will not have any effect when 66 | // running in environments without access to the unsafe package such as 67 | // Google App Engine or with the "safe" build tag specified. 68 | DisablePointerMethods bool 69 | 70 | // DisablePointerAddresses specifies whether to disable the printing of 71 | // pointer addresses. This is useful when diffing data structures in tests. 72 | DisablePointerAddresses bool 73 | 74 | // DisableCapacities specifies whether to disable the printing of capacities 75 | // for arrays, slices, maps and channels. This is useful when diffing 76 | // data structures in tests. 77 | DisableCapacities bool 78 | 79 | // ContinueOnMethod specifies whether or not recursion should continue once 80 | // a custom error or Stringer interface is invoked. The default, false, 81 | // means it will print the results of invoking the custom error or Stringer 82 | // interface and return immediately instead of continuing to recurse into 83 | // the internals of the data type. 84 | // 85 | // NOTE: This flag does not have any effect if method invocation is disabled 86 | // via the DisableMethods or DisablePointerMethods options. 87 | ContinueOnMethod bool 88 | 89 | // SortKeys specifies map keys should be sorted before being printed. Use 90 | // this to have a more deterministic, diffable output. Note that only 91 | // native types (bool, int, uint, floats, uintptr and string) and types 92 | // that support the error or Stringer interfaces (if methods are 93 | // enabled) are supported, with other types sorted according to the 94 | // reflect.Value.String() output which guarantees display stability. 95 | SortKeys bool 96 | 97 | // SpewKeys specifies that, as a last resort attempt, map keys should 98 | // be spewed to strings and sorted by those strings. This is only 99 | // considered if SortKeys is true. 100 | SpewKeys bool 101 | } 102 | 103 | // Config is the active configuration of the top-level functions. 104 | // The configuration can be changed by modifying the contents of spew.Config. 105 | var Config = ConfigState{Indent: " "} 106 | 107 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 108 | // passed with a Formatter interface returned by c.NewFormatter. It returns 109 | // the formatted string as a value that satisfies error. See NewFormatter 110 | // for formatting details. 111 | // 112 | // This function is shorthand for the following syntax: 113 | // 114 | // fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) 115 | func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { 116 | return fmt.Errorf(format, c.convertArgs(a)...) 117 | } 118 | 119 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 120 | // passed with a Formatter interface returned by c.NewFormatter. It returns 121 | // the number of bytes written and any write error encountered. See 122 | // NewFormatter for formatting details. 123 | // 124 | // This function is shorthand for the following syntax: 125 | // 126 | // fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) 127 | func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { 128 | return fmt.Fprint(w, c.convertArgs(a)...) 129 | } 130 | 131 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 132 | // passed with a Formatter interface returned by c.NewFormatter. It returns 133 | // the number of bytes written and any write error encountered. See 134 | // NewFormatter for formatting details. 135 | // 136 | // This function is shorthand for the following syntax: 137 | // 138 | // fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) 139 | func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 140 | return fmt.Fprintf(w, format, c.convertArgs(a)...) 141 | } 142 | 143 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 144 | // passed with a Formatter interface returned by c.NewFormatter. See 145 | // NewFormatter for formatting details. 146 | // 147 | // This function is shorthand for the following syntax: 148 | // 149 | // fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) 150 | func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 151 | return fmt.Fprintln(w, c.convertArgs(a)...) 152 | } 153 | 154 | // Print is a wrapper for fmt.Print that treats each argument as if it were 155 | // passed with a Formatter interface returned by c.NewFormatter. It returns 156 | // the number of bytes written and any write error encountered. See 157 | // NewFormatter for formatting details. 158 | // 159 | // This function is shorthand for the following syntax: 160 | // 161 | // fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) 162 | func (c *ConfigState) Print(a ...interface{}) (n int, err error) { 163 | return fmt.Print(c.convertArgs(a)...) 164 | } 165 | 166 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 167 | // passed with a Formatter interface returned by c.NewFormatter. It returns 168 | // the number of bytes written and any write error encountered. See 169 | // NewFormatter for formatting details. 170 | // 171 | // This function is shorthand for the following syntax: 172 | // 173 | // fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) 174 | func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { 175 | return fmt.Printf(format, c.convertArgs(a)...) 176 | } 177 | 178 | // Println is a wrapper for fmt.Println that treats each argument as if it were 179 | // passed with a Formatter interface returned by c.NewFormatter. It returns 180 | // the number of bytes written and any write error encountered. See 181 | // NewFormatter for formatting details. 182 | // 183 | // This function is shorthand for the following syntax: 184 | // 185 | // fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) 186 | func (c *ConfigState) Println(a ...interface{}) (n int, err error) { 187 | return fmt.Println(c.convertArgs(a)...) 188 | } 189 | 190 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 191 | // passed with a Formatter interface returned by c.NewFormatter. It returns 192 | // the resulting string. See NewFormatter for formatting details. 193 | // 194 | // This function is shorthand for the following syntax: 195 | // 196 | // fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) 197 | func (c *ConfigState) Sprint(a ...interface{}) string { 198 | return fmt.Sprint(c.convertArgs(a)...) 199 | } 200 | 201 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 202 | // passed with a Formatter interface returned by c.NewFormatter. It returns 203 | // the resulting string. See NewFormatter for formatting details. 204 | // 205 | // This function is shorthand for the following syntax: 206 | // 207 | // fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) 208 | func (c *ConfigState) Sprintf(format string, a ...interface{}) string { 209 | return fmt.Sprintf(format, c.convertArgs(a)...) 210 | } 211 | 212 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 213 | // were passed with a Formatter interface returned by c.NewFormatter. It 214 | // returns the resulting string. See NewFormatter for formatting details. 215 | // 216 | // This function is shorthand for the following syntax: 217 | // 218 | // fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) 219 | func (c *ConfigState) Sprintln(a ...interface{}) string { 220 | return fmt.Sprintln(c.convertArgs(a)...) 221 | } 222 | 223 | /* 224 | NewFormatter returns a custom formatter that satisfies the fmt.Formatter 225 | interface. As a result, it integrates cleanly with standard fmt package 226 | printing functions. The formatter is useful for inline printing of smaller data 227 | types similar to the standard %v format specifier. 228 | 229 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 230 | addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb 231 | combinations. Any other verbs such as %x and %q will be sent to the the 232 | standard fmt package for formatting. In addition, the custom formatter ignores 233 | the width and precision arguments (however they will still work on the format 234 | specifiers not handled by the custom formatter). 235 | 236 | Typically this function shouldn't be called directly. It is much easier to make 237 | use of the custom formatter by calling one of the convenience functions such as 238 | c.Printf, c.Println, or c.Printf. 239 | */ 240 | func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { 241 | return newFormatter(c, v) 242 | } 243 | 244 | // Fdump formats and displays the passed arguments to io.Writer w. It formats 245 | // exactly the same as Dump. 246 | func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { 247 | fdump(c, w, a...) 248 | } 249 | 250 | /* 251 | Dump displays the passed parameters to standard out with newlines, customizable 252 | indentation, and additional debug information such as complete types and all 253 | pointer addresses used to indirect to the final value. It provides the 254 | following features over the built-in printing facilities provided by the fmt 255 | package: 256 | 257 | * Pointers are dereferenced and followed 258 | * Circular data structures are detected and handled properly 259 | * Custom Stringer/error interfaces are optionally invoked, including 260 | on unexported types 261 | * Custom types which only implement the Stringer/error interfaces via 262 | a pointer receiver are optionally invoked when passing non-pointer 263 | variables 264 | * Byte arrays and slices are dumped like the hexdump -C command which 265 | includes offsets, byte values in hex, and ASCII output 266 | 267 | The configuration options are controlled by modifying the public members 268 | of c. See ConfigState for options documentation. 269 | 270 | See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to 271 | get the formatted result as a string. 272 | */ 273 | func (c *ConfigState) Dump(a ...interface{}) { 274 | fdump(c, os.Stdout, a...) 275 | } 276 | 277 | // Sdump returns a string with the passed arguments formatted exactly the same 278 | // as Dump. 279 | func (c *ConfigState) Sdump(a ...interface{}) string { 280 | var buf bytes.Buffer 281 | fdump(c, &buf, a...) 282 | return buf.String() 283 | } 284 | 285 | // convertArgs accepts a slice of arguments and returns a slice of the same 286 | // length with each argument converted to a spew Formatter interface using 287 | // the ConfigState associated with s. 288 | func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { 289 | formatters = make([]interface{}, len(args)) 290 | for index, arg := range args { 291 | formatters[index] = newFormatter(c, arg) 292 | } 293 | return formatters 294 | } 295 | 296 | // NewDefaultConfig returns a ConfigState with the following default settings. 297 | // 298 | // Indent: " " 299 | // MaxDepth: 0 300 | // DisableMethods: false 301 | // DisablePointerMethods: false 302 | // ContinueOnMethod: false 303 | // SortKeys: false 304 | func NewDefaultConfig() *ConfigState { 305 | return &ConfigState{Indent: " "} 306 | } 307 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | Package spew implements a deep pretty printer for Go data structures to aid in 19 | debugging. 20 | 21 | A quick overview of the additional features spew provides over the built-in 22 | printing facilities for Go data types are as follows: 23 | 24 | * Pointers are dereferenced and followed 25 | * Circular data structures are detected and handled properly 26 | * Custom Stringer/error interfaces are optionally invoked, including 27 | on unexported types 28 | * Custom types which only implement the Stringer/error interfaces via 29 | a pointer receiver are optionally invoked when passing non-pointer 30 | variables 31 | * Byte arrays and slices are dumped like the hexdump -C command which 32 | includes offsets, byte values in hex, and ASCII output (only when using 33 | Dump style) 34 | 35 | There are two different approaches spew allows for dumping Go data structures: 36 | 37 | * Dump style which prints with newlines, customizable indentation, 38 | and additional debug information such as types and all pointer addresses 39 | used to indirect to the final value 40 | * A custom Formatter interface that integrates cleanly with the standard fmt 41 | package and replaces %v, %+v, %#v, and %#+v to provide inline printing 42 | similar to the default %v while providing the additional functionality 43 | outlined above and passing unsupported format verbs such as %x and %q 44 | along to fmt 45 | 46 | Quick Start 47 | 48 | This section demonstrates how to quickly get started with spew. See the 49 | sections below for further details on formatting and configuration options. 50 | 51 | To dump a variable with full newlines, indentation, type, and pointer 52 | information use Dump, Fdump, or Sdump: 53 | spew.Dump(myVar1, myVar2, ...) 54 | spew.Fdump(someWriter, myVar1, myVar2, ...) 55 | str := spew.Sdump(myVar1, myVar2, ...) 56 | 57 | Alternatively, if you would prefer to use format strings with a compacted inline 58 | printing style, use the convenience wrappers Printf, Fprintf, etc with 59 | %v (most compact), %+v (adds pointer addresses), %#v (adds types), or 60 | %#+v (adds types and pointer addresses): 61 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 62 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 63 | spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 64 | spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 65 | 66 | Configuration Options 67 | 68 | Configuration of spew is handled by fields in the ConfigState type. For 69 | convenience, all of the top-level functions use a global state available 70 | via the spew.Config global. 71 | 72 | It is also possible to create a ConfigState instance that provides methods 73 | equivalent to the top-level functions. This allows concurrent configuration 74 | options. See the ConfigState documentation for more details. 75 | 76 | The following configuration options are available: 77 | * Indent 78 | String to use for each indentation level for Dump functions. 79 | It is a single space by default. A popular alternative is "\t". 80 | 81 | * MaxDepth 82 | Maximum number of levels to descend into nested data structures. 83 | There is no limit by default. 84 | 85 | * DisableMethods 86 | Disables invocation of error and Stringer interface methods. 87 | Method invocation is enabled by default. 88 | 89 | * DisablePointerMethods 90 | Disables invocation of error and Stringer interface methods on types 91 | which only accept pointer receivers from non-pointer variables. 92 | Pointer method invocation is enabled by default. 93 | 94 | * DisablePointerAddresses 95 | DisablePointerAddresses specifies whether to disable the printing of 96 | pointer addresses. This is useful when diffing data structures in tests. 97 | 98 | * DisableCapacities 99 | DisableCapacities specifies whether to disable the printing of 100 | capacities for arrays, slices, maps and channels. This is useful when 101 | diffing data structures in tests. 102 | 103 | * ContinueOnMethod 104 | Enables recursion into types after invoking error and Stringer interface 105 | methods. Recursion after method invocation is disabled by default. 106 | 107 | * SortKeys 108 | Specifies map keys should be sorted before being printed. Use 109 | this to have a more deterministic, diffable output. Note that 110 | only native types (bool, int, uint, floats, uintptr and string) 111 | and types which implement error or Stringer interfaces are 112 | supported with other types sorted according to the 113 | reflect.Value.String() output which guarantees display 114 | stability. Natural map order is used by default. 115 | 116 | * SpewKeys 117 | Specifies that, as a last resort attempt, map keys should be 118 | spewed to strings and sorted by those strings. This is only 119 | considered if SortKeys is true. 120 | 121 | Dump Usage 122 | 123 | Simply call spew.Dump with a list of variables you want to dump: 124 | 125 | spew.Dump(myVar1, myVar2, ...) 126 | 127 | You may also call spew.Fdump if you would prefer to output to an arbitrary 128 | io.Writer. For example, to dump to standard error: 129 | 130 | spew.Fdump(os.Stderr, myVar1, myVar2, ...) 131 | 132 | A third option is to call spew.Sdump to get the formatted output as a string: 133 | 134 | str := spew.Sdump(myVar1, myVar2, ...) 135 | 136 | Sample Dump Output 137 | 138 | See the Dump example for details on the setup of the types and variables being 139 | shown here. 140 | 141 | (main.Foo) { 142 | unexportedField: (*main.Bar)(0xf84002e210)({ 143 | flag: (main.Flag) flagTwo, 144 | data: (uintptr) 145 | }), 146 | ExportedField: (map[interface {}]interface {}) (len=1) { 147 | (string) (len=3) "one": (bool) true 148 | } 149 | } 150 | 151 | Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C 152 | command as shown. 153 | ([]uint8) (len=32 cap=32) { 154 | 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 155 | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 156 | 00000020 31 32 |12| 157 | } 158 | 159 | Custom Formatter 160 | 161 | Spew provides a custom formatter that implements the fmt.Formatter interface 162 | so that it integrates cleanly with standard fmt package printing functions. The 163 | formatter is useful for inline printing of smaller data types similar to the 164 | standard %v format specifier. 165 | 166 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 167 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 168 | combinations. Any other verbs such as %x and %q will be sent to the the 169 | standard fmt package for formatting. In addition, the custom formatter ignores 170 | the width and precision arguments (however they will still work on the format 171 | specifiers not handled by the custom formatter). 172 | 173 | Custom Formatter Usage 174 | 175 | The simplest way to make use of the spew custom formatter is to call one of the 176 | convenience functions such as spew.Printf, spew.Println, or spew.Printf. The 177 | functions have syntax you are most likely already familiar with: 178 | 179 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 180 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 181 | spew.Println(myVar, myVar2) 182 | spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 183 | spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 184 | 185 | See the Index for the full list convenience functions. 186 | 187 | Sample Formatter Output 188 | 189 | Double pointer to a uint8: 190 | %v: <**>5 191 | %+v: <**>(0xf8400420d0->0xf8400420c8)5 192 | %#v: (**uint8)5 193 | %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 194 | 195 | Pointer to circular struct with a uint8 field and a pointer to itself: 196 | %v: <*>{1 <*>} 197 | %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} 198 | %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} 199 | %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} 200 | 201 | See the Printf example for details on the setup of variables being shown 202 | here. 203 | 204 | Errors 205 | 206 | Since it is possible for custom Stringer/error interfaces to panic, spew 207 | detects them and handles them internally by printing the panic information 208 | inline with the output. Since spew is intended to provide deep pretty printing 209 | capabilities on structures, it intentionally does not return any errors. 210 | */ 211 | package spew 212 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/format.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "reflect" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | // supportedFlags is a list of all the character flags supported by fmt package. 28 | const supportedFlags = "0-+# " 29 | 30 | // formatState implements the fmt.Formatter interface and contains information 31 | // about the state of a formatting operation. The NewFormatter function can 32 | // be used to get a new Formatter which can be used directly as arguments 33 | // in standard fmt package printing calls. 34 | type formatState struct { 35 | value interface{} 36 | fs fmt.State 37 | depth int 38 | pointers map[uintptr]int 39 | ignoreNextType bool 40 | cs *ConfigState 41 | } 42 | 43 | // buildDefaultFormat recreates the original format string without precision 44 | // and width information to pass in to fmt.Sprintf in the case of an 45 | // unrecognized type. Unless new types are added to the language, this 46 | // function won't ever be called. 47 | func (f *formatState) buildDefaultFormat() (format string) { 48 | buf := bytes.NewBuffer(percentBytes) 49 | 50 | for _, flag := range supportedFlags { 51 | if f.fs.Flag(int(flag)) { 52 | buf.WriteRune(flag) 53 | } 54 | } 55 | 56 | buf.WriteRune('v') 57 | 58 | format = buf.String() 59 | return format 60 | } 61 | 62 | // constructOrigFormat recreates the original format string including precision 63 | // and width information to pass along to the standard fmt package. This allows 64 | // automatic deferral of all format strings this package doesn't support. 65 | func (f *formatState) constructOrigFormat(verb rune) (format string) { 66 | buf := bytes.NewBuffer(percentBytes) 67 | 68 | for _, flag := range supportedFlags { 69 | if f.fs.Flag(int(flag)) { 70 | buf.WriteRune(flag) 71 | } 72 | } 73 | 74 | if width, ok := f.fs.Width(); ok { 75 | buf.WriteString(strconv.Itoa(width)) 76 | } 77 | 78 | if precision, ok := f.fs.Precision(); ok { 79 | buf.Write(precisionBytes) 80 | buf.WriteString(strconv.Itoa(precision)) 81 | } 82 | 83 | buf.WriteRune(verb) 84 | 85 | format = buf.String() 86 | return format 87 | } 88 | 89 | // unpackValue returns values inside of non-nil interfaces when possible and 90 | // ensures that types for values which have been unpacked from an interface 91 | // are displayed when the show types flag is also set. 92 | // This is useful for data types like structs, arrays, slices, and maps which 93 | // can contain varying types packed inside an interface. 94 | func (f *formatState) unpackValue(v reflect.Value) reflect.Value { 95 | if v.Kind() == reflect.Interface { 96 | f.ignoreNextType = false 97 | if !v.IsNil() { 98 | v = v.Elem() 99 | } 100 | } 101 | return v 102 | } 103 | 104 | // formatPtr handles formatting of pointers by indirecting them as necessary. 105 | func (f *formatState) formatPtr(v reflect.Value) { 106 | // Display nil if top level pointer is nil. 107 | showTypes := f.fs.Flag('#') 108 | if v.IsNil() && (!showTypes || f.ignoreNextType) { 109 | f.fs.Write(nilAngleBytes) 110 | return 111 | } 112 | 113 | // Remove pointers at or below the current depth from map used to detect 114 | // circular refs. 115 | for k, depth := range f.pointers { 116 | if depth >= f.depth { 117 | delete(f.pointers, k) 118 | } 119 | } 120 | 121 | // Keep list of all dereferenced pointers to possibly show later. 122 | pointerChain := make([]uintptr, 0) 123 | 124 | // Figure out how many levels of indirection there are by derferencing 125 | // pointers and unpacking interfaces down the chain while detecting circular 126 | // references. 127 | nilFound := false 128 | cycleFound := false 129 | indirects := 0 130 | ve := v 131 | for ve.Kind() == reflect.Ptr { 132 | if ve.IsNil() { 133 | nilFound = true 134 | break 135 | } 136 | indirects++ 137 | addr := ve.Pointer() 138 | pointerChain = append(pointerChain, addr) 139 | if pd, ok := f.pointers[addr]; ok && pd < f.depth { 140 | cycleFound = true 141 | indirects-- 142 | break 143 | } 144 | f.pointers[addr] = f.depth 145 | 146 | ve = ve.Elem() 147 | if ve.Kind() == reflect.Interface { 148 | if ve.IsNil() { 149 | nilFound = true 150 | break 151 | } 152 | ve = ve.Elem() 153 | } 154 | } 155 | 156 | // Display type or indirection level depending on flags. 157 | if showTypes && !f.ignoreNextType { 158 | f.fs.Write(openParenBytes) 159 | f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) 160 | f.fs.Write([]byte(ve.Type().String())) 161 | f.fs.Write(closeParenBytes) 162 | } else { 163 | if nilFound || cycleFound { 164 | indirects += strings.Count(ve.Type().String(), "*") 165 | } 166 | f.fs.Write(openAngleBytes) 167 | f.fs.Write([]byte(strings.Repeat("*", indirects))) 168 | f.fs.Write(closeAngleBytes) 169 | } 170 | 171 | // Display pointer information depending on flags. 172 | if f.fs.Flag('+') && (len(pointerChain) > 0) { 173 | f.fs.Write(openParenBytes) 174 | for i, addr := range pointerChain { 175 | if i > 0 { 176 | f.fs.Write(pointerChainBytes) 177 | } 178 | printHexPtr(f.fs, addr) 179 | } 180 | f.fs.Write(closeParenBytes) 181 | } 182 | 183 | // Display dereferenced value. 184 | switch { 185 | case nilFound: 186 | f.fs.Write(nilAngleBytes) 187 | 188 | case cycleFound: 189 | f.fs.Write(circularShortBytes) 190 | 191 | default: 192 | f.ignoreNextType = true 193 | f.format(ve) 194 | } 195 | } 196 | 197 | // format is the main workhorse for providing the Formatter interface. It 198 | // uses the passed reflect value to figure out what kind of object we are 199 | // dealing with and formats it appropriately. It is a recursive function, 200 | // however circular data structures are detected and handled properly. 201 | func (f *formatState) format(v reflect.Value) { 202 | // Handle invalid reflect values immediately. 203 | kind := v.Kind() 204 | if kind == reflect.Invalid { 205 | f.fs.Write(invalidAngleBytes) 206 | return 207 | } 208 | 209 | // Handle pointers specially. 210 | if kind == reflect.Ptr { 211 | f.formatPtr(v) 212 | return 213 | } 214 | 215 | // Print type information unless already handled elsewhere. 216 | if !f.ignoreNextType && f.fs.Flag('#') { 217 | f.fs.Write(openParenBytes) 218 | f.fs.Write([]byte(v.Type().String())) 219 | f.fs.Write(closeParenBytes) 220 | } 221 | f.ignoreNextType = false 222 | 223 | // Call Stringer/error interfaces if they exist and the handle methods 224 | // flag is enabled. 225 | if !f.cs.DisableMethods { 226 | if (kind != reflect.Invalid) && (kind != reflect.Interface) { 227 | if handled := handleMethods(f.cs, f.fs, v); handled { 228 | return 229 | } 230 | } 231 | } 232 | 233 | switch kind { 234 | case reflect.Invalid: 235 | // Do nothing. We should never get here since invalid has already 236 | // been handled above. 237 | 238 | case reflect.Bool: 239 | printBool(f.fs, v.Bool()) 240 | 241 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 242 | printInt(f.fs, v.Int(), 10) 243 | 244 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 245 | printUint(f.fs, v.Uint(), 10) 246 | 247 | case reflect.Float32: 248 | printFloat(f.fs, v.Float(), 32) 249 | 250 | case reflect.Float64: 251 | printFloat(f.fs, v.Float(), 64) 252 | 253 | case reflect.Complex64: 254 | printComplex(f.fs, v.Complex(), 32) 255 | 256 | case reflect.Complex128: 257 | printComplex(f.fs, v.Complex(), 64) 258 | 259 | case reflect.Slice: 260 | if v.IsNil() { 261 | f.fs.Write(nilAngleBytes) 262 | break 263 | } 264 | fallthrough 265 | 266 | case reflect.Array: 267 | f.fs.Write(openBracketBytes) 268 | f.depth++ 269 | if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { 270 | f.fs.Write(maxShortBytes) 271 | } else { 272 | numEntries := v.Len() 273 | for i := 0; i < numEntries; i++ { 274 | if i > 0 { 275 | f.fs.Write(spaceBytes) 276 | } 277 | f.ignoreNextType = true 278 | f.format(f.unpackValue(v.Index(i))) 279 | } 280 | } 281 | f.depth-- 282 | f.fs.Write(closeBracketBytes) 283 | 284 | case reflect.String: 285 | f.fs.Write([]byte(v.String())) 286 | 287 | case reflect.Interface: 288 | // The only time we should get here is for nil interfaces due to 289 | // unpackValue calls. 290 | if v.IsNil() { 291 | f.fs.Write(nilAngleBytes) 292 | } 293 | 294 | case reflect.Ptr: 295 | // Do nothing. We should never get here since pointers have already 296 | // been handled above. 297 | 298 | case reflect.Map: 299 | // nil maps should be indicated as different than empty maps 300 | if v.IsNil() { 301 | f.fs.Write(nilAngleBytes) 302 | break 303 | } 304 | 305 | f.fs.Write(openMapBytes) 306 | f.depth++ 307 | if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { 308 | f.fs.Write(maxShortBytes) 309 | } else { 310 | keys := v.MapKeys() 311 | if f.cs.SortKeys { 312 | sortValues(keys, f.cs) 313 | } 314 | for i, key := range keys { 315 | if i > 0 { 316 | f.fs.Write(spaceBytes) 317 | } 318 | f.ignoreNextType = true 319 | f.format(f.unpackValue(key)) 320 | f.fs.Write(colonBytes) 321 | f.ignoreNextType = true 322 | f.format(f.unpackValue(v.MapIndex(key))) 323 | } 324 | } 325 | f.depth-- 326 | f.fs.Write(closeMapBytes) 327 | 328 | case reflect.Struct: 329 | numFields := v.NumField() 330 | f.fs.Write(openBraceBytes) 331 | f.depth++ 332 | if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { 333 | f.fs.Write(maxShortBytes) 334 | } else { 335 | vt := v.Type() 336 | for i := 0; i < numFields; i++ { 337 | if i > 0 { 338 | f.fs.Write(spaceBytes) 339 | } 340 | vtf := vt.Field(i) 341 | if f.fs.Flag('+') || f.fs.Flag('#') { 342 | f.fs.Write([]byte(vtf.Name)) 343 | f.fs.Write(colonBytes) 344 | } 345 | f.format(f.unpackValue(v.Field(i))) 346 | } 347 | } 348 | f.depth-- 349 | f.fs.Write(closeBraceBytes) 350 | 351 | case reflect.Uintptr: 352 | printHexPtr(f.fs, uintptr(v.Uint())) 353 | 354 | case reflect.UnsafePointer, reflect.Chan, reflect.Func: 355 | printHexPtr(f.fs, v.Pointer()) 356 | 357 | // There were not any other types at the time this code was written, but 358 | // fall back to letting the default fmt package handle it if any get added. 359 | default: 360 | format := f.buildDefaultFormat() 361 | if v.CanInterface() { 362 | fmt.Fprintf(f.fs, format, v.Interface()) 363 | } else { 364 | fmt.Fprintf(f.fs, format, v.String()) 365 | } 366 | } 367 | } 368 | 369 | // Format satisfies the fmt.Formatter interface. See NewFormatter for usage 370 | // details. 371 | func (f *formatState) Format(fs fmt.State, verb rune) { 372 | f.fs = fs 373 | 374 | // Use standard formatting for verbs that are not v. 375 | if verb != 'v' { 376 | format := f.constructOrigFormat(verb) 377 | fmt.Fprintf(fs, format, f.value) 378 | return 379 | } 380 | 381 | if f.value == nil { 382 | if fs.Flag('#') { 383 | fs.Write(interfaceBytes) 384 | } 385 | fs.Write(nilAngleBytes) 386 | return 387 | } 388 | 389 | f.format(reflect.ValueOf(f.value)) 390 | } 391 | 392 | // newFormatter is a helper function to consolidate the logic from the various 393 | // public methods which take varying config states. 394 | func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { 395 | fs := &formatState{value: v, cs: cs} 396 | fs.pointers = make(map[uintptr]int) 397 | return fs 398 | } 399 | 400 | /* 401 | NewFormatter returns a custom formatter that satisfies the fmt.Formatter 402 | interface. As a result, it integrates cleanly with standard fmt package 403 | printing functions. The formatter is useful for inline printing of smaller data 404 | types similar to the standard %v format specifier. 405 | 406 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 407 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 408 | combinations. Any other verbs such as %x and %q will be sent to the the 409 | standard fmt package for formatting. In addition, the custom formatter ignores 410 | the width and precision arguments (however they will still work on the format 411 | specifiers not handled by the custom formatter). 412 | 413 | Typically this function shouldn't be called directly. It is much easier to make 414 | use of the custom formatter by calling one of the convenience functions such as 415 | Printf, Println, or Fprintf. 416 | */ 417 | func NewFormatter(v interface{}) fmt.Formatter { 418 | return newFormatter(&Config, v) 419 | } 420 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_compare.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "time" 8 | ) 9 | 10 | // Deprecated: CompareType has only ever been for internal use and has accidentally been published since v1.6.0. Do not use it. 11 | type CompareType = compareResult 12 | 13 | type compareResult int 14 | 15 | const ( 16 | compareLess compareResult = iota - 1 17 | compareEqual 18 | compareGreater 19 | ) 20 | 21 | var ( 22 | intType = reflect.TypeOf(int(1)) 23 | int8Type = reflect.TypeOf(int8(1)) 24 | int16Type = reflect.TypeOf(int16(1)) 25 | int32Type = reflect.TypeOf(int32(1)) 26 | int64Type = reflect.TypeOf(int64(1)) 27 | 28 | uintType = reflect.TypeOf(uint(1)) 29 | uint8Type = reflect.TypeOf(uint8(1)) 30 | uint16Type = reflect.TypeOf(uint16(1)) 31 | uint32Type = reflect.TypeOf(uint32(1)) 32 | uint64Type = reflect.TypeOf(uint64(1)) 33 | 34 | uintptrType = reflect.TypeOf(uintptr(1)) 35 | 36 | float32Type = reflect.TypeOf(float32(1)) 37 | float64Type = reflect.TypeOf(float64(1)) 38 | 39 | stringType = reflect.TypeOf("") 40 | 41 | timeType = reflect.TypeOf(time.Time{}) 42 | bytesType = reflect.TypeOf([]byte{}) 43 | ) 44 | 45 | func compare(obj1, obj2 interface{}, kind reflect.Kind) (compareResult, bool) { 46 | obj1Value := reflect.ValueOf(obj1) 47 | obj2Value := reflect.ValueOf(obj2) 48 | 49 | // throughout this switch we try and avoid calling .Convert() if possible, 50 | // as this has a pretty big performance impact 51 | switch kind { 52 | case reflect.Int: 53 | { 54 | intobj1, ok := obj1.(int) 55 | if !ok { 56 | intobj1 = obj1Value.Convert(intType).Interface().(int) 57 | } 58 | intobj2, ok := obj2.(int) 59 | if !ok { 60 | intobj2 = obj2Value.Convert(intType).Interface().(int) 61 | } 62 | if intobj1 > intobj2 { 63 | return compareGreater, true 64 | } 65 | if intobj1 == intobj2 { 66 | return compareEqual, true 67 | } 68 | if intobj1 < intobj2 { 69 | return compareLess, true 70 | } 71 | } 72 | case reflect.Int8: 73 | { 74 | int8obj1, ok := obj1.(int8) 75 | if !ok { 76 | int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) 77 | } 78 | int8obj2, ok := obj2.(int8) 79 | if !ok { 80 | int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) 81 | } 82 | if int8obj1 > int8obj2 { 83 | return compareGreater, true 84 | } 85 | if int8obj1 == int8obj2 { 86 | return compareEqual, true 87 | } 88 | if int8obj1 < int8obj2 { 89 | return compareLess, true 90 | } 91 | } 92 | case reflect.Int16: 93 | { 94 | int16obj1, ok := obj1.(int16) 95 | if !ok { 96 | int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) 97 | } 98 | int16obj2, ok := obj2.(int16) 99 | if !ok { 100 | int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) 101 | } 102 | if int16obj1 > int16obj2 { 103 | return compareGreater, true 104 | } 105 | if int16obj1 == int16obj2 { 106 | return compareEqual, true 107 | } 108 | if int16obj1 < int16obj2 { 109 | return compareLess, true 110 | } 111 | } 112 | case reflect.Int32: 113 | { 114 | int32obj1, ok := obj1.(int32) 115 | if !ok { 116 | int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) 117 | } 118 | int32obj2, ok := obj2.(int32) 119 | if !ok { 120 | int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) 121 | } 122 | if int32obj1 > int32obj2 { 123 | return compareGreater, true 124 | } 125 | if int32obj1 == int32obj2 { 126 | return compareEqual, true 127 | } 128 | if int32obj1 < int32obj2 { 129 | return compareLess, true 130 | } 131 | } 132 | case reflect.Int64: 133 | { 134 | int64obj1, ok := obj1.(int64) 135 | if !ok { 136 | int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) 137 | } 138 | int64obj2, ok := obj2.(int64) 139 | if !ok { 140 | int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) 141 | } 142 | if int64obj1 > int64obj2 { 143 | return compareGreater, true 144 | } 145 | if int64obj1 == int64obj2 { 146 | return compareEqual, true 147 | } 148 | if int64obj1 < int64obj2 { 149 | return compareLess, true 150 | } 151 | } 152 | case reflect.Uint: 153 | { 154 | uintobj1, ok := obj1.(uint) 155 | if !ok { 156 | uintobj1 = obj1Value.Convert(uintType).Interface().(uint) 157 | } 158 | uintobj2, ok := obj2.(uint) 159 | if !ok { 160 | uintobj2 = obj2Value.Convert(uintType).Interface().(uint) 161 | } 162 | if uintobj1 > uintobj2 { 163 | return compareGreater, true 164 | } 165 | if uintobj1 == uintobj2 { 166 | return compareEqual, true 167 | } 168 | if uintobj1 < uintobj2 { 169 | return compareLess, true 170 | } 171 | } 172 | case reflect.Uint8: 173 | { 174 | uint8obj1, ok := obj1.(uint8) 175 | if !ok { 176 | uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) 177 | } 178 | uint8obj2, ok := obj2.(uint8) 179 | if !ok { 180 | uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) 181 | } 182 | if uint8obj1 > uint8obj2 { 183 | return compareGreater, true 184 | } 185 | if uint8obj1 == uint8obj2 { 186 | return compareEqual, true 187 | } 188 | if uint8obj1 < uint8obj2 { 189 | return compareLess, true 190 | } 191 | } 192 | case reflect.Uint16: 193 | { 194 | uint16obj1, ok := obj1.(uint16) 195 | if !ok { 196 | uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) 197 | } 198 | uint16obj2, ok := obj2.(uint16) 199 | if !ok { 200 | uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) 201 | } 202 | if uint16obj1 > uint16obj2 { 203 | return compareGreater, true 204 | } 205 | if uint16obj1 == uint16obj2 { 206 | return compareEqual, true 207 | } 208 | if uint16obj1 < uint16obj2 { 209 | return compareLess, true 210 | } 211 | } 212 | case reflect.Uint32: 213 | { 214 | uint32obj1, ok := obj1.(uint32) 215 | if !ok { 216 | uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) 217 | } 218 | uint32obj2, ok := obj2.(uint32) 219 | if !ok { 220 | uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) 221 | } 222 | if uint32obj1 > uint32obj2 { 223 | return compareGreater, true 224 | } 225 | if uint32obj1 == uint32obj2 { 226 | return compareEqual, true 227 | } 228 | if uint32obj1 < uint32obj2 { 229 | return compareLess, true 230 | } 231 | } 232 | case reflect.Uint64: 233 | { 234 | uint64obj1, ok := obj1.(uint64) 235 | if !ok { 236 | uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) 237 | } 238 | uint64obj2, ok := obj2.(uint64) 239 | if !ok { 240 | uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) 241 | } 242 | if uint64obj1 > uint64obj2 { 243 | return compareGreater, true 244 | } 245 | if uint64obj1 == uint64obj2 { 246 | return compareEqual, true 247 | } 248 | if uint64obj1 < uint64obj2 { 249 | return compareLess, true 250 | } 251 | } 252 | case reflect.Float32: 253 | { 254 | float32obj1, ok := obj1.(float32) 255 | if !ok { 256 | float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) 257 | } 258 | float32obj2, ok := obj2.(float32) 259 | if !ok { 260 | float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) 261 | } 262 | if float32obj1 > float32obj2 { 263 | return compareGreater, true 264 | } 265 | if float32obj1 == float32obj2 { 266 | return compareEqual, true 267 | } 268 | if float32obj1 < float32obj2 { 269 | return compareLess, true 270 | } 271 | } 272 | case reflect.Float64: 273 | { 274 | float64obj1, ok := obj1.(float64) 275 | if !ok { 276 | float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) 277 | } 278 | float64obj2, ok := obj2.(float64) 279 | if !ok { 280 | float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) 281 | } 282 | if float64obj1 > float64obj2 { 283 | return compareGreater, true 284 | } 285 | if float64obj1 == float64obj2 { 286 | return compareEqual, true 287 | } 288 | if float64obj1 < float64obj2 { 289 | return compareLess, true 290 | } 291 | } 292 | case reflect.String: 293 | { 294 | stringobj1, ok := obj1.(string) 295 | if !ok { 296 | stringobj1 = obj1Value.Convert(stringType).Interface().(string) 297 | } 298 | stringobj2, ok := obj2.(string) 299 | if !ok { 300 | stringobj2 = obj2Value.Convert(stringType).Interface().(string) 301 | } 302 | if stringobj1 > stringobj2 { 303 | return compareGreater, true 304 | } 305 | if stringobj1 == stringobj2 { 306 | return compareEqual, true 307 | } 308 | if stringobj1 < stringobj2 { 309 | return compareLess, true 310 | } 311 | } 312 | // Check for known struct types we can check for compare results. 313 | case reflect.Struct: 314 | { 315 | // All structs enter here. We're not interested in most types. 316 | if !obj1Value.CanConvert(timeType) { 317 | break 318 | } 319 | 320 | // time.Time can be compared! 321 | timeObj1, ok := obj1.(time.Time) 322 | if !ok { 323 | timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) 324 | } 325 | 326 | timeObj2, ok := obj2.(time.Time) 327 | if !ok { 328 | timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) 329 | } 330 | 331 | if timeObj1.Before(timeObj2) { 332 | return compareLess, true 333 | } 334 | if timeObj1.Equal(timeObj2) { 335 | return compareEqual, true 336 | } 337 | return compareGreater, true 338 | } 339 | case reflect.Slice: 340 | { 341 | // We only care about the []byte type. 342 | if !obj1Value.CanConvert(bytesType) { 343 | break 344 | } 345 | 346 | // []byte can be compared! 347 | bytesObj1, ok := obj1.([]byte) 348 | if !ok { 349 | bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) 350 | 351 | } 352 | bytesObj2, ok := obj2.([]byte) 353 | if !ok { 354 | bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) 355 | } 356 | 357 | return compareResult(bytes.Compare(bytesObj1, bytesObj2)), true 358 | } 359 | case reflect.Uintptr: 360 | { 361 | uintptrObj1, ok := obj1.(uintptr) 362 | if !ok { 363 | uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr) 364 | } 365 | uintptrObj2, ok := obj2.(uintptr) 366 | if !ok { 367 | uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr) 368 | } 369 | if uintptrObj1 > uintptrObj2 { 370 | return compareGreater, true 371 | } 372 | if uintptrObj1 == uintptrObj2 { 373 | return compareEqual, true 374 | } 375 | if uintptrObj1 < uintptrObj2 { 376 | return compareLess, true 377 | } 378 | } 379 | } 380 | 381 | return compareEqual, false 382 | } 383 | 384 | // Greater asserts that the first element is greater than the second 385 | // 386 | // assert.Greater(t, 2, 1) 387 | // assert.Greater(t, float64(2), float64(1)) 388 | // assert.Greater(t, "b", "a") 389 | func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 390 | if h, ok := t.(tHelper); ok { 391 | h.Helper() 392 | } 393 | return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) 394 | } 395 | 396 | // GreaterOrEqual asserts that the first element is greater than or equal to the second 397 | // 398 | // assert.GreaterOrEqual(t, 2, 1) 399 | // assert.GreaterOrEqual(t, 2, 2) 400 | // assert.GreaterOrEqual(t, "b", "a") 401 | // assert.GreaterOrEqual(t, "b", "b") 402 | func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 403 | if h, ok := t.(tHelper); ok { 404 | h.Helper() 405 | } 406 | return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) 407 | } 408 | 409 | // Less asserts that the first element is less than the second 410 | // 411 | // assert.Less(t, 1, 2) 412 | // assert.Less(t, float64(1), float64(2)) 413 | // assert.Less(t, "a", "b") 414 | func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 415 | if h, ok := t.(tHelper); ok { 416 | h.Helper() 417 | } 418 | return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) 419 | } 420 | 421 | // LessOrEqual asserts that the first element is less than or equal to the second 422 | // 423 | // assert.LessOrEqual(t, 1, 2) 424 | // assert.LessOrEqual(t, 2, 2) 425 | // assert.LessOrEqual(t, "a", "b") 426 | // assert.LessOrEqual(t, "b", "b") 427 | func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 428 | if h, ok := t.(tHelper); ok { 429 | h.Helper() 430 | } 431 | return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) 432 | } 433 | 434 | // Positive asserts that the specified element is positive 435 | // 436 | // assert.Positive(t, 1) 437 | // assert.Positive(t, 1.23) 438 | func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { 439 | if h, ok := t.(tHelper); ok { 440 | h.Helper() 441 | } 442 | zero := reflect.Zero(reflect.TypeOf(e)) 443 | return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...) 444 | } 445 | 446 | // Negative asserts that the specified element is negative 447 | // 448 | // assert.Negative(t, -1) 449 | // assert.Negative(t, -1.23) 450 | func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { 451 | if h, ok := t.(tHelper); ok { 452 | h.Helper() 453 | } 454 | zero := reflect.Zero(reflect.TypeOf(e)) 455 | return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...) 456 | } 457 | 458 | func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { 459 | if h, ok := t.(tHelper); ok { 460 | h.Helper() 461 | } 462 | 463 | e1Kind := reflect.ValueOf(e1).Kind() 464 | e2Kind := reflect.ValueOf(e2).Kind() 465 | if e1Kind != e2Kind { 466 | return Fail(t, "Elements should be the same type", msgAndArgs...) 467 | } 468 | 469 | compareResult, isComparable := compare(e1, e2, e1Kind) 470 | if !isComparable { 471 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) 472 | } 473 | 474 | if !containsValue(allowedComparesResults, compareResult) { 475 | return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) 476 | } 477 | 478 | return true 479 | } 480 | 481 | func containsValue(values []compareResult, value compareResult) bool { 482 | for _, v := range values { 483 | if v == value { 484 | return true 485 | } 486 | } 487 | 488 | return false 489 | } 490 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentFormat}} 2 | func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_order.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // isOrdered checks that collection contains orderable elements. 9 | func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { 10 | objKind := reflect.TypeOf(object).Kind() 11 | if objKind != reflect.Slice && objKind != reflect.Array { 12 | return false 13 | } 14 | 15 | objValue := reflect.ValueOf(object) 16 | objLen := objValue.Len() 17 | 18 | if objLen <= 1 { 19 | return true 20 | } 21 | 22 | value := objValue.Index(0) 23 | valueInterface := value.Interface() 24 | firstValueKind := value.Kind() 25 | 26 | for i := 1; i < objLen; i++ { 27 | prevValue := value 28 | prevValueInterface := valueInterface 29 | 30 | value = objValue.Index(i) 31 | valueInterface = value.Interface() 32 | 33 | compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) 34 | 35 | if !isComparable { 36 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) 37 | } 38 | 39 | if !containsValue(allowedComparesResults, compareResult) { 40 | return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) 41 | } 42 | } 43 | 44 | return true 45 | } 46 | 47 | // IsIncreasing asserts that the collection is increasing 48 | // 49 | // assert.IsIncreasing(t, []int{1, 2, 3}) 50 | // assert.IsIncreasing(t, []float{1, 2}) 51 | // assert.IsIncreasing(t, []string{"a", "b"}) 52 | func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 53 | return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) 54 | } 55 | 56 | // IsNonIncreasing asserts that the collection is not increasing 57 | // 58 | // assert.IsNonIncreasing(t, []int{2, 1, 1}) 59 | // assert.IsNonIncreasing(t, []float{2, 1}) 60 | // assert.IsNonIncreasing(t, []string{"b", "a"}) 61 | func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 62 | return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) 63 | } 64 | 65 | // IsDecreasing asserts that the collection is decreasing 66 | // 67 | // assert.IsDecreasing(t, []int{2, 1, 0}) 68 | // assert.IsDecreasing(t, []float{2, 1}) 69 | // assert.IsDecreasing(t, []string{"b", "a"}) 70 | func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 71 | return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) 72 | } 73 | 74 | // IsNonDecreasing asserts that the collection is not decreasing 75 | // 76 | // assert.IsNonDecreasing(t, []int{1, 1, 2}) 77 | // assert.IsNonDecreasing(t, []float{1, 2}) 78 | // assert.IsNonDecreasing(t, []string{"a", "b"}) 79 | func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 80 | return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) 81 | } 82 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // # Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // 7 | // import ( 8 | // "testing" 9 | // "github.com/stretchr/testify/assert" 10 | // ) 11 | // 12 | // func TestSomething(t *testing.T) { 13 | // 14 | // var a string = "Hello" 15 | // var b string = "Hello" 16 | // 17 | // assert.Equal(t, a, b, "The two words should be the same.") 18 | // 19 | // } 20 | // 21 | // if you assert many times, use the format below: 22 | // 23 | // import ( 24 | // "testing" 25 | // "github.com/stretchr/testify/assert" 26 | // ) 27 | // 28 | // func TestSomething(t *testing.T) { 29 | // assert := assert.New(t) 30 | // 31 | // var a string = "Hello" 32 | // var b string = "Hello" 33 | // 34 | // assert.Equal(a, b, "The two words should be the same.") 35 | // } 36 | // 37 | // # Assertions 38 | // 39 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 40 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 41 | // testing framework. This allows the assertion funcs to write the failings and other details to 42 | // the correct place. 43 | // 44 | // Every assertion function also takes an optional string message as the final argument, 45 | // allowing custom error messages to be appended to the message the assertion method outputs. 46 | package assert 47 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 and 12 | // an error if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url, http.NoBody) 16 | if err != nil { 17 | return -1, err 18 | } 19 | req.URL.RawQuery = values.Encode() 20 | handler(w, req) 21 | return w.Code, nil 22 | } 23 | 24 | // HTTPSuccess asserts that a specified handler returns a success status code. 25 | // 26 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 27 | // 28 | // Returns whether the assertion was successful (true) or not (false). 29 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 30 | if h, ok := t.(tHelper); ok { 31 | h.Helper() 32 | } 33 | code, err := httpCode(handler, method, url, values) 34 | if err != nil { 35 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 36 | } 37 | 38 | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent 39 | if !isSuccessCode { 40 | Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 41 | } 42 | 43 | return isSuccessCode 44 | } 45 | 46 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 47 | // 48 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 49 | // 50 | // Returns whether the assertion was successful (true) or not (false). 51 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 52 | if h, ok := t.(tHelper); ok { 53 | h.Helper() 54 | } 55 | code, err := httpCode(handler, method, url, values) 56 | if err != nil { 57 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 58 | } 59 | 60 | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 61 | if !isRedirectCode { 62 | Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 63 | } 64 | 65 | return isRedirectCode 66 | } 67 | 68 | // HTTPError asserts that a specified handler returns an error status code. 69 | // 70 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 71 | // 72 | // Returns whether the assertion was successful (true) or not (false). 73 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 74 | if h, ok := t.(tHelper); ok { 75 | h.Helper() 76 | } 77 | code, err := httpCode(handler, method, url, values) 78 | if err != nil { 79 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 80 | } 81 | 82 | isErrorCode := code >= http.StatusBadRequest 83 | if !isErrorCode { 84 | Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 85 | } 86 | 87 | return isErrorCode 88 | } 89 | 90 | // HTTPStatusCode asserts that a specified handler returns a specified status code. 91 | // 92 | // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) 93 | // 94 | // Returns whether the assertion was successful (true) or not (false). 95 | func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { 96 | if h, ok := t.(tHelper); ok { 97 | h.Helper() 98 | } 99 | code, err := httpCode(handler, method, url, values) 100 | if err != nil { 101 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 102 | } 103 | 104 | successful := code == statuscode 105 | if !successful { 106 | Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...) 107 | } 108 | 109 | return successful 110 | } 111 | 112 | // HTTPBody is a helper that returns HTTP body of the response. It returns 113 | // empty string if building a new request fails. 114 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 115 | w := httptest.NewRecorder() 116 | if len(values) > 0 { 117 | url += "?" + values.Encode() 118 | } 119 | req, err := http.NewRequest(method, url, http.NoBody) 120 | if err != nil { 121 | return "" 122 | } 123 | handler(w, req) 124 | return w.Body.String() 125 | } 126 | 127 | // HTTPBodyContains asserts that a specified handler returns a 128 | // body that contains a string. 129 | // 130 | // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 131 | // 132 | // Returns whether the assertion was successful (true) or not (false). 133 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 134 | if h, ok := t.(tHelper); ok { 135 | h.Helper() 136 | } 137 | body := HTTPBody(handler, method, url, values) 138 | 139 | contains := strings.Contains(body, fmt.Sprint(str)) 140 | if !contains { 141 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) 142 | } 143 | 144 | return contains 145 | } 146 | 147 | // HTTPBodyNotContains asserts that a specified handler returns a 148 | // body that does not contain a string. 149 | // 150 | // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 151 | // 152 | // Returns whether the assertion was successful (true) or not (false). 153 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 154 | if h, ok := t.(tHelper); ok { 155 | h.Helper() 156 | } 157 | body := HTTPBody(handler, method, url, values) 158 | 159 | contains := strings.Contains(body, fmt.Sprint(str)) 160 | if contains { 161 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) 162 | } 163 | 164 | return !contains 165 | } 166 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default 2 | // +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default 3 | 4 | // Package yaml is an implementation of YAML functions that calls a pluggable implementation. 5 | // 6 | // This implementation is selected with the testify_yaml_custom build tag. 7 | // 8 | // go test -tags testify_yaml_custom 9 | // 10 | // This implementation can be used at build time to replace the default implementation 11 | // to avoid linking with [gopkg.in/yaml.v3]. 12 | // 13 | // In your test package: 14 | // 15 | // import assertYaml "github.com/stretchr/testify/assert/yaml" 16 | // 17 | // func init() { 18 | // assertYaml.Unmarshal = func (in []byte, out interface{}) error { 19 | // // ... 20 | // return nil 21 | // } 22 | // } 23 | package yaml 24 | 25 | var Unmarshal func(in []byte, out interface{}) error 26 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go: -------------------------------------------------------------------------------- 1 | //go:build !testify_yaml_fail && !testify_yaml_custom 2 | // +build !testify_yaml_fail,!testify_yaml_custom 3 | 4 | // Package yaml is just an indirection to handle YAML deserialization. 5 | // 6 | // This package is just an indirection that allows the builder to override the 7 | // indirection with an alternative implementation of this package that uses 8 | // another implementation of YAML deserialization. This allows to not either not 9 | // use YAML deserialization at all, or to use another implementation than 10 | // [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). 11 | // 12 | // Alternative implementations are selected using build tags: 13 | // 14 | // - testify_yaml_fail: [Unmarshal] always fails with an error 15 | // - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it 16 | // before calling any of [github.com/stretchr/testify/assert.YAMLEq] or 17 | // [github.com/stretchr/testify/assert.YAMLEqf]. 18 | // 19 | // Usage: 20 | // 21 | // go test -tags testify_yaml_fail 22 | // 23 | // You can check with "go list" which implementation is linked: 24 | // 25 | // go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 26 | // go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 27 | // go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 28 | // 29 | // [PR #1120]: https://github.com/stretchr/testify/pull/1120 30 | package yaml 31 | 32 | import goyaml "gopkg.in/yaml.v3" 33 | 34 | // Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. 35 | func Unmarshal(in []byte, out interface{}) error { 36 | return goyaml.Unmarshal(in, out) 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default 2 | // +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default 3 | 4 | // Package yaml is an implementation of YAML functions that always fail. 5 | // 6 | // This implementation can be used at build time to replace the default implementation 7 | // to avoid linking with [gopkg.in/yaml.v3]: 8 | // 9 | // go test -tags testify_yaml_fail 10 | package yaml 11 | 12 | import "errors" 13 | 14 | var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") 15 | 16 | func Unmarshal([]byte, interface{}) error { 17 | return errNotImplemented 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/doc.go: -------------------------------------------------------------------------------- 1 | // Package require implements the same assertions as the `assert` package but 2 | // stops test execution when a test fails. 3 | // 4 | // # Example Usage 5 | // 6 | // The following is a complete example using require in a standard test function: 7 | // 8 | // import ( 9 | // "testing" 10 | // "github.com/stretchr/testify/require" 11 | // ) 12 | // 13 | // func TestSomething(t *testing.T) { 14 | // 15 | // var a string = "Hello" 16 | // var b string = "Hello" 17 | // 18 | // require.Equal(t, a, b, "The two words should be the same.") 19 | // 20 | // } 21 | // 22 | // # Assertions 23 | // 24 | // The `require` package have same global functions as in the `assert` package, 25 | // but instead of returning a boolean result they call `t.FailNow()`. 26 | // 27 | // Every assertion function also takes an optional string message as the final argument, 28 | // allowing custom error messages to be appended to the message the assertion method outputs. 29 | package require 30 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/forward_requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/require.go.tmpl: -------------------------------------------------------------------------------- 1 | {{ replace .Comment "assert." "require."}} 2 | func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } 5 | t.FailNow() 6 | } 7 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/require_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // TestingT is an interface wrapper around *testing.T 4 | type TestingT interface { 5 | Errorf(format string, args ...interface{}) 6 | FailNow() 7 | } 8 | 9 | type tHelper = interface { 10 | Helper() 11 | } 12 | 13 | // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful 14 | // for table driven tests. 15 | type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) 16 | 17 | // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful 18 | // for table driven tests. 19 | type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) 20 | 21 | // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful 22 | // for table driven tests. 23 | type BoolAssertionFunc func(TestingT, bool, ...interface{}) 24 | 25 | // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful 26 | // for table driven tests. 27 | type ErrorAssertionFunc func(TestingT, error, ...interface{}) 28 | 29 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" 30 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | This project is covered by two different licenses: MIT and Apache. 3 | 4 | #### MIT License #### 5 | 6 | The following files were ported to Go from C files of libyaml, and thus 7 | are still covered by their original MIT license, with the additional 8 | copyright staring in 2011 when the project was ported over: 9 | 10 | apic.go emitterc.go parserc.go readerc.go scannerc.go 11 | writerc.go yamlh.go yamlprivateh.go 12 | 13 | Copyright (c) 2006-2010 Kirill Simonov 14 | Copyright (c) 2006-2011 Kirill Simonov 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of 17 | this software and associated documentation files (the "Software"), to deal in 18 | the Software without restriction, including without limitation the rights to 19 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 20 | of the Software, and to permit persons to whom the Software is furnished to do 21 | so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | 34 | ### Apache License ### 35 | 36 | All the remaining project files are covered by the Apache license: 37 | 38 | Copyright (c) 2011-2019 Canonical Ltd 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.2, but preserves some behavior 16 | from 1.1 for backwards compatibility. 17 | 18 | Specifically, as of v3 of the yaml package: 19 | 20 | - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being 21 | decoded into a typed bool value. Otherwise they behave as a string. Booleans 22 | in YAML 1.2 are _true/false_ only. 23 | - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ 24 | as specified in YAML 1.2, because most parsers still use the old format. 25 | Octals in the _0o777_ format are supported though, so new files work. 26 | - Does not support base-60 floats. These are gone from YAML 1.2, and were 27 | actually never supported by this package as it's clearly a poor choice. 28 | 29 | and offers backwards 30 | compatibility with YAML 1.1 in some cases. 31 | 1.2, including support for 32 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 33 | implemented, and base-60 floats from YAML 1.1 are purposefully not 34 | supported since they're a poor design and are gone in YAML 1.2. 35 | 36 | Installation and usage 37 | ---------------------- 38 | 39 | The import path for the package is *gopkg.in/yaml.v3*. 40 | 41 | To install it, run: 42 | 43 | go get gopkg.in/yaml.v3 44 | 45 | API documentation 46 | ----------------- 47 | 48 | If opened in a browser, the import path itself leads to the API documentation: 49 | 50 | - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) 51 | 52 | API stability 53 | ------------- 54 | 55 | The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). 56 | 57 | 58 | License 59 | ------- 60 | 61 | The yaml package is licensed under the MIT and Apache License 2.0 licenses. 62 | Please see the LICENSE file for details. 63 | 64 | 65 | Example 66 | ------- 67 | 68 | ```Go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | "log" 74 | 75 | "gopkg.in/yaml.v3" 76 | ) 77 | 78 | var data = ` 79 | a: Easy! 80 | b: 81 | c: 2 82 | d: [3, 4] 83 | ` 84 | 85 | // Note: struct fields must be public in order for unmarshal to 86 | // correctly populate the data. 87 | type T struct { 88 | A string 89 | B struct { 90 | RenamedC int `yaml:"c"` 91 | D []int `yaml:",flow"` 92 | } 93 | } 94 | 95 | func main() { 96 | t := T{} 97 | 98 | err := yaml.Unmarshal([]byte(data), &t) 99 | if err != nil { 100 | log.Fatalf("error: %v", err) 101 | } 102 | fmt.Printf("--- t:\n%v\n\n", t) 103 | 104 | d, err := yaml.Marshal(&t) 105 | if err != nil { 106 | log.Fatalf("error: %v", err) 107 | } 108 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 109 | 110 | m := make(map[interface{}]interface{}) 111 | 112 | err = yaml.Unmarshal([]byte(data), &m) 113 | if err != nil { 114 | log.Fatalf("error: %v", err) 115 | } 116 | fmt.Printf("--- m:\n%v\n\n", m) 117 | 118 | d, err = yaml.Marshal(&m) 119 | if err != nil { 120 | log.Fatalf("error: %v", err) 121 | } 122 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 123 | } 124 | ``` 125 | 126 | This example will generate the following output: 127 | 128 | ``` 129 | --- t: 130 | {Easy! {2 [3 4]}} 131 | 132 | --- t dump: 133 | a: Easy! 134 | b: 135 | c: 2 136 | d: [3, 4] 137 | 138 | 139 | --- m: 140 | map[a:Easy! b:map[c:2 d:[3 4]]] 141 | 142 | --- m dump: 143 | a: Easy! 144 | b: 145 | c: 2 146 | d: 147 | - 3 148 | - 4 149 | ``` 150 | 151 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/readerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | import ( 26 | "io" 27 | ) 28 | 29 | // Set the reader error and return 0. 30 | func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { 31 | parser.error = yaml_READER_ERROR 32 | parser.problem = problem 33 | parser.problem_offset = offset 34 | parser.problem_value = value 35 | return false 36 | } 37 | 38 | // Byte order marks. 39 | const ( 40 | bom_UTF8 = "\xef\xbb\xbf" 41 | bom_UTF16LE = "\xff\xfe" 42 | bom_UTF16BE = "\xfe\xff" 43 | ) 44 | 45 | // Determine the input stream encoding by checking the BOM symbol. If no BOM is 46 | // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. 47 | func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { 48 | // Ensure that we had enough bytes in the raw buffer. 49 | for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { 50 | if !yaml_parser_update_raw_buffer(parser) { 51 | return false 52 | } 53 | } 54 | 55 | // Determine the encoding. 56 | buf := parser.raw_buffer 57 | pos := parser.raw_buffer_pos 58 | avail := len(buf) - pos 59 | if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { 60 | parser.encoding = yaml_UTF16LE_ENCODING 61 | parser.raw_buffer_pos += 2 62 | parser.offset += 2 63 | } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { 64 | parser.encoding = yaml_UTF16BE_ENCODING 65 | parser.raw_buffer_pos += 2 66 | parser.offset += 2 67 | } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { 68 | parser.encoding = yaml_UTF8_ENCODING 69 | parser.raw_buffer_pos += 3 70 | parser.offset += 3 71 | } else { 72 | parser.encoding = yaml_UTF8_ENCODING 73 | } 74 | return true 75 | } 76 | 77 | // Update the raw buffer. 78 | func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { 79 | size_read := 0 80 | 81 | // Return if the raw buffer is full. 82 | if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { 83 | return true 84 | } 85 | 86 | // Return on EOF. 87 | if parser.eof { 88 | return true 89 | } 90 | 91 | // Move the remaining bytes in the raw buffer to the beginning. 92 | if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { 93 | copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) 94 | } 95 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] 96 | parser.raw_buffer_pos = 0 97 | 98 | // Call the read handler to fill the buffer. 99 | size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) 100 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] 101 | if err == io.EOF { 102 | parser.eof = true 103 | } else if err != nil { 104 | return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) 105 | } 106 | return true 107 | } 108 | 109 | // Ensure that the buffer contains at least `length` characters. 110 | // Return true on success, false on failure. 111 | // 112 | // The length is supposed to be significantly less that the buffer size. 113 | func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { 114 | if parser.read_handler == nil { 115 | panic("read handler must be set") 116 | } 117 | 118 | // [Go] This function was changed to guarantee the requested length size at EOF. 119 | // The fact we need to do this is pretty awful, but the description above implies 120 | // for that to be the case, and there are tests 121 | 122 | // If the EOF flag is set and the raw buffer is empty, do nothing. 123 | if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { 124 | // [Go] ACTUALLY! Read the documentation of this function above. 125 | // This is just broken. To return true, we need to have the 126 | // given length in the buffer. Not doing that means every single 127 | // check that calls this function to make sure the buffer has a 128 | // given length is Go) panicking; or C) accessing invalid memory. 129 | //return true 130 | } 131 | 132 | // Return if the buffer contains enough characters. 133 | if parser.unread >= length { 134 | return true 135 | } 136 | 137 | // Determine the input encoding if it is not known yet. 138 | if parser.encoding == yaml_ANY_ENCODING { 139 | if !yaml_parser_determine_encoding(parser) { 140 | return false 141 | } 142 | } 143 | 144 | // Move the unread characters to the beginning of the buffer. 145 | buffer_len := len(parser.buffer) 146 | if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { 147 | copy(parser.buffer, parser.buffer[parser.buffer_pos:]) 148 | buffer_len -= parser.buffer_pos 149 | parser.buffer_pos = 0 150 | } else if parser.buffer_pos == buffer_len { 151 | buffer_len = 0 152 | parser.buffer_pos = 0 153 | } 154 | 155 | // Open the whole buffer for writing, and cut it before returning. 156 | parser.buffer = parser.buffer[:cap(parser.buffer)] 157 | 158 | // Fill the buffer until it has enough characters. 159 | first := true 160 | for parser.unread < length { 161 | 162 | // Fill the raw buffer if necessary. 163 | if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { 164 | if !yaml_parser_update_raw_buffer(parser) { 165 | parser.buffer = parser.buffer[:buffer_len] 166 | return false 167 | } 168 | } 169 | first = false 170 | 171 | // Decode the raw buffer. 172 | inner: 173 | for parser.raw_buffer_pos != len(parser.raw_buffer) { 174 | var value rune 175 | var width int 176 | 177 | raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos 178 | 179 | // Decode the next character. 180 | switch parser.encoding { 181 | case yaml_UTF8_ENCODING: 182 | // Decode a UTF-8 character. Check RFC 3629 183 | // (http://www.ietf.org/rfc/rfc3629.txt) for more details. 184 | // 185 | // The following table (taken from the RFC) is used for 186 | // decoding. 187 | // 188 | // Char. number range | UTF-8 octet sequence 189 | // (hexadecimal) | (binary) 190 | // --------------------+------------------------------------ 191 | // 0000 0000-0000 007F | 0xxxxxxx 192 | // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 193 | // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 194 | // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 195 | // 196 | // Additionally, the characters in the range 0xD800-0xDFFF 197 | // are prohibited as they are reserved for use with UTF-16 198 | // surrogate pairs. 199 | 200 | // Determine the length of the UTF-8 sequence. 201 | octet := parser.raw_buffer[parser.raw_buffer_pos] 202 | switch { 203 | case octet&0x80 == 0x00: 204 | width = 1 205 | case octet&0xE0 == 0xC0: 206 | width = 2 207 | case octet&0xF0 == 0xE0: 208 | width = 3 209 | case octet&0xF8 == 0xF0: 210 | width = 4 211 | default: 212 | // The leading octet is invalid. 213 | return yaml_parser_set_reader_error(parser, 214 | "invalid leading UTF-8 octet", 215 | parser.offset, int(octet)) 216 | } 217 | 218 | // Check if the raw buffer contains an incomplete character. 219 | if width > raw_unread { 220 | if parser.eof { 221 | return yaml_parser_set_reader_error(parser, 222 | "incomplete UTF-8 octet sequence", 223 | parser.offset, -1) 224 | } 225 | break inner 226 | } 227 | 228 | // Decode the leading octet. 229 | switch { 230 | case octet&0x80 == 0x00: 231 | value = rune(octet & 0x7F) 232 | case octet&0xE0 == 0xC0: 233 | value = rune(octet & 0x1F) 234 | case octet&0xF0 == 0xE0: 235 | value = rune(octet & 0x0F) 236 | case octet&0xF8 == 0xF0: 237 | value = rune(octet & 0x07) 238 | default: 239 | value = 0 240 | } 241 | 242 | // Check and decode the trailing octets. 243 | for k := 1; k < width; k++ { 244 | octet = parser.raw_buffer[parser.raw_buffer_pos+k] 245 | 246 | // Check if the octet is valid. 247 | if (octet & 0xC0) != 0x80 { 248 | return yaml_parser_set_reader_error(parser, 249 | "invalid trailing UTF-8 octet", 250 | parser.offset+k, int(octet)) 251 | } 252 | 253 | // Decode the octet. 254 | value = (value << 6) + rune(octet&0x3F) 255 | } 256 | 257 | // Check the length of the sequence against the value. 258 | switch { 259 | case width == 1: 260 | case width == 2 && value >= 0x80: 261 | case width == 3 && value >= 0x800: 262 | case width == 4 && value >= 0x10000: 263 | default: 264 | return yaml_parser_set_reader_error(parser, 265 | "invalid length of a UTF-8 sequence", 266 | parser.offset, -1) 267 | } 268 | 269 | // Check the range of the value. 270 | if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { 271 | return yaml_parser_set_reader_error(parser, 272 | "invalid Unicode character", 273 | parser.offset, int(value)) 274 | } 275 | 276 | case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: 277 | var low, high int 278 | if parser.encoding == yaml_UTF16LE_ENCODING { 279 | low, high = 0, 1 280 | } else { 281 | low, high = 1, 0 282 | } 283 | 284 | // The UTF-16 encoding is not as simple as one might 285 | // naively think. Check RFC 2781 286 | // (http://www.ietf.org/rfc/rfc2781.txt). 287 | // 288 | // Normally, two subsequent bytes describe a Unicode 289 | // character. However a special technique (called a 290 | // surrogate pair) is used for specifying character 291 | // values larger than 0xFFFF. 292 | // 293 | // A surrogate pair consists of two pseudo-characters: 294 | // high surrogate area (0xD800-0xDBFF) 295 | // low surrogate area (0xDC00-0xDFFF) 296 | // 297 | // The following formulas are used for decoding 298 | // and encoding characters using surrogate pairs: 299 | // 300 | // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) 301 | // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) 302 | // W1 = 110110yyyyyyyyyy 303 | // W2 = 110111xxxxxxxxxx 304 | // 305 | // where U is the character value, W1 is the high surrogate 306 | // area, W2 is the low surrogate area. 307 | 308 | // Check for incomplete UTF-16 character. 309 | if raw_unread < 2 { 310 | if parser.eof { 311 | return yaml_parser_set_reader_error(parser, 312 | "incomplete UTF-16 character", 313 | parser.offset, -1) 314 | } 315 | break inner 316 | } 317 | 318 | // Get the character. 319 | value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + 320 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) 321 | 322 | // Check for unexpected low surrogate area. 323 | if value&0xFC00 == 0xDC00 { 324 | return yaml_parser_set_reader_error(parser, 325 | "unexpected low surrogate area", 326 | parser.offset, int(value)) 327 | } 328 | 329 | // Check for a high surrogate area. 330 | if value&0xFC00 == 0xD800 { 331 | width = 4 332 | 333 | // Check for incomplete surrogate pair. 334 | if raw_unread < 4 { 335 | if parser.eof { 336 | return yaml_parser_set_reader_error(parser, 337 | "incomplete UTF-16 surrogate pair", 338 | parser.offset, -1) 339 | } 340 | break inner 341 | } 342 | 343 | // Get the next character. 344 | value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + 345 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) 346 | 347 | // Check for a low surrogate area. 348 | if value2&0xFC00 != 0xDC00 { 349 | return yaml_parser_set_reader_error(parser, 350 | "expected low surrogate area", 351 | parser.offset+2, int(value2)) 352 | } 353 | 354 | // Generate the value of the surrogate pair. 355 | value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) 356 | } else { 357 | width = 2 358 | } 359 | 360 | default: 361 | panic("impossible") 362 | } 363 | 364 | // Check if the character is in the allowed range: 365 | // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) 366 | // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) 367 | // | [#x10000-#x10FFFF] (32 bit) 368 | switch { 369 | case value == 0x09: 370 | case value == 0x0A: 371 | case value == 0x0D: 372 | case value >= 0x20 && value <= 0x7E: 373 | case value == 0x85: 374 | case value >= 0xA0 && value <= 0xD7FF: 375 | case value >= 0xE000 && value <= 0xFFFD: 376 | case value >= 0x10000 && value <= 0x10FFFF: 377 | default: 378 | return yaml_parser_set_reader_error(parser, 379 | "control characters are not allowed", 380 | parser.offset, int(value)) 381 | } 382 | 383 | // Move the raw pointers. 384 | parser.raw_buffer_pos += width 385 | parser.offset += width 386 | 387 | // Finally put the character into the buffer. 388 | if value <= 0x7F { 389 | // 0000 0000-0000 007F . 0xxxxxxx 390 | parser.buffer[buffer_len+0] = byte(value) 391 | buffer_len += 1 392 | } else if value <= 0x7FF { 393 | // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx 394 | parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) 395 | parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) 396 | buffer_len += 2 397 | } else if value <= 0xFFFF { 398 | // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx 399 | parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) 400 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) 401 | parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) 402 | buffer_len += 3 403 | } else { 404 | // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 405 | parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) 406 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) 407 | parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) 408 | parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) 409 | buffer_len += 4 410 | } 411 | 412 | parser.unread++ 413 | } 414 | 415 | // On EOF, put NUL into the buffer and return. 416 | if parser.eof { 417 | parser.buffer[buffer_len] = 0 418 | buffer_len++ 419 | parser.unread++ 420 | break 421 | } 422 | } 423 | // [Go] Read the documentation of this function above. To return true, 424 | // we need to have the given length in the buffer. Not doing that means 425 | // every single check that calls this function to make sure the buffer 426 | // has a given length is Go) panicking; or C) accessing invalid memory. 427 | // This happens here due to the EOF above breaking early. 428 | for buffer_len < length { 429 | parser.buffer[buffer_len] = 0 430 | buffer_len++ 431 | } 432 | parser.buffer = parser.buffer[:buffer_len] 433 | return true 434 | } 435 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/resolve.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "encoding/base64" 20 | "math" 21 | "regexp" 22 | "strconv" 23 | "strings" 24 | "time" 25 | ) 26 | 27 | type resolveMapItem struct { 28 | value interface{} 29 | tag string 30 | } 31 | 32 | var resolveTable = make([]byte, 256) 33 | var resolveMap = make(map[string]resolveMapItem) 34 | 35 | func init() { 36 | t := resolveTable 37 | t[int('+')] = 'S' // Sign 38 | t[int('-')] = 'S' 39 | for _, c := range "0123456789" { 40 | t[int(c)] = 'D' // Digit 41 | } 42 | for _, c := range "yYnNtTfFoO~" { 43 | t[int(c)] = 'M' // In map 44 | } 45 | t[int('.')] = '.' // Float (potentially in map) 46 | 47 | var resolveMapList = []struct { 48 | v interface{} 49 | tag string 50 | l []string 51 | }{ 52 | {true, boolTag, []string{"true", "True", "TRUE"}}, 53 | {false, boolTag, []string{"false", "False", "FALSE"}}, 54 | {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, 55 | {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, 56 | {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, 57 | {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, 58 | {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, 59 | {"<<", mergeTag, []string{"<<"}}, 60 | } 61 | 62 | m := resolveMap 63 | for _, item := range resolveMapList { 64 | for _, s := range item.l { 65 | m[s] = resolveMapItem{item.v, item.tag} 66 | } 67 | } 68 | } 69 | 70 | const ( 71 | nullTag = "!!null" 72 | boolTag = "!!bool" 73 | strTag = "!!str" 74 | intTag = "!!int" 75 | floatTag = "!!float" 76 | timestampTag = "!!timestamp" 77 | seqTag = "!!seq" 78 | mapTag = "!!map" 79 | binaryTag = "!!binary" 80 | mergeTag = "!!merge" 81 | ) 82 | 83 | var longTags = make(map[string]string) 84 | var shortTags = make(map[string]string) 85 | 86 | func init() { 87 | for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { 88 | ltag := longTag(stag) 89 | longTags[stag] = ltag 90 | shortTags[ltag] = stag 91 | } 92 | } 93 | 94 | const longTagPrefix = "tag:yaml.org,2002:" 95 | 96 | func shortTag(tag string) string { 97 | if strings.HasPrefix(tag, longTagPrefix) { 98 | if stag, ok := shortTags[tag]; ok { 99 | return stag 100 | } 101 | return "!!" + tag[len(longTagPrefix):] 102 | } 103 | return tag 104 | } 105 | 106 | func longTag(tag string) string { 107 | if strings.HasPrefix(tag, "!!") { 108 | if ltag, ok := longTags[tag]; ok { 109 | return ltag 110 | } 111 | return longTagPrefix + tag[2:] 112 | } 113 | return tag 114 | } 115 | 116 | func resolvableTag(tag string) bool { 117 | switch tag { 118 | case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: 119 | return true 120 | } 121 | return false 122 | } 123 | 124 | var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) 125 | 126 | func resolve(tag string, in string) (rtag string, out interface{}) { 127 | tag = shortTag(tag) 128 | if !resolvableTag(tag) { 129 | return tag, in 130 | } 131 | 132 | defer func() { 133 | switch tag { 134 | case "", rtag, strTag, binaryTag: 135 | return 136 | case floatTag: 137 | if rtag == intTag { 138 | switch v := out.(type) { 139 | case int64: 140 | rtag = floatTag 141 | out = float64(v) 142 | return 143 | case int: 144 | rtag = floatTag 145 | out = float64(v) 146 | return 147 | } 148 | } 149 | } 150 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 151 | }() 152 | 153 | // Any data is accepted as a !!str or !!binary. 154 | // Otherwise, the prefix is enough of a hint about what it might be. 155 | hint := byte('N') 156 | if in != "" { 157 | hint = resolveTable[in[0]] 158 | } 159 | if hint != 0 && tag != strTag && tag != binaryTag { 160 | // Handle things we can lookup in a map. 161 | if item, ok := resolveMap[in]; ok { 162 | return item.tag, item.value 163 | } 164 | 165 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 166 | // are purposefully unsupported here. They're still quoted on 167 | // the way out for compatibility with other parser, though. 168 | 169 | switch hint { 170 | case 'M': 171 | // We've already checked the map above. 172 | 173 | case '.': 174 | // Not in the map, so maybe a normal float. 175 | floatv, err := strconv.ParseFloat(in, 64) 176 | if err == nil { 177 | return floatTag, floatv 178 | } 179 | 180 | case 'D', 'S': 181 | // Int, float, or timestamp. 182 | // Only try values as a timestamp if the value is unquoted or there's an explicit 183 | // !!timestamp tag. 184 | if tag == "" || tag == timestampTag { 185 | t, ok := parseTimestamp(in) 186 | if ok { 187 | return timestampTag, t 188 | } 189 | } 190 | 191 | plain := strings.Replace(in, "_", "", -1) 192 | intv, err := strconv.ParseInt(plain, 0, 64) 193 | if err == nil { 194 | if intv == int64(int(intv)) { 195 | return intTag, int(intv) 196 | } else { 197 | return intTag, intv 198 | } 199 | } 200 | uintv, err := strconv.ParseUint(plain, 0, 64) 201 | if err == nil { 202 | return intTag, uintv 203 | } 204 | if yamlStyleFloat.MatchString(plain) { 205 | floatv, err := strconv.ParseFloat(plain, 64) 206 | if err == nil { 207 | return floatTag, floatv 208 | } 209 | } 210 | if strings.HasPrefix(plain, "0b") { 211 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 212 | if err == nil { 213 | if intv == int64(int(intv)) { 214 | return intTag, int(intv) 215 | } else { 216 | return intTag, intv 217 | } 218 | } 219 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 220 | if err == nil { 221 | return intTag, uintv 222 | } 223 | } else if strings.HasPrefix(plain, "-0b") { 224 | intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) 225 | if err == nil { 226 | if true || intv == int64(int(intv)) { 227 | return intTag, int(intv) 228 | } else { 229 | return intTag, intv 230 | } 231 | } 232 | } 233 | // Octals as introduced in version 1.2 of the spec. 234 | // Octals from the 1.1 spec, spelled as 0777, are still 235 | // decoded by default in v3 as well for compatibility. 236 | // May be dropped in v4 depending on how usage evolves. 237 | if strings.HasPrefix(plain, "0o") { 238 | intv, err := strconv.ParseInt(plain[2:], 8, 64) 239 | if err == nil { 240 | if intv == int64(int(intv)) { 241 | return intTag, int(intv) 242 | } else { 243 | return intTag, intv 244 | } 245 | } 246 | uintv, err := strconv.ParseUint(plain[2:], 8, 64) 247 | if err == nil { 248 | return intTag, uintv 249 | } 250 | } else if strings.HasPrefix(plain, "-0o") { 251 | intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) 252 | if err == nil { 253 | if true || intv == int64(int(intv)) { 254 | return intTag, int(intv) 255 | } else { 256 | return intTag, intv 257 | } 258 | } 259 | } 260 | default: 261 | panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") 262 | } 263 | } 264 | return strTag, in 265 | } 266 | 267 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 268 | // as appropriate for the resulting length. 269 | func encodeBase64(s string) string { 270 | const lineLen = 70 271 | encLen := base64.StdEncoding.EncodedLen(len(s)) 272 | lines := encLen/lineLen + 1 273 | buf := make([]byte, encLen*2+lines) 274 | in := buf[0:encLen] 275 | out := buf[encLen:] 276 | base64.StdEncoding.Encode(in, []byte(s)) 277 | k := 0 278 | for i := 0; i < len(in); i += lineLen { 279 | j := i + lineLen 280 | if j > len(in) { 281 | j = len(in) 282 | } 283 | k += copy(out[k:], in[i:j]) 284 | if lines > 1 { 285 | out[k] = '\n' 286 | k++ 287 | } 288 | } 289 | return string(out[:k]) 290 | } 291 | 292 | // This is a subset of the formats allowed by the regular expression 293 | // defined at http://yaml.org/type/timestamp.html. 294 | var allowedTimestampFormats = []string{ 295 | "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. 296 | "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". 297 | "2006-1-2 15:4:5.999999999", // space separated with no time zone 298 | "2006-1-2", // date only 299 | // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" 300 | // from the set of examples. 301 | } 302 | 303 | // parseTimestamp parses s as a timestamp string and 304 | // returns the timestamp and reports whether it succeeded. 305 | // Timestamp formats are defined at http://yaml.org/type/timestamp.html 306 | func parseTimestamp(s string) (time.Time, bool) { 307 | // TODO write code to check all the formats supported by 308 | // http://yaml.org/type/timestamp.html instead of using time.Parse. 309 | 310 | // Quick check: all date formats start with YYYY-. 311 | i := 0 312 | for ; i < len(s); i++ { 313 | if c := s[i]; c < '0' || c > '9' { 314 | break 315 | } 316 | } 317 | if i != 4 || i == len(s) || s[i] != '-' { 318 | return time.Time{}, false 319 | } 320 | for _, format := range allowedTimestampFormats { 321 | if t, err := time.Parse(format, s); err == nil { 322 | return t, true 323 | } 324 | } 325 | return time.Time{}, false 326 | } 327 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/sorter.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "reflect" 20 | "unicode" 21 | ) 22 | 23 | type keyList []reflect.Value 24 | 25 | func (l keyList) Len() int { return len(l) } 26 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 27 | func (l keyList) Less(i, j int) bool { 28 | a := l[i] 29 | b := l[j] 30 | ak := a.Kind() 31 | bk := b.Kind() 32 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 33 | a = a.Elem() 34 | ak = a.Kind() 35 | } 36 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 37 | b = b.Elem() 38 | bk = b.Kind() 39 | } 40 | af, aok := keyFloat(a) 41 | bf, bok := keyFloat(b) 42 | if aok && bok { 43 | if af != bf { 44 | return af < bf 45 | } 46 | if ak != bk { 47 | return ak < bk 48 | } 49 | return numLess(a, b) 50 | } 51 | if ak != reflect.String || bk != reflect.String { 52 | return ak < bk 53 | } 54 | ar, br := []rune(a.String()), []rune(b.String()) 55 | digits := false 56 | for i := 0; i < len(ar) && i < len(br); i++ { 57 | if ar[i] == br[i] { 58 | digits = unicode.IsDigit(ar[i]) 59 | continue 60 | } 61 | al := unicode.IsLetter(ar[i]) 62 | bl := unicode.IsLetter(br[i]) 63 | if al && bl { 64 | return ar[i] < br[i] 65 | } 66 | if al || bl { 67 | if digits { 68 | return al 69 | } else { 70 | return bl 71 | } 72 | } 73 | var ai, bi int 74 | var an, bn int64 75 | if ar[i] == '0' || br[i] == '0' { 76 | for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 77 | if ar[j] != '0' { 78 | an = 1 79 | bn = 1 80 | break 81 | } 82 | } 83 | } 84 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 85 | an = an*10 + int64(ar[ai]-'0') 86 | } 87 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 88 | bn = bn*10 + int64(br[bi]-'0') 89 | } 90 | if an != bn { 91 | return an < bn 92 | } 93 | if ai != bi { 94 | return ai < bi 95 | } 96 | return ar[i] < br[i] 97 | } 98 | return len(ar) < len(br) 99 | } 100 | 101 | // keyFloat returns a float value for v if it is a number/bool 102 | // and whether it is a number/bool or not. 103 | func keyFloat(v reflect.Value) (f float64, ok bool) { 104 | switch v.Kind() { 105 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 106 | return float64(v.Int()), true 107 | case reflect.Float32, reflect.Float64: 108 | return v.Float(), true 109 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 110 | return float64(v.Uint()), true 111 | case reflect.Bool: 112 | if v.Bool() { 113 | return 1, true 114 | } 115 | return 0, true 116 | } 117 | return 0, false 118 | } 119 | 120 | // numLess returns whether a < b. 121 | // a and b must necessarily have the same kind. 122 | func numLess(a, b reflect.Value) bool { 123 | switch a.Kind() { 124 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 125 | return a.Int() < b.Int() 126 | case reflect.Float32, reflect.Float64: 127 | return a.Float() < b.Float() 128 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 129 | return a.Uint() < b.Uint() 130 | case reflect.Bool: 131 | return !a.Bool() && b.Bool() 132 | } 133 | panic("not a number") 134 | } 135 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/writerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | // Set the writer error and return false. 26 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 27 | emitter.error = yaml_WRITER_ERROR 28 | emitter.problem = problem 29 | return false 30 | } 31 | 32 | // Flush the output buffer. 33 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 34 | if emitter.write_handler == nil { 35 | panic("write handler not set") 36 | } 37 | 38 | // Check if the buffer is empty. 39 | if emitter.buffer_pos == 0 { 40 | return true 41 | } 42 | 43 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 44 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 45 | } 46 | emitter.buffer_pos = 0 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | const ( 26 | // The size of the input raw buffer. 27 | input_raw_buffer_size = 512 28 | 29 | // The size of the input buffer. 30 | // It should be possible to decode the whole raw buffer. 31 | input_buffer_size = input_raw_buffer_size * 3 32 | 33 | // The size of the output buffer. 34 | output_buffer_size = 128 35 | 36 | // The size of the output raw buffer. 37 | // It should be possible to encode the whole output buffer. 38 | output_raw_buffer_size = (output_buffer_size*2 + 2) 39 | 40 | // The size of other stacks and queues. 41 | initial_stack_size = 16 42 | initial_queue_size = 16 43 | initial_string_size = 16 44 | ) 45 | 46 | // Check if the character at the specified position is an alphabetical 47 | // character, a digit, '_', or '-'. 48 | func is_alpha(b []byte, i int) bool { 49 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 50 | } 51 | 52 | // Check if the character at the specified position is a digit. 53 | func is_digit(b []byte, i int) bool { 54 | return b[i] >= '0' && b[i] <= '9' 55 | } 56 | 57 | // Get the value of a digit. 58 | func as_digit(b []byte, i int) int { 59 | return int(b[i]) - '0' 60 | } 61 | 62 | // Check if the character at the specified position is a hex-digit. 63 | func is_hex(b []byte, i int) bool { 64 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 65 | } 66 | 67 | // Get the value of a hex-digit. 68 | func as_hex(b []byte, i int) int { 69 | bi := b[i] 70 | if bi >= 'A' && bi <= 'F' { 71 | return int(bi) - 'A' + 10 72 | } 73 | if bi >= 'a' && bi <= 'f' { 74 | return int(bi) - 'a' + 10 75 | } 76 | return int(bi) - '0' 77 | } 78 | 79 | // Check if the character is ASCII. 80 | func is_ascii(b []byte, i int) bool { 81 | return b[i] <= 0x7F 82 | } 83 | 84 | // Check if the character at the start of the buffer can be printed unescaped. 85 | func is_printable(b []byte, i int) bool { 86 | return ((b[i] == 0x0A) || // . == #x0A 87 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 88 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 89 | (b[i] > 0xC2 && b[i] < 0xED) || 90 | (b[i] == 0xED && b[i+1] < 0xA0) || 91 | (b[i] == 0xEE) || 92 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 93 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 94 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 95 | } 96 | 97 | // Check if the character at the specified position is NUL. 98 | func is_z(b []byte, i int) bool { 99 | return b[i] == 0x00 100 | } 101 | 102 | // Check if the beginning of the buffer is a BOM. 103 | func is_bom(b []byte, i int) bool { 104 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 105 | } 106 | 107 | // Check if the character at the specified position is space. 108 | func is_space(b []byte, i int) bool { 109 | return b[i] == ' ' 110 | } 111 | 112 | // Check if the character at the specified position is tab. 113 | func is_tab(b []byte, i int) bool { 114 | return b[i] == '\t' 115 | } 116 | 117 | // Check if the character at the specified position is blank (space or tab). 118 | func is_blank(b []byte, i int) bool { 119 | //return is_space(b, i) || is_tab(b, i) 120 | return b[i] == ' ' || b[i] == '\t' 121 | } 122 | 123 | // Check if the character at the specified position is a line break. 124 | func is_break(b []byte, i int) bool { 125 | return (b[i] == '\r' || // CR (#xD) 126 | b[i] == '\n' || // LF (#xA) 127 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 128 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 129 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 130 | } 131 | 132 | func is_crlf(b []byte, i int) bool { 133 | return b[i] == '\r' && b[i+1] == '\n' 134 | } 135 | 136 | // Check if the character is a line break or NUL. 137 | func is_breakz(b []byte, i int) bool { 138 | //return is_break(b, i) || is_z(b, i) 139 | return ( 140 | // is_break: 141 | b[i] == '\r' || // CR (#xD) 142 | b[i] == '\n' || // LF (#xA) 143 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 144 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 145 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 146 | // is_z: 147 | b[i] == 0) 148 | } 149 | 150 | // Check if the character is a line break, space, or NUL. 151 | func is_spacez(b []byte, i int) bool { 152 | //return is_space(b, i) || is_breakz(b, i) 153 | return ( 154 | // is_space: 155 | b[i] == ' ' || 156 | // is_breakz: 157 | b[i] == '\r' || // CR (#xD) 158 | b[i] == '\n' || // LF (#xA) 159 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 160 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 161 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 162 | b[i] == 0) 163 | } 164 | 165 | // Check if the character is a line break, space, tab, or NUL. 166 | func is_blankz(b []byte, i int) bool { 167 | //return is_blank(b, i) || is_breakz(b, i) 168 | return ( 169 | // is_blank: 170 | b[i] == ' ' || b[i] == '\t' || 171 | // is_breakz: 172 | b[i] == '\r' || // CR (#xD) 173 | b[i] == '\n' || // LF (#xA) 174 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 175 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 176 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 177 | b[i] == 0) 178 | } 179 | 180 | // Determine the width of the character. 181 | func width(b byte) int { 182 | // Don't replace these by a switch without first 183 | // confirming that it is being inlined. 184 | if b&0x80 == 0x00 { 185 | return 1 186 | } 187 | if b&0xE0 == 0xC0 { 188 | return 2 189 | } 190 | if b&0xF0 == 0xE0 { 191 | return 3 192 | } 193 | if b&0xF8 == 0xF0 { 194 | return 4 195 | } 196 | return 0 197 | 198 | } 199 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/davecgh/go-spew v1.1.1 2 | ## explicit 3 | github.com/davecgh/go-spew/spew 4 | # github.com/pmezard/go-difflib v1.0.0 5 | ## explicit 6 | github.com/pmezard/go-difflib/difflib 7 | # github.com/stretchr/testify v1.10.0 8 | ## explicit; go 1.17 9 | github.com/stretchr/testify/assert 10 | github.com/stretchr/testify/assert/yaml 11 | github.com/stretchr/testify/require 12 | # gopkg.in/yaml.v3 v3.0.1 13 | ## explicit 14 | gopkg.in/yaml.v3 15 | --------------------------------------------------------------------------------