14 Commits

Author SHA1 Message Date
Joel Kamp
9674825e8f Merge pull request #7 from docker/feat-add-policy-oci-attestations
feat: add policy, oci, attestation
2024-04-22 14:19:54 -05:00
mrjoelkamp
f8f40807bc feat: pr comments 2024-04-22 14:16:45 -05:00
mrjoelkamp
1b2f80d4c5 refactor: export oci 2024-04-22 12:51:10 -05:00
mrjoelkamp
bf2dff9f66 revert: add exported constants 2024-04-22 12:50:52 -05:00
mrjoelkamp
1813ab8a7a feat: add exported constants 2024-04-22 12:47:14 -05:00
mrjoelkamp
a3422b5331 feat: add policy, oci, attestation 2024-04-22 12:38:56 -05:00
dependabot[bot]
20f83f6189 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#6) 2024-04-22 13:18:13 +00:00
Joel Kamp
047025f9a8 Merge pull request #5 from docker/fix-move-mock-tl
refactor: move GetMockTL to tlog/mock.go
2024-04-19 11:26:53 -05:00
mrjoelkamp
345a71b4df fix: move GetMockTL to tlog/mock.go 2024-04-19 10:24:25 -05:00
Joel Kamp
40067cd9ae Merge pull request #4 from docker/feat-add-tlog-pubkey-test
feat: add tlog and signerverifier
2024-04-19 10:13:57 -05:00
mrjoelkamp
c81a228bf6 feat: add tlog and signerverifier 2024-04-19 09:23:46 -05:00
Joel Kamp
cfce71f5ba Merge pull request #2 from docker/feat-add-CODEOWNERS
feat: add CODEOWNERS
2024-04-17 12:54:13 -05:00
Joel Kamp
b397754fd9 Merge branch 'main' into feat-add-CODEOWNERS 2024-04-16 10:20:28 -05:00
mrjoelkamp
1c49c63606 feat: add CODEOWNERS 2024-04-16 09:38:10 -05:00
87 changed files with 3226 additions and 107 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @docker/supply-chain-security

142
go.mod
View File

@@ -3,80 +3,172 @@ module github.com/docker/attest
go 1.22.1
require (
github.com/aws/aws-sdk-go-v2/config v1.27.11
github.com/containerd/containerd v1.7.14
github.com/distribution/reference v0.5.0
github.com/go-openapi/runtime v0.28.0
github.com/go-openapi/strfmt v0.23.0
github.com/google/go-containerregistry v0.19.1
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/in-toto/in-toto-golang v0.9.0
github.com/open-policy-agent/opa v0.63.0
github.com/opencontainers/image-spec v1.1.0
github.com/package-url/packageurl-go v0.1.2
github.com/pkg/errors v0.9.1
github.com/secure-systems-lab/go-securesystemslib v0.8.0
github.com/sigstore/cosign/v2 v2.2.4
github.com/sigstore/rekor v1.3.6
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.30.0
github.com/testcontainers/testcontainers-go/modules/registry v0.30.0
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240402164131-b2e024ad4752
gopkg.in/yaml.v3 v3.0.1
sigs.k8s.io/yaml v1.4.0
)
replace github.com/theupdateframework/go-tuf/v2 => github.com/mrjoelkamp/go-tuf/v2 v2.0.1 // for https://github.com/theupdateframework/go-tuf/pull/632
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/containerd/containerd v1.7.12 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v24.0.0+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20230907030200-6d76a0f91e1e // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/jellydator/ttlcache/v3 v3.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.51.1 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sigstore/sigstore v1.8.3 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/theupdateframework/go-tuf v0.7.0 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.3 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
)

664
go.sum
View File

@@ -1,51 +1,246 @@
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU=
cloud.google.com/go/compute v1.25.0/go.mod h1:GR7F0ZPZH8EhChlMo9FkLd7eUTwEymjqQagxzilIxIE=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
cloud.google.com/go/kms v1.15.8 h1:szIeDCowID8th2i8XE4uRev5PMxQFqW+JjwYxL9h6xs=
cloud.google.com/go/kms v1.15.8/go.mod h1:WoUHcDjD9pluCg7pNds131awnH429QGvRM3N/4MyoVs=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24=
cuelang.org/go v0.8.1 h1:VFYsxIFSPY5KgSaH1jQ2GxHOrbu6Ga3kEI70yCZwnOg=
cuelang.org/go v0.8.1/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg=
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 h1:DRiANoJTiW6obBQe3SqZizkuV1PEgfiiGivmVocDy64=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0/go.mod h1:qLIye2hwb/ZouqhpSD9Zn3SJipvpEnz1Ywl3VUk9Y0s=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8=
github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E=
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/cr-20160607 v1.0.1 h1:WEnP1iPFKJU74ryUKh/YDPHoxMZawqlPajOymyNAkts=
github.com/alibabacloud-go/cr-20160607 v1.0.1/go.mod h1:QHeKZtZ3F3FOE+/uIXCBAp8POwnUYekpLwr1dtQa5r0=
github.com/alibabacloud-go/cr-20181201 v1.0.10 h1:B60f6S1imsgn2fgC6X6FrVNrONDrbCT0NwYhsJ0C9/c=
github.com/alibabacloud-go/cr-20181201 v1.0.10/go.mod h1:VN9orB/w5G20FjytoSpZROqu9ZqxwycASmGqYUJSoDc=
github.com/alibabacloud-go/darabonba-openapi v0.2.1 h1:WyzxxKvhdVDlwpAMOHgAiCJ+NXa6g5ZWPFEzaK/ewwY=
github.com/alibabacloud-go/darabonba-openapi v0.2.1/go.mod h1:zXOqLbpIqq543oioL9IuuZYOQgHQ5B8/n5OPrnko8aY=
github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8=
github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/tea v1.2.1 h1:rFF1LnrAdhaiPmKwH5xwYOKlMh66CqRwPUTzIK74ask=
github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA=
github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.51.6 h1:Ld36dn9r7P9IjU8WZSaswQ8Y/XUCRpewim5980DwYiU=
github.com/aws/aws-sdk-go v1.51.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA=
github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE=
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs=
github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2 h1:y6LX9GUoEA3mO0qpFl1ZQHj1rFyPWVphlzebiSt2tKE=
github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2/go.mod h1:Q0LcmaN/Qr8+4aSBrdrXXePqoX0eOuYpJLbYpilmWnA=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 h1:PpbXaecV3sLAS6rjQiaKw4/jyq3Z8gNzmoJupHAoBp0=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2/go.mod h1:fUHpGXr4DrXkEDpGAjClPsviWf+Bszeb0daKE0blxv8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU=
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8/go.mod h1:2JF49jcDOrLStIXN/j/K1EKRq8a8R2qRnlZA6/o/c7c=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/buildkite/agent/v3 v3.62.0 h1:yvzSjI8Lgifw883I8m9u8/L/Thxt4cLFd5aWPn3gg70=
github.com/buildkite/agent/v3 v3.62.0/go.mod h1:jN6SokGXrVNNIpI0BGQ+j5aWeI3gin8F+3zwA5Q6gqM=
github.com/buildkite/go-pipeline v0.3.2 h1:SW4EaXNwfjow7xDRPGgX0Rcx+dPj5C1kV9LKCLjWGtM=
github.com/buildkite/go-pipeline v0.3.2/go.mod h1:iY5jzs3Afc8yHg6KDUcu3EJVkfaUkd9x/v/OH98qyUA=
github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 h1:k6UDF1uPYOs0iy1HPeotNa155qXRWrzKnqAaGXHLZCE=
github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251/go.mod h1:gbPR1gPu9dB96mucYIR7T3B7p/78hRVSOuzIWLHK2Y4=
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0=
github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk=
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4=
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA=
github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU=
github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc=
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I=
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM=
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/proto v1.12.1 h1:6n/Z2pZAnBwuhU66Gs8160B8rrrYKo7h2F2sCOnNceE=
github.com/emicklei/proto v1.12.1/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -53,13 +248,67 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg=
github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/certificate-transparency-go v1.1.8 h1:LGYKkgZF7satzgTak9R4yzfJXEeYVAjV6/EAEJOf1to=
github.com/google/certificate-transparency-go v1.1.8/go.mod h1:bV/o8r0TBKRf1X//iiiSgWrvII4d7/8OiA+3vG26gI8=
github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM=
github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -67,30 +316,107 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY=
github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg=
github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w=
github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM=
github.com/google/trillian v1.6.0 h1:jMBeDBIkINFvS2n6oV5maDqfRlxREAc6CW9QYWQ0qT4=
github.com/google/trillian v1.6.0/go.mod h1:Yu3nIMITzNhhMJEHjAtp6xKiu+H/iHu2Oq5FjV2mCWI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE=
github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/letsencrypt/boulder v0.0.0-20230907030200-6d76a0f91e1e h1:RLTpX495BXToqxpM90Ws4hXEo4Wfh81jr9DX1n/4WOo=
github.com/letsencrypt/boulder v0.0.0-20230907030200-6d76a0f91e1e/go.mod h1:EAuqr9VFWxBi9nD5jc/EA2MT1RFty9288TF6zdtYoCU=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 h1:WGrKdjHtWC67RX96eTkYD2f53NDHhrq/7robWTAfk4s=
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491/go.mod h1:o158RFmdEbYyIZmXAbrvmJWesbyxlLKee6X64VPVuOc=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
@@ -99,147 +425,316 @@ github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mozillazg/docker-credential-acr-helper v0.3.0 h1:DVWFZ3/O8BP6Ue3iS/Olw+G07u1hCq1EOVCDZZjCIBI=
github.com/mozillazg/docker-credential-acr-helper v0.3.0/go.mod h1:cZlu3tof523ujmLuiNUb6JsjtHcNA70u1jitrrdnuyA=
github.com/mrjoelkamp/go-tuf/v2 v2.0.1 h1:nDJGPlrU05sirPlA16M1XJiGDqM0zMwguA4cVgCJ9YY=
github.com/mrjoelkamp/go-tuf/v2 v2.0.1/go.mod h1:LJo5jrV0LYV0jVSbCjPem6+0zrkPz8FnimzIECzsFDY=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE=
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM=
github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/open-policy-agent/opa v0.63.0 h1:ztNNste1v8kH0/vJMJNquE45lRvqwrM5mY9Ctr9xIXw=
github.com/open-policy-agent/opa v0.63.0/go.mod h1:9VQPqEfoB2N//AToTxzZ1pVTVPUoF2Mhd64szzjWPpU=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4=
github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw=
github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf h1:014O62zIzQwvoD7Ekj3ePDF5bv9Xxy0w6AZk0qYbjUk=
github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A=
github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=
github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4=
github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k=
github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA=
github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sigstore/cosign/v2 v2.2.4 h1:iY4vtEacmu2hkNj1Fh+8EBqBwKs2DHM27/lbNWDFJro=
github.com/sigstore/cosign/v2 v2.2.4/go.mod h1:JZlRD2uaEjVAvZ1XJ3QkkZJhTqSDVtLaet+C/TMR81Y=
github.com/sigstore/fulcio v1.4.5 h1:WWNnrOknD0DbruuZWCbN+86WRROpEl3Xts+WT2Ek1yc=
github.com/sigstore/fulcio v1.4.5/go.mod h1:oz3Qwlma8dWcSS/IENR/6SjbW4ipN0cxpRVfgdsjMU8=
github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8=
github.com/sigstore/rekor v1.3.6/go.mod h1:JDTSNNMdQ/PxdsS49DJkJ+pRJCO/83nbR5p3aZQteXc=
github.com/sigstore/sigstore v1.8.3 h1:G7LVXqL+ekgYtYdksBks9B38dPoIsbscjQJX/MGWkA4=
github.com/sigstore/sigstore v1.8.3/go.mod h1:mqbTEariiGA94cn6G3xnDiV6BD8eSLdL/eA7bvJ0fVs=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3 h1:LTfPadUAo+PDRUbbdqbeSl2OuoFQwUFTnJ4stu+nwWw=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3/go.mod h1:QV/Lxlxm0POyhfyBtIbTWxNeF18clMlkkyL9mu45y18=
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3 h1:xgbPRCr2npmmsuVVteJqi/ERw9+I13Wou7kq0Yk4D8g=
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3/go.mod h1:G4+I83FILPX6MtnoaUdmv/bRGEVtR3JdLeJa/kXdk/0=
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3 h1:vDl2fqPT0h3D/k6NZPlqnKFd1tz3335wm39qjvpZNJc=
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3/go.mod h1:9uOJXbXEXj+M6QjMKH5PaL5WDMu43rHfbIMgXzA8eKI=
github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.3 h1:h9G8j+Ds21zqqulDbA/R/ft64oQQIyp8S7wJYABYSlg=
github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.3/go.mod h1:zgCeHOuqF6k7A7TTEvftcA9V3FRzB7mrPtHOhXAQBnc=
github.com/sigstore/timestamp-authority v1.2.2 h1:X4qyutnCQqJ0apMewFyx+3t7Tws00JQ/JonBiu3QvLE=
github.com/sigstore/timestamp-authority v1.2.2/go.mod h1:nEah4Eq4wpliDjlY342rXclGSO7Kb9hoRrl9tqLW13A=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spiffe/go-spiffe/v2 v2.2.0 h1:9Vf06UsvsDbLYK/zJ4sYsIsHmMFknUD+feA7IYoWMQY=
github.com/spiffe/go-spiffe/v2 v2.2.0/go.mod h1:Urzb779b3+IwDJD2ZbN8fVl3Aa8G4N/PiUe6iXC0XxU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/testcontainers/testcontainers-go v0.30.0 h1:jmn/XS22q4YRrcMwWg0pAwlClzs/abopbsBzrepyc4E=
github.com/testcontainers/testcontainers-go v0.30.0/go.mod h1:K+kHNGiM5zjklKjgTtcrEetF3uhWbMUyqAQoyoh8Pf0=
github.com/testcontainers/testcontainers-go/modules/registry v0.30.0 h1:/GYaNnQ09Gvwv3GvhWYbzL2gQiqwzlqDyQZ175uVPC4=
github.com/testcontainers/testcontainers-go/modules/registry v0.30.0/go.mod h1:bu2AS7kGxJQgZ16qbb5SHKSuEVrriENPIpKugl0aCHA=
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=
github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4=
github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.step.sm/crypto v0.44.2 h1:t3p3uQ7raP2jp2ha9P6xkQF85TJZh+87xmjSLaib+jk=
go.step.sm/crypto v0.44.2/go.mod h1:x1439EnFhadzhkuaGX7sz03LEMQ+jV4gRamf5LCZJQQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4=
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.172.0 h1:/1OcMZGPmW1rX2LCu2CmGUD1KXK1+pfzxotxyRUCCdk=
google.golang.org/api v0.172.0/go.mod h1:+fJZq6QXWfa9pXhnIzsjx4yI22d4aI9ZpLb58gvXjis=
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s=
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U=
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E=
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
@@ -247,11 +742,44 @@ google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=
gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/release-utils v0.7.7 h1:JKDOvhCk6zW8ipEOkpTGDH/mW3TI+XqtPp16aaQ79FU=
sigs.k8s.io/release-utils v0.7.7/go.mod h1:iU7DGVNi3umZJ8q6aHyUFzsDUIaYwNnNKGHo3YE5E3s=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=

View File

@@ -1,8 +1,24 @@
package test
import (
"context"
"os"
"testing"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/policy"
"github.com/docker/attest/pkg/signerverifier"
"github.com/docker/attest/pkg/tlog"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
const (
USE_MOCK_TL = true
USE_MOCK_KMS = true
USE_MOCK_POLICY = true
AwsRegion = "us-east-1"
AwsKmsKeyArn = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
)
func CreateTempDir(t *testing.T, dir, pattern string) string {
@@ -20,3 +36,51 @@ func CreateTempDir(t *testing.T, dir, pattern string) string {
})
return tempDir
}
func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
var tl tlog.TL
if USE_MOCK_TL {
tl = tlog.GetMockTL()
} else {
tl = &tlog.RekorTL{}
}
ctx := tlog.WithTL(context.Background(), tl)
var policyEvaluator policy.PolicyEvaluator
if USE_MOCK_POLICY {
policyEvaluator = GetMockPolicy()
} else {
policyEvaluator = policy.NewRegoEvaluator(true)
}
ctx = policy.WithPolicyEvaluator(ctx, policyEvaluator)
var signer dsse.SignerVerifier
var err error
if USE_MOCK_KMS {
signer, err = GetMockSigner(ctx)
if err != nil {
t.Fatal(err)
}
} else {
signer, err = signerverifier.GetAWSSigner(ctx, AwsKmsKeyArn, AwsRegion)
if err != nil {
t.Fatal(err)
}
}
return ctx, signer
}
func GetMockSigner(ctx context.Context) (dsse.SignerVerifier, error) {
return signerverifier.GenKeyPair()
}
func GetMockPolicy() policy.PolicyEvaluator {
return &policy.MockPolicyEvaluator{
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, policy []*policy.PolicyFile, input *policy.PolicyInput) error {
return nil
},
}
}

View File

@@ -11,3 +11,8 @@ func HexHashBytes(input []byte) string {
hashSum := s256.Sum(nil)
return hex.EncodeToString(hashSum)
}
func S256(data []byte) []byte {
h := sha256.Sum256(data)
return h[:]
}

10
internal/util/strings.go Normal file
View File

@@ -0,0 +1,10 @@
package util
func StringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}

62
pkg/attestation/sign.go Normal file
View File

@@ -0,0 +1,62 @@
package attestation
import (
"context"
"fmt"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/tlog"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// SignDSSE signs a payload with a given signer and uploads the signature to the transparency log
func SignDSSE(ctx context.Context, payload []byte, payloadType string, signer dsse.SignerVerifier) (*Envelope, error) {
t := tlog.GetTL(ctx)
env := new(Envelope)
env.Payload = base64Encoding.EncodeToString(payload)
env.PayloadType = payloadType
encPayload := dsse.PAE(payloadType, payload)
// statement message digest
hash := util.S256(encPayload)
// sign message digest
sig, err := signer.Sign(ctx, hash)
if err != nil {
return nil, fmt.Errorf("error signing attestation: %w", err)
}
// get Key ID from signer
keyId, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
// upload to TL
entry, err := t.UploadLogEntry(ctx, keyId, encPayload, sig, signer)
if err != nil {
return nil, fmt.Errorf("error uploading TL entry: %w", err)
}
entryObj, err := t.UnmarshalEntry(entry)
if err != nil {
return nil, fmt.Errorf("error unmarshaling tl entry: %w", err)
}
// add signature w/ tl extension to dsse envelope
env.Signatures = append(env.Signatures, Signature{
KeyID: keyId,
Sig: base64Encoding.EncodeToString(sig),
Extension: Extension{
Kind: DockerDsseExtKind,
Ext: DockerDsseExtension{
Tl: DockerTlExtension{
Kind: RekorTlExtKind,
Data: entryObj, // transparency log entry metadata
},
},
},
})
return env, nil
}

View File

@@ -0,0 +1,147 @@
package attestation_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/signerverifier"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/stretchr/testify/assert"
)
func TestSignVerifyAttestation(t *testing.T) {
ctx, signer := test.Setup(t)
stmt := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
Type: intoto.StatementInTotoV01,
PredicateType: intoto.PredicateSPDX,
},
Predicate: "test",
}
payload, err := json.Marshal(stmt)
assert.NoError(t, err)
env, err := attestation.SignDSSE(ctx, payload, intoto.PayloadType, signer)
assert.NoError(t, err)
// marshal envelope to json to test for bugs when marshaling envelope data
serializedEnv, err := json.Marshal(env)
assert.NoError(t, err)
deserializedEnv := new(attestation.Envelope)
err = json.Unmarshal(serializedEnv, deserializedEnv)
assert.NoError(t, err)
// signer.Public() calls AWS API when using AWS signer, use attestation.GetPublicVerificationKey() to get key from TUF repo
// signer.Public() used here for test purposes
ecPub, ok := signer.Public().(*ecdsa.PublicKey)
assert.True(t, ok)
pem, err := signerverifier.ToPEM(ecPub)
assert.NoError(t, err)
keyId, err := signerverifier.KeyID(ecPub)
assert.NoError(t, err)
badKeyPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.NoError(t, err)
badKey := &badKeyPriv.PublicKey
badPEM, err := signerverifier.ToPEM(badKey)
assert.NoError(t, err)
testCases := []struct {
name string
keyId string
pem []byte
distrust bool
from time.Time
to *time.Time
status string
expectedError string
}{
{
name: "all OK",
keyId: keyId,
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: "",
},
{
name: "key not found",
keyId: "someotherkey",
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: fmt.Sprintf("key not found: %s", keyId),
},
{
name: "key distrusted",
keyId: keyId,
pem: pem,
distrust: true,
from: time.Time{},
to: nil,
status: "active",
expectedError: "distrusted",
},
{
name: "key not yet valid",
keyId: keyId,
pem: pem,
distrust: false,
from: time.Now().Add(time.Hour),
to: nil,
status: "active",
expectedError: "not yet valid",
},
{
name: "key already revoked",
keyId: keyId,
pem: pem,
distrust: false,
from: time.Time{},
to: new(time.Time),
status: "revoked",
expectedError: "already revoked",
},
{
name: "bad key",
keyId: keyId,
pem: badPEM,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: "signature is not valid",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keyMeta := attestation.KeyMetadata{
ID: tc.keyId,
PEM: string(tc.pem),
Distrust: tc.distrust,
From: tc.from,
To: tc.to,
Status: tc.status,
}
_, err = attestation.VerifyDSSE(ctx, deserializedEnv, attestation.KeysMap{tc.keyId: keyMeta})
if tc.expectedError != "" {
assert.Contains(t, err.Error(), tc.expectedError)
} else {
assert.NoError(t, err)
}
})
}
}

35
pkg/attestation/types.go Normal file
View File

@@ -0,0 +1,35 @@
package attestation
import "encoding/base64"
const (
DockerDsseExtKind = "application/vnd.docker.attestation-verification.v1+json"
RekorTlExtKind = "Rekor"
)
var base64Encoding = base64.StdEncoding.Strict()
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
}
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Extension Extension `json:"extension"`
}
type Extension struct {
Kind string `json:"kind"`
Ext DockerDsseExtension `json:"ext"`
}
type DockerDsseExtension struct {
Tl DockerTlExtension `json:"tl"`
}
type DockerTlExtension struct {
Kind string `json:"kind"`
Data any `json:"data"`
}

134
pkg/attestation/verify.go Normal file
View File

@@ -0,0 +1,134 @@
package attestation
import (
"context"
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"time"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/signerverifier"
"github.com/docker/attest/pkg/tlog"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type KeyMetadata struct {
ID string `json:"id"`
PEM string `json:"key"`
From time.Time `json:"from"`
To *time.Time `json:"to"`
Status string `json:"status"`
SigningFormat string `json:"signing-format"`
Distrust bool `json:"distrust,omitempty"`
}
type Keys []KeyMetadata
type KeysMap map[string]KeyMetadata
func VerifyDSSE(ctx context.Context, env *Envelope, keys KeysMap) ([]byte, error) {
// enforce payload type
if !ValidPayloadType(env.PayloadType) {
return nil, fmt.Errorf("unsupported payload type %s", env.PayloadType)
}
if len(env.Signatures) == 0 {
return nil, fmt.Errorf("no signatures found")
}
payload, err := base64Encoding.DecodeString(env.Payload)
if err != nil {
return nil, fmt.Errorf("error failed to decode payload: %w", err)
}
encPayload := dsse.PAE(env.PayloadType, payload)
// verify signatures and transparency log entry
for _, sig := range env.Signatures {
err := verifySignature(ctx, sig, encPayload, keys)
if err != nil {
return nil, err
}
}
return payload, nil
}
func verifySignature(ctx context.Context, sig Signature, payload []byte, keys KeysMap) error {
t := tlog.GetTL(ctx)
if sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension kind")
}
if sig.Extension.Kind != DockerDsseExtKind {
return fmt.Errorf("error unsupported signature extension kind: %s", sig.Extension.Kind)
}
// verify TL entry
if sig.Extension.Ext.Tl.Kind != RekorTlExtKind {
return fmt.Errorf("error unsupported TL extension kind: %s", sig.Extension.Ext.Tl.Kind)
}
entry := sig.Extension.Ext.Tl.Data
entryBytes, err := json.Marshal(entry)
if err != nil {
return fmt.Errorf("failed to marshal TL entry: %w", err)
}
integratedTime, err := t.VerifyLogEntry(ctx, entryBytes)
if err != nil {
return fmt.Errorf("TL entry failed verification: %w", err)
}
keyMeta, ok := keys[sig.KeyID]
if !ok {
return fmt.Errorf("error key not found: %s", sig.KeyID)
}
if keyMeta.Distrust {
return fmt.Errorf("key %s is distrusted", keyMeta.ID)
}
if integratedTime.Before(keyMeta.From) {
return fmt.Errorf("key %s was not yet valid at TL log time %s (key valid from %s)", keyMeta.ID, integratedTime, keyMeta.From)
}
if keyMeta.To != nil && !integratedTime.Before(*keyMeta.To) {
return fmt.Errorf("key %s was already %s at TL log time %s (key %s at %s)", keyMeta.ID, keyMeta.Status, integratedTime, keyMeta.Status, *keyMeta.To)
}
// TODO: this is unmarshalling with MarshalPKIXPublicKey only for us to marshal it again
publicKey, err := signerverifier.Parse([]byte(keyMeta.PEM))
if err != nil {
return fmt.Errorf("failed to parse public key: %w", err)
}
// verify TL entry payload
encodedPub, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return fmt.Errorf("error failed to marshal public key: %w", err)
}
err = t.VerifyEntryPayload(entryBytes, payload, encodedPub)
if err != nil {
return fmt.Errorf("TL entry failed payload verification: %w", err)
}
// decode signature
signature, err := base64.StdEncoding.Strict().DecodeString(sig.Sig)
if err != nil {
return fmt.Errorf("error failed to decode signature: %w", err)
}
// verify payload ecdsa signature
ok = ecdsa.VerifyASN1(publicKey, util.S256(payload), signature)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func ValidPayloadType(payloadType string) bool {
return payloadType == intoto.PayloadType || payloadType == ociv1.MediaTypeDescriptor
}

View File

@@ -0,0 +1,46 @@
package attestation_test
import (
"encoding/base64"
"testing"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
)
func TestValidPayloadType(t *testing.T) {
testCases := []struct {
name string
payloadType string
expected bool
}{
{"valid in-toto payload type", intoto.PayloadType, true},
{"valid oci descriptor payload type", ociv1.MediaTypeDescriptor, true},
{"invalid payload type", "application/vnd.test.fail", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equalf(t, tc.expected, attestation.ValidPayloadType(tc.payloadType), "expected %v for payload type %s", tc.expected, tc.payloadType)
})
}
}
func TestVerifyUnsignedAttestation(t *testing.T) {
ctx, _ := test.Setup(t)
payload := []byte("payload")
env := &attestation.Envelope{
// no signatures
Signatures: []attestation.Signature{},
Payload: base64.StdEncoding.EncodeToString(payload),
PayloadType: intoto.PayloadType,
}
_, err := attestation.VerifyDSSE(ctx, env, attestation.KeysMap{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "no signatures")
}

View File

@@ -16,7 +16,7 @@ import (
)
func TestGetTufMetadataMirror(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "internal", "test", "testdata", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
@@ -34,7 +34,7 @@ func TestGetTufMetadataMirror(t *testing.T) {
}
func TestGetMetadataManifest(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "internal", "test", "testdata", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
@@ -74,7 +74,7 @@ func TestGetMetadataManifest(t *testing.T) {
}
func TestGetDelegatedMetadataMirrors(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "internal", "test", "testdata", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")

View File

@@ -22,7 +22,7 @@ type Layers struct {
}
func TestGetTufTargetsMirror(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "internal", "test", "testdata", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
@@ -56,7 +56,7 @@ func TestGetTufTargetsMirror(t *testing.T) {
}
func TestTargetDelegationMetadata(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "internal", "test", "testdata", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
@@ -69,7 +69,7 @@ func TestTargetDelegationMetadata(t *testing.T) {
}
func TestGetDelegatedTargetMirrors(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "internal", "test", "testdata", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")

27
pkg/oci/http.go Normal file
View File

@@ -0,0 +1,27 @@
package oci
import (
"net/http"
"github.com/hashicorp/go-cleanhttp"
)
type userAgentTransporter struct {
ua string
rt http.RoundTripper
}
type Option = func(*http.Client)
func (u *userAgentTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", u.ua)
return u.rt.RoundTrip(req)
}
func HttpTransport() http.RoundTripper {
return &userAgentTransporter{
ua: "Docker-Client",
rt: cleanhttp.DefaultTransport(),
}
}

395
pkg/oci/oci.go Normal file
View File

@@ -0,0 +1,395 @@
package oci
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/containerd/containerd/platforms"
"github.com/distribution/reference"
att "github.com/docker/attest/pkg/attestation"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/package-url/packageurl-go"
"github.com/pkg/errors"
)
// parsePlatform parses the provided platform string or attempts to obtain
// the platform of the current host system
func parsePlatform(platformStr string) (*v1.Platform, error) {
if platformStr == "" {
cdp := platforms.Normalize(platforms.DefaultSpec())
if cdp.OS != "windows" {
cdp.OS = "linux"
}
return &v1.Platform{
OS: cdp.OS,
Architecture: cdp.Architecture,
Variant: cdp.Variant,
}, nil
} else {
return v1.ParsePlatform(platformStr)
}
}
func attestationManifestFromOCILayout(path string, platformStr string) (*AttestationManifest, error) {
idx, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, fmt.Errorf("failed to load image index: %w", err)
}
idxm, err := idx.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
idxDescriptor := idxm.Manifests[0]
name := idxDescriptor.Annotations["org.opencontainers.image.ref.name"]
idxDigest := idxDescriptor.Digest
mfs, err := idx.ImageIndex(idxDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
}
mfs2, err := mfs.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
platform, err := parsePlatform(platformStr)
if err != nil {
return nil, fmt.Errorf("failed to parse platform: %w", err)
}
var imageDigest string
for _, mf := range mfs2.Manifests {
if mf.Platform.Equals(*platform) {
imageDigest = mf.Digest.String()
}
}
for _, mf := range mfs2.Manifests {
if mf.Annotations[DockerReferenceType] != AttestationManifestType {
continue
}
if mf.Annotations[DockerReferenceDigest] != imageDigest {
continue
}
attestationImage, err := mfs.Image(mf.Digest)
if err != nil {
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
}
manifest, err := attestationImage.Manifest()
if err != nil {
return nil, fmt.Errorf("failed to get manifest: %w", err)
}
attest := &AttestationManifest{
Name: name,
Image: attestationImage,
Manifest: manifest,
Descriptor: &mf,
Digest: imageDigest,
Platform: platform,
}
return attest, nil
}
return nil, errors.New("attestation manifest not found")
}
// implementation of AttestationResolver that closes over attestations from an oci layout
type OCILayoutResolver struct {
Path string
Platform string
*AttestationManifest
}
func (r *OCILayoutResolver) ImagePlatformStr() string {
return r.Platform
}
func (r *OCILayoutResolver) fetchAttestationManifest() (*AttestationManifest, error) {
if r.AttestationManifest == nil {
m, err := attestationManifestFromOCILayout(r.Path, r.Platform)
if err != nil {
return nil, fmt.Errorf("failed to get attestation manifest: %w", err)
}
r.AttestationManifest = m
}
return r.AttestationManifest, nil
}
func (r *OCILayoutResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
if r.AttestationManifest == nil {
_, err := r.fetchAttestationManifest()
if err != nil {
return nil, fmt.Errorf("failed to get attestation manifest: %w", err)
}
}
attestationImage := r.AttestationManifest.Image
layers, err := attestationImage.Layers()
if err != nil {
return nil, fmt.Errorf("failed to extract layers from attestation image: %w", err)
}
var envs []*att.Envelope
manifest := r.AttestationManifest.Manifest
for i, l := range manifest.Layers {
if l.Annotations[InTotoPredicateType] != predicateType {
continue
}
layer := layers[i]
mt, err := layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
mts := string(mt)
if !strings.HasSuffix(mts, "+dsse") {
continue
}
var env = new(att.Envelope)
// parse layer blob as json
r, err := layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer r.Close()
err = json.NewDecoder(r).Decode(env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
envs = append(envs, env)
}
return envs, nil
}
func (r *OCILayoutResolver) ImageName(ctx context.Context) (string, error) {
if r.AttestationManifest == nil {
_, err := r.fetchAttestationManifest()
if err != nil {
return "", fmt.Errorf("failed to get attestation manifest: %w", err)
}
}
return r.Name, nil
}
func (r *OCILayoutResolver) ImageDigest(ctx context.Context) (string, error) {
if r.AttestationManifest == nil {
_, err := r.fetchAttestationManifest()
if err != nil {
return "", fmt.Errorf("failed to get attestation manifest: %w", err)
}
}
return r.Digest, nil
}
type RegistryResolver struct {
Image string
Platform string
*AttestationManifest
}
func (r *RegistryResolver) ImageName(ctx context.Context) (string, error) {
return r.Image, nil
}
func (r *RegistryResolver) ImagePlatformStr() string {
return r.Platform
}
func (r *RegistryResolver) ImageDigest(ctx context.Context) (string, error) {
if r.AttestationManifest == nil {
attest, err := FetchAttestationManifest(ctx, r.Image, r.Platform)
if err != nil {
return "", fmt.Errorf("failed to get attestation manifest: %w", err)
}
r.AttestationManifest = attest
}
return r.Digest, nil
}
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
if r.AttestationManifest == nil {
attest, err := FetchAttestationManifest(ctx, r.Image, r.Platform)
if err != nil {
return nil, fmt.Errorf("failed to get attestation manifest: %w", err)
}
r.AttestationManifest = attest
}
return ExtractEnvelopes(r.AttestationManifest, predicateType)
}
func FetchAttestationManifest(ctx context.Context, image, platformStr string) (*AttestationManifest, error) {
platform, err := parsePlatform(platformStr)
if err != nil {
return nil, fmt.Errorf("failed to parse platform %s: %w", platform, err)
}
// we want to get to the image index, so ignoring platform for now
options := withOptions(ctx, nil)
ref, err := name.ParseReference(image)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
desc, err := remote.Index(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to obtain index manifest: %w", err)
}
ix, err := desc.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to obtain index manifest: %w", err)
}
digest, err := imageDigestForPlatform(ix, platform)
if err != nil {
return nil, fmt.Errorf("failed to obtain image for platform: %w", err)
}
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), digest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
attestationDigest, err := attestationDigestForDigest(ix, digest, "attestation-manifest")
if err != nil {
return nil, fmt.Errorf("failed to obtain attestation for image: %w", err)
}
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), attestationDigest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
remoteDescriptor, err := remote.Get(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to get attestation: %w", err)
}
manifest := new(v1.Manifest)
err = json.Unmarshal(remoteDescriptor.Manifest, manifest)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal attestation: %w", err)
}
attestationImage, err := remoteDescriptor.Image()
if err != nil {
return nil, fmt.Errorf("failed to get attestation image: %w", err)
}
attest := &AttestationManifest{
Name: image,
Image: attestationImage,
Manifest: manifest,
Descriptor: &remoteDescriptor.Descriptor,
Digest: digest,
Platform: platform,
}
return attest, nil
}
func withOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
// prepare options
options := []remote.Option{remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithTransport(HttpTransport()), remote.WithContext(ctx)}
// add in platform into remote Get operation; this might conflict with an explicit digest, but we are trying anyway
if platform != nil {
options = append(options, remote.WithPlatform(*platform))
}
return options
}
func ExtractEnvelopes(ia *AttestationManifest, predicateType string) ([]*att.Envelope, error) {
manifest := ia.Manifest
im := ia.Image
var envs []*att.Envelope
ls, err := im.Layers()
if err != nil {
return nil, fmt.Errorf("failed to get layers: %w", err)
}
for i, l := range manifest.Layers {
if (strings.HasPrefix(string(l.MediaType), "application/vnd.in-toto.")) &&
strings.HasSuffix(string(l.MediaType), "+dsse") &&
l.Annotations[InTotoPredicateType] == predicateType {
reader, err := ls[i].Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer reader.Close()
var env = new(att.Envelope)
err = json.NewDecoder(reader).Decode(&env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
envs = append(envs, env)
}
}
return envs, nil
}
func imageDigestForPlatform(ix *v1.IndexManifest, platform *v1.Platform) (string, error) {
for _, m := range ix.Manifests {
if m.MediaType == ocispec.MediaTypeImageManifest || m.MediaType == "application/vnd.docker.distribution.manifest.v2+json" && m.Platform.Equals(*platform) {
return m.Digest.String(), nil
}
}
return "", errors.New(fmt.Sprintf("no image found for platform %v", platform))
}
func attestationDigestForDigest(ix *v1.IndexManifest, imageDigest string, attestType string) (string, error) {
for _, m := range ix.Manifests {
if v, ok := m.Annotations["vnd.docker.reference.type"]; ok && v == attestType {
if d, ok := m.Annotations["vnd.docker.reference.digest"]; ok && d == imageDigest {
return m.Digest.String(), nil
}
}
}
return "", errors.New(fmt.Sprintf("no attestation found for image %s", imageDigest))
}
func RefToPURL(ref string, platform string) (string, bool, error) {
var isCanonical bool
named, err := reference.ParseNormalizedNamed(ref)
if err != nil {
return "", false, fmt.Errorf("failed to parse ref %q: %w", ref, err)
}
var qualifiers []packageurl.Qualifier
if canonical, ok := named.(reference.Canonical); ok {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "digest",
Value: canonical.Digest().String(),
})
isCanonical = true
} else {
named = reference.TagNameOnly(named)
}
version := ""
if tagged, ok := named.(reference.Tagged); ok {
version = tagged.Tag()
}
name := reference.FamiliarName(named)
ns := ""
parts := strings.Split(name, "/")
if len(parts) > 1 {
ns = strings.Join(parts[:len(parts)-1], "/")
}
name = parts[len(parts)-1]
pf, err := parsePlatform(platform)
if err != nil {
return "", false, fmt.Errorf("failed to parse platform %q: %w", platform, err)
}
if pf != nil {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "platform",
Value: pf.String(),
})
}
p := packageurl.NewPackageURL("docker", ns, name, version, qualifiers, "")
return p.ToString(), isCanonical, nil
}

49
pkg/oci/oci_test.go Normal file
View File

@@ -0,0 +1,49 @@
package oci
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRefToPurl(t *testing.T) {
purl, canonical, err := RefToPURL("alpine", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@latest?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("google/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/google/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("library/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("docker.io/library/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("localhost:5001/library/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/library/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("localhost:5001/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("localhost:5001/alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/alpine?digest=sha256%3Ac5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b&platform=arm64%2Flinux", purl)
assert.True(t, canonical)
}

46
pkg/oci/resolver.go Normal file
View File

@@ -0,0 +1,46 @@
package oci
import (
"context"
att "github.com/docker/attest/pkg/attestation"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type AttestationManifest struct {
// attestation image details
Image v1.Image
Manifest *v1.Manifest
Descriptor *v1.Descriptor
// details of subect image
Name string
Digest string
Platform *v1.Platform
}
type AttestationResolver interface {
ImageName(ctx context.Context) (string, error)
ImagePlatformStr() string
ImageDigest(ctx context.Context) (string, error)
Attestations(ctx context.Context, mediaType string) ([]*att.Envelope, error)
}
type MockResolver struct {
Envs []*att.Envelope
}
func (r MockResolver) Attestations(ctx context.Context, mediaType string) ([]*att.Envelope, error) {
return r.Envs, nil
}
func (r MockResolver) ImageName(ctx context.Context) (string, error) {
return "library/alpine:latest", nil
}
func (r MockResolver) ImageDigest(ctx context.Context) (string, error) {
return "sha256:test-digest", nil
}
func (r MockResolver) ImagePlatformStr() string {
return "linux/amd64"
}

8
pkg/oci/types.go Normal file
View File

@@ -0,0 +1,8 @@
package oci
const (
DockerReferenceType = "vnd.docker.reference.type"
DockerReferenceDigest = "vnd.docker.reference.digest"
AttestationManifestType = "attestation-manifest"
InTotoPredicateType = "in-toto.io/predicate-type"
)

41
pkg/policy/evaluator.go Normal file
View File

@@ -0,0 +1,41 @@
package policy
import (
"context"
"fmt"
"github.com/docker/attest/pkg/oci"
)
type policyEvaluatorCtxKeyType struct{}
var PolicyEvaluatorCtxKey policyEvaluatorCtxKeyType
// sets PolicyEvaluator in context
func WithPolicyEvaluator(ctx context.Context, pe PolicyEvaluator) context.Context {
return context.WithValue(ctx, PolicyEvaluatorCtxKey, pe)
}
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set
func GetPolicyEvaluator(ctx context.Context) (PolicyEvaluator, error) {
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(PolicyEvaluator)
if !ok {
return nil, fmt.Errorf("no policy evaluator client set on context (set one with policy.WithPolicyEvaluator)")
}
return t, nil
}
type PolicyEvaluator interface {
Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
}
type MockPolicyEvaluator struct {
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
}
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error {
if pe.EvaluateFunc != nil {
return pe.EvaluateFunc(ctx, resolver, policy, input)
}
return nil
}

203
pkg/policy/policy.go Normal file
View File

@@ -0,0 +1,203 @@
package policy
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/distribution/reference"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/tuf"
goyaml "gopkg.in/yaml.v3"
)
const (
PolicyMappingFileName = "mapping.yaml"
)
var (
PolicyFileNames = []string{"data.yaml", "policy.rego"}
)
type PolicyMappings struct {
Version string `json:"version"`
Kind string `json:"kind"`
Policies []PolicyMapping `json:"policies"`
Mirrors []PolicyMirror `json:"mirrors"`
}
type PolicyMapping struct {
Name string `json:"namespace"`
Location string `json:"location"`
Description string `json:"description"`
Origin PolicyOrigin `json:"origin"`
}
type PolicyMirror struct {
Name string `json:"name"`
Mirror MirrorSpec `json:"mirror"`
}
type MirrorSpec struct {
Domains []string `json:"domains"`
Prefix string `json:"prefix"`
}
type PolicyOrigin struct {
Name string `json:"name"`
Prefix string `json:"prefix"`
Domain string `json:"domain"`
}
type PolicyOptions struct {
TufClient tuf.TUFClient
LocalTargetsDir string
LocalPolicyDir string
}
type PolicyInput struct {
Digest string `json:"digest"`
Purl string `json:"purl"`
IsCanonical bool `json:"isCanonical"`
}
type PolicyFile struct {
Path string
Content []byte
}
func resolveLocalPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
if opts.LocalPolicyDir == "" {
return nil, fmt.Errorf("local policy dir not set")
}
files := make([]*PolicyFile, 0, len(PolicyFileNames))
for _, filename := range PolicyFileNames {
filePath := path.Join(opts.LocalPolicyDir, mapping.Location, filename)
fileContents, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read policy file %s: %w", filename, err)
}
files = append(files, &PolicyFile{
Path: filename,
Content: fileContents,
})
}
return files, nil
}
func loadLocalMappings(opts *PolicyOptions) (*PolicyMappings, error) {
if opts.LocalPolicyDir == "" {
return nil, nil
}
mappings := &PolicyMappings{}
path := path.Join(opts.LocalPolicyDir, PolicyMappingFileName)
mappingFile, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read policy mapping file %s: %w", path, err)
}
err = goyaml.Unmarshal(mappingFile, mappings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal policy mapping file %s: %w", path, err)
}
return mappings, nil
}
func resolveTufPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
files := make([]*PolicyFile, 0, len(PolicyFileNames))
for _, filename := range PolicyFileNames {
filePath := path.Join(mapping.Location, filename)
_, fileContents, err := opts.TufClient.DownloadTarget(filePath, filepath.Join(opts.LocalTargetsDir, filePath))
if err != nil {
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
}
files = append(files, &PolicyFile{
Path: filename,
Content: fileContents,
})
}
return files, nil
}
func loadTufMappings(tufClient tuf.TUFClient, localTargetsDir string) (*PolicyMappings, error) {
filename := PolicyMappingFileName
_, fileContents, err := tufClient.DownloadTarget(filename, filepath.Join(localTargetsDir, filename))
if err != nil {
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
}
mappings := &PolicyMappings{}
err = goyaml.Unmarshal(fileContents, mappings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal policy mapping file %s: %w", filename, err)
}
return mappings, nil
}
func findPolicyMatch(named reference.Named, mappings *PolicyMappings) (*PolicyMapping, *PolicyMirror) {
if mappings != nil {
for _, mapping := range mappings.Policies {
if mapping.Origin.Domain == reference.Domain(named) &&
strings.HasPrefix(reference.Path(named), mapping.Origin.Prefix) {
return &mapping, nil
}
}
// now search mirrors
for _, mirror := range mappings.Mirrors {
if util.StringInSlice(reference.Domain(named), mirror.Mirror.Domains) &&
strings.HasPrefix(reference.Path(named), mirror.Mirror.Prefix) {
for _, mapping := range mappings.Policies {
if mapping.Name == mirror.Name {
return &mapping, nil
}
}
return nil, &mirror
}
}
}
return nil, nil
}
func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *PolicyOptions) ([]*PolicyFile, error) {
imageName, err := resolver.ImageName(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get image name: %w", err)
}
named, err := reference.ParseNormalizedNamed(imageName)
if err != nil {
return nil, fmt.Errorf("failed to parse image name: %w", err)
}
localMappings, err := loadLocalMappings(opts)
if err != nil {
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
}
mapping, mirror := findPolicyMatch(named, localMappings)
if mapping != nil {
return resolveLocalPolicy(opts, mapping)
}
// must check tuf
tufMappings, err := loadTufMappings(opts.TufClient, opts.LocalTargetsDir)
if err != nil {
return nil, fmt.Errorf("failed to load tuf policy mappings: %w", err)
}
// it's a mirror of a tuf policy
if mirror != nil {
for _, mapping := range tufMappings.Policies {
if mapping.Name == mirror.Name {
return resolveTufPolicy(opts, &mapping)
}
}
}
// try to resolve a tuf policy directly
mapping, _ = findPolicyMatch(named, tufMappings)
if mapping == nil {
return nil, nil
}
return resolveTufPolicy(opts, mapping)
}

110
pkg/policy/policy_test.go Normal file
View File

@@ -0,0 +1,110 @@
package policy_test
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/policy"
"github.com/docker/attest/pkg/tuf"
"github.com/stretchr/testify/assert"
)
func loadAttestation(t *testing.T, path string) *attestation.Envelope {
ex, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var env = new(attestation.Envelope)
err = json.Unmarshal(ex, env)
if err != nil {
t.Fatal(err)
}
return env
}
func TestRegoEvaluator_Evaluate(t *testing.T) {
ctx, _ := test.Setup(t)
TestDataPath := filepath.Join("..", "..", "test", "testdata")
MockTufRepo := filepath.Join(TestDataPath, "local-policy")
ExampleAttestation := filepath.Join(TestDataPath, "example_attestation.json")
VSA := filepath.Join(TestDataPath, "vsa.json")
re := policy.NewRegoEvaluator(true)
defaultInput := &policy.PolicyInput{
Digest: "sha256:test-digest",
Purl: "test-purl",
IsCanonical: true,
}
defaultResolver := oci.MockResolver{
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation)},
}
vsaResolver := oci.MockResolver{
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation), loadAttestation(t, VSA)},
}
testCases := []struct {
repo string
expectSuccess bool
input *policy.PolicyInput
resolver oci.AttestationResolver
policy *policy.PolicyOptions
}{
{repo: "testdata/mock-tuf-allow", expectSuccess: true, input: defaultInput, resolver: defaultResolver},
{repo: "testdata/mock-tuf-deny", expectSuccess: false, input: defaultInput, resolver: defaultResolver},
{repo: "testdata/mock-tuf-verify-sig", expectSuccess: true, input: defaultInput, resolver: defaultResolver},
{repo: "testdata/mock-tuf-wrong-key", expectSuccess: false, input: defaultInput, resolver: defaultResolver},
{repo: MockTufRepo, expectSuccess: true, input: &policy.PolicyInput{
Digest: "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
Purl: "pkg:docker/test-image@test?platform=linux%2Famd64",
IsCanonical: true,
}, resolver: vsaResolver},
{repo: MockTufRepo, expectSuccess: true, input: &policy.PolicyInput{
Digest: "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
Purl: "pkg:docker/test-image@test?platform=linux%2Famd64",
IsCanonical: false,
}, resolver: vsaResolver},
// not a doi
{repo: MockTufRepo, expectSuccess: false, input: defaultInput, resolver: vsaResolver, policy: &policy.PolicyOptions{
LocalPolicyDir: "testdata/mock-tuf-deny",
}},
// digest mismatch
{repo: MockTufRepo, expectSuccess: false, input: &policy.PolicyInput{
Digest: "sha256:test-digest-wrong",
Purl: "test-purl",
IsCanonical: false,
}, resolver: vsaResolver},
}
for _, tc := range testCases {
t.Run(tc.repo, func(t *testing.T) {
tufClient := tuf.NewMockTufClient(tc.repo, test.CreateTempDir(t, "", "tuf-dest"))
if tc.policy == nil {
tc.policy = &policy.PolicyOptions{
TufClient: tufClient,
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
}
}
policyFiles, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy)
assert.NoErrorf(t, err, "failed to resolve policy")
err = re.Evaluate(ctx, tc.resolver, policyFiles, tc.input)
if tc.expectSuccess {
assert.NoErrorf(t, err, "Evaluate failed")
} else {
assert.Errorf(t, err, "Evaluate should have failed")
}
})
}
}

220
pkg/policy/rego.go Normal file
View File

@@ -0,0 +1,220 @@
package policy
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
att "github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/oci"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/storage/inmem"
"github.com/open-policy-agent/opa/tester"
"github.com/open-policy-agent/opa/topdown"
"github.com/open-policy-agent/opa/types"
opa "github.com/open-policy-agent/opa/util"
"sigs.k8s.io/yaml"
)
type regoEvaluator struct {
debug bool
}
func NewRegoEvaluator(debug bool) PolicyEvaluator {
return &regoEvaluator{
debug: debug,
}
}
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, files []*PolicyFile, input *PolicyInput) error {
var regoOpts []func(*rego.Rego)
// Create a new in-memory store
store := inmem.New()
params := storage.TransactionParams{}
params.Write = true
txn, err := store.NewTransaction(ctx, params)
if err != nil {
return err
}
for _, target := range files {
// load yaml as data (no rego opt for this!?)
if filepath.Ext(target.Path) == ".yaml" {
yamlData, err := loadYAML(target.Path, target.Content)
if err != nil {
return err
}
err = store.Write(ctx, txn, storage.AddOp, storage.Path{}, yamlData)
if err != nil {
return err
}
} else {
regoOpts = append(regoOpts, rego.Module(target.Path, string(target.Content)))
}
}
err = store.Commit(ctx, txn)
if err != nil {
store.Abort(ctx, txn)
return err
}
if re.debug {
regoOpts = append(regoOpts,
rego.EnablePrintStatements(true),
rego.PrintHook(topdown.NewPrintHook(os.Stderr)),
rego.Dump(os.Stderr),
)
}
regoOpts = append(regoOpts,
rego.Query("data.docker.allow"),
rego.StrictBuiltinErrors(true),
rego.Input(input),
rego.Store(store),
)
for _, custom := range RegoFunctions(resolver) {
regoOpts = append(regoOpts, custom.Func)
}
r := rego.New(regoOpts...)
rs, err := r.Eval(ctx)
if err != nil {
return fmt.Errorf("error from Eval: %w", err)
}
if !rs.Allowed() {
return fmt.Errorf("policy evaluation failed")
}
return nil
}
var dynamicObj = types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))
var arrayObj = types.NewArray(nil, dynamicObj)
var verifyDecl = &ast.Builtin{
Name: "attestations.verify_envelope",
Decl: types.NewFunction(types.Args(dynamicObj, arrayObj), dynamicObj),
Nondeterministic: true,
}
var attestDecl = &ast.Builtin{
Name: "attestations.attestation",
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
Nondeterministic: true,
}
func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
return []*tester.Builtin{
{
Decl: verifyDecl,
Func: rego.Function2(
&rego.Function{
Name: verifyDecl.Name,
Decl: verifyDecl.Decl,
Memoize: true,
Nondeterministic: verifyDecl.Nondeterministic,
},
verifyIntotoEnvelope),
},
{
Decl: attestDecl,
Func: rego.Function1(
&rego.Function{
Name: attestDecl.Name,
Decl: attestDecl.Decl,
Memoize: true,
Nondeterministic: attestDecl.Nondeterministic,
},
fetchIntotoAttestations(resolver)),
},
}
}
func fetchIntotoAttestations(resolver oci.AttestationResolver) func(rego.BuiltinContext, *ast.Term) (*ast.Term, error) {
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
if !ok {
return nil, fmt.Errorf("predicateTypeTerm is not a string")
}
predicateType := string(predicateTypeStr)
envelopes, err := resolver.Attestations(rCtx.Context, predicateType)
if err != nil {
return nil, err
}
// Convert each envelope to an ast.Value.
values := make([]*ast.Term, len(envelopes))
for i, envelope := range envelopes {
value, err := ast.InterfaceToValue(envelope)
if err != nil {
return nil, err
}
values[i] = ast.NewTerm(value)
}
// Wrap the values in an ast.Array and convert it to an ast.Term.
array := ast.NewTerm(ast.NewArray(values...))
return array, nil
}
}
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, keysTerm *ast.Term) (*ast.Term, error) {
env := new(att.Envelope)
var keys att.Keys
err := ast.As(envTerm.Value, env)
if err != nil {
return nil, fmt.Errorf("failed to cast envelope: %w", err)
}
err = ast.As(keysTerm.Value, &keys)
if err != nil {
return nil, fmt.Errorf("failed to cast keys: %w", err)
}
keysmap := make(map[string]att.KeyMetadata, len(keys))
for _, key := range keys {
keysmap[key.ID] = key
}
payload, err := att.VerifyDSSE(rCtx.Context, env, keysmap)
if err != nil {
return nil, err
}
statement := new(intoto.Statement)
switch env.PayloadType {
case intoto.PayloadType:
err = json.Unmarshal(payload, statement)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
}
// TODO: implement other types of envelope
default:
return nil, fmt.Errorf("unsupported payload type: %s", env.PayloadType)
}
value, err := ast.InterfaceToValue(statement)
if err != nil {
return nil, err
}
return ast.NewTerm(value), nil
}
func loadYAML(path string, bs []byte) (interface{}, error) {
var x interface{}
bs, err := yaml.YAMLToJSON(bs)
if err != nil {
return nil, fmt.Errorf("%v: error converting YAML to JSON: %v", path, err)
}
err = opa.UnmarshalJSON(bs, &x)
if err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
return x, nil
}

View File

@@ -0,0 +1 @@
config:

View File

@@ -0,0 +1,5 @@
package docker
import rego.v1
allow := true

View File

@@ -0,0 +1,10 @@
# map repos to policies
version: v1
kind: policy-mapping
policies:
- origin:
domain: docker.io
prefix: library/
name: docker-official-images
description: Docker Official Images
location: doi

View File

@@ -0,0 +1 @@
config:

View File

@@ -0,0 +1,5 @@
package docker
import rego.v1
allow := false

View File

@@ -0,0 +1,10 @@
# map repos to policies
version: v1
kind: policy-mapping
policies:
- origin:
domain: docker.io
prefix: library/
name: docker-official-images
description: Docker Official Images
location: doi

View File

@@ -0,0 +1 @@
config:

View File

@@ -0,0 +1,15 @@
package docker
import rego.v1
keys := [{
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN\n9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==\n-----END PUBLIC KEY-----",
"from": "2023-12-15T14:00:00Z",
"to": null
}]
allow if {
some env in attestations.attestation("foo")
statement := attestations.verify_envelope(env, keys)
}

View File

@@ -0,0 +1,10 @@
# map repos to policies
version: v1
kind: policy-mapping
policies:
- origin:
domain: docker.io
prefix: library/
name: docker-official-images
description: Docker Official Images
location: doi

View File

@@ -0,0 +1 @@
config:

View File

@@ -0,0 +1,19 @@
package docker
import rego.v1
keys := {
"a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4": {
"id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
"key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHyZpSgzvqFqNv7f3x7865OS38rAb\nQMcff55zM2UH/KR3Pr84a8QsGDNgaNGzJQJWjtMSgfV8WnNoffNK+svFNg==\n-----END PUBLIC KEY-----",
"from": "2023-12-15T14:00:00Z",
"to": null,
}
}
allow if {
some env in attestations.attestation("foo")
statement := attestations.verify_envelope(env, keys)
}
allow := true

View File

@@ -0,0 +1,10 @@
# map repos to policies
version: v1
kind: policy-mapping
policies:
- origin:
domain: docker.io
prefix: library/
name: docker-official-images
description: Docker Official Images
location: doi

27
pkg/signerverifier/aws.go Normal file
View File

@@ -0,0 +1,27 @@
package signerverifier
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
awssigner "github.com/sigstore/sigstore/pkg/signature/kms/aws"
)
// using AWS KMS
func GetAWSSigner(ctx context.Context, keyArn string, region string) (dsse.SignerVerifier, error) {
keypath := fmt.Sprintf("awskms:///%s", keyArn)
sv, err := awssigner.LoadSignerVerifier(ctx, keypath, config.WithRegion(region))
if err != nil {
return nil, fmt.Errorf("error loading aws signer verifier: %w", err)
}
cs, _, err := sv.CryptoSigner(context.Background(), func(err error) {})
if err != nil {
return nil, fmt.Errorf("error getting aws crypto signer: %w", err)
}
signer := &ECDSA256_SignerVerifier{
Signer: cs,
}
return signer, nil
}

View File

@@ -0,0 +1,56 @@
package signerverifier
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"github.com/docker/attest/internal/util"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type ECDSA256_SignerVerifier struct {
crypto.Signer
}
// implement keyid function
func (s *ECDSA256_SignerVerifier) KeyID() (string, error) {
keyid, err := KeyID(s.Signer.Public())
if err != nil {
return "", fmt.Errorf("error getting keyid: %w", err)
}
return keyid, nil
}
func (s *ECDSA256_SignerVerifier) Public() crypto.PublicKey {
return s.Signer.Public()
}
func (s *ECDSA256_SignerVerifier) Sign(ctx context.Context, data []byte) ([]byte, error) {
return s.Signer.Sign(rand.Reader, data, crypto.SHA256)
}
func (s *ECDSA256_SignerVerifier) Verify(ctx context.Context, data []byte, sig []byte) error {
pub, ok := s.Signer.Public().(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("public key is not ecdsa")
}
ok = ecdsa.VerifyASN1(pub, util.S256(data), sig)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func GenKeyPair() (dsse.SignerVerifier, error) {
signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
return &ECDSA256_SignerVerifier{
Signer: signer,
}, nil
}

View File

@@ -0,0 +1,17 @@
package signerverifier
import (
"crypto"
"crypto/x509"
"fmt"
"github.com/docker/attest/internal/util"
)
func KeyID(pubKey crypto.PublicKey) (string, error) {
pub, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", fmt.Errorf("error marshalling public key: %w", err)
}
return util.HexHashBytes(pub), nil
}

View File

@@ -0,0 +1,39 @@
package signerverifier
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"fmt"
)
const pemType = "PUBLIC KEY"
func Parse(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
p, _ := pem.Decode(pubkeyBytes)
if p == nil {
return nil, fmt.Errorf("pubkey file does not contain any PEM data")
}
if p.Type != pemType {
return nil, fmt.Errorf("pubkey file does not contain a public key")
}
pubKey, err := x509.ParsePKIXPublicKey(p.Bytes)
if err != nil {
return nil, fmt.Errorf("error failed to parse public key: %w", err)
}
ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("error public key is not an ecdsa key: %w", err)
}
return ecdsaPubKey, nil
}
func ToPEM(ecdsaPubKey *ecdsa.PublicKey) ([]byte, error) {
pubKeyBytes, err := x509.MarshalPKIXPublicKey(ecdsaPubKey)
if err != nil {
return nil, fmt.Errorf("error failed to marshal public key: %w", err)
}
return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: pubKeyBytes}), nil
}

50
pkg/tlog/mock.go Normal file
View File

@@ -0,0 +1,50 @@
package tlog
import (
"context"
"fmt"
"time"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/sigstore/rekor/pkg/generated/models"
)
const (
USE_MOCK_TL = true
TestEntry = `{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5Zjg2ZDA4MTg4NGM3ZDY1OWEyZmVhYTBjNTVhZDAxNWEzYmY0ZjFiMmIwYjgyMmNkMTVkNmMxNWIwZjAwYTA4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJQUlyVUZGUzBIYmNzZjc5L08yajVXdHl2R2Vvd1NVSXpZcDlBM2IwWnREVUFpQVQxZU42ZjFyVmVWa011REFlN3dxWkJ2bE5LY2VsajNVVDNmaWhyQjZSY2c9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSlZla05DSzJGQlJFRm5SVU5CWjBWQ1RVRnZSME5EY1VkVFRUUTVRa0ZOUTAxQk9IaEVWRUZNUW1kT1ZrSkJUVlJDU0ZKc1l6TlJkMGhvWTA0S1RXcE5lRTFxU1ROTlZHdDVUWHBWTlZkb1kwNU5hbEY0VFdwSk1rMVVhM2xOZWxVMVYycEJVRTFSTUhkRGQxbEVWbEZSUkVWM1VqQmFXRTR3VFVacmR3cEZkMWxJUzI5YVNYcHFNRU5CVVZsSlMyOWFTWHBxTUVSQlVXTkVVV2RCUlVRMFZpdFNSV2g0SzJGeFYwZzNlV3hOVFVSSVlXaE9UVzVOVEZOUFNsQXZDamxyUVcwNWJIQXJNMjF4V1ZSQmFGVlNjbUUyVDBRMVVYZzRXbUprSzJWMVVIbFFhemw1SzNjdloxZEhSRUk1ZW00dlNXd3hTMDVIVFVWUmQwUm5XVVFLVmxJd1VFRlJTQzlDUVZGRVFXZGxRVTFDVFVkQk1WVmtTbEZSVFUxQmIwZERRM05IUVZGVlJrSjNUVVJOUVhkSFFURlZaRVYzUlVJdmQxRkRUVUZCZHdwRWQxbEVWbEl3VWtKQlozZENiMGxGWkVkV2VtUkVRVXRDWjJkeGFHdHFUMUJSVVVSQlowNUtRVVJDUjBGcFJVRTNOMjFFTDFSbVJtRlJVemxrWlhRMENqbFhaRk41YURKT1VTOUZiMVJtYVVGdFFtaHVWblpEVTNSUVowTkpVVU1yZDNSdllpOU9iMUp4T0c5cU4wZDNibTVKYUZKVGRDOVJNbmtyVXpoUkwzSUthRkpVYW5GaE9HZExRVDA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=","integratedTime":1703705039,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d","logIndex":59674396,"verification":{"inclusionProof":{"checkpoint":"rekor.sigstore.dev - 2605736670972794746\n55510966\nJCi1O53Xmdi9lXnui4Q5SQ+MJSMnWr1Bxn+Q2Qf22tU=\nTimestamp: 1703705040158839214\n\n— rekor.sigstore.dev wNI9ajBFAiAXgtjFDVqCSgiSP04TQzELrz4+EyBwyYVL2EEULTCy0AIhAI9peLU76ZUD1tvU8qvzBJBo77IYD1rc+A1MPc35AeVK\n","hashes":["fb77ee213b48f4b18dc81c6e634c570abf99b257713561f174f2e0f4c039af67","6cb113bbefadecbbb8b89b1c08232438a6125071790b6a062cff8c1ccfdcb91e","6fbe1424e264e4590ca502d671b7a036c87f7a90d1f57534b98eb781144160bf","077b606720a6478200f6c3ed08a68e9b01b1cae192cb120888ddcc95521601bd","b6f8e8bc21ae0cde82b92422a4b4f37b28a43185821e468a4e65b6c79ed8f5b7","89332533fac54e9bc68c7353c42f6ebb9fe38039f67910332ff95082072068d4","0814d6f707a75fb3334bab14ab5466bd8b9a64ae7be7cd4d53a428c64932bc66","e883e826f10329c63a4a2ed21156037a050df43b9d74079296beac6968ed4150","d79230703257b7e4a8a61b032b6980d1a0bdbc7ae96ca838b525b3751785fe48","2f4a77e5288462cd3b75084d37f1502dcbe0943d18dd95cb247fc1ebbabc0aad","38562c253d3536d0d00e3547c880b6b0251a25ac69605b50c9eaa1a27186cc7a","9dea192350ff8b3c0f5ccda38261cb38ebd61869281c3928912332d1144e0a04","2c4d25ba59aa573ab2c79c2d3cd9e1d74789b10632432724d63112ce50b44874","98c486feb5d87092a78a46c4b5be04868654900affc2e86ffb20074dc73a883a","6969c49bd73f19bf28a5eaeabd331ddd60502defb2cd3d96e17b741c80adec6c"],"logIndex":55510965,"rootHash":"2428b53b9dd799d8bd9579ee8b8439490f8c2523275abd41c67f90d907f6dad5","treeSize":55510966},"signedEntryTimestamp":"MEUCIQCG9PRI8PcvtJyE9pbcculZipze6NEWR1Nk8EYocto3BwIgYu5gqgjW80HMjSjUxUNJLp0wlVTesnJCeByUBySc59w="}}`
)
func GetMockTL() TL {
unmarshalEntry := func(entry []byte) (*models.LogEntryAnon, error) {
le := new(models.LogEntryAnon)
err := le.UnmarshalBinary(entry)
if err != nil {
return nil, fmt.Errorf("error failed to unmarshal TL entry: %w", err)
}
return le, nil
}
return &MockTL{
UploadLogEntryFunc: func(ctx context.Context, subject string, payload []byte, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
return []byte(TestEntry), nil
},
VerifyLogEntryFunc: func(ctx context.Context, entryBytes []byte) (time.Time, error) {
// return the integrated time in the log entry without any checking
le, err := unmarshalEntry(entryBytes)
if err != nil {
return time.Time{}, err
}
if le.IntegratedTime == nil {
return time.Time{}, fmt.Errorf("error missing integrated time in TL entry")
}
return time.Unix(*le.IntegratedTime, 0), nil
},
VerifyEntryPayloadFunc: func(entryBytes, payload, pkToken []byte) error {
return nil
},
UnmarshalEntryFunc: func(entry []byte) (any, error) {
return unmarshalEntry(entry)
},
}
}

267
pkg/tlog/tl.go Normal file
View File

@@ -0,0 +1,267 @@
package tlog
import (
"context"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
"strings"
"time"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/signerverifier"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/sigstore/cosign/v2/pkg/cosign"
rclient "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"
)
const (
DefaultRekorURL = "https://rekor.sigstore.dev"
)
type tlCtxKeyType struct{}
var TlCtxKey tlCtxKeyType
// sets TL in context
func WithTL(ctx context.Context, tl TL) context.Context {
return context.WithValue(ctx, TlCtxKey, tl)
}
// gets TL from context, defaults to Rekor TL if not set
func GetTL(ctx context.Context) TL {
t, ok := ctx.Value(TlCtxKey).(TL)
if !ok {
t = &RekorTL{}
}
return t
}
type TlPayload struct {
Algorithm string
Hash string
Signature string
PublicKey string
}
type TL interface {
UploadLogEntry(ctx context.Context, subject string, payload, signature []byte, signer dsse.SignerVerifier) ([]byte, error)
VerifyLogEntry(ctx context.Context, entryBytes []byte) (time.Time, error)
VerifyEntryPayload(entryBytes, payload, publicKey []byte) error
UnmarshalEntry(entryBytes []byte) (any, error)
}
type MockTL struct {
UploadLogEntryFunc func(ctx context.Context, subject string, payload, signature []byte, signer dsse.SignerVerifier) ([]byte, error)
VerifyLogEntryFunc func(ctx context.Context, entryBytes []byte) (time.Time, error)
VerifyEntryPayloadFunc func(entryBytes, payload, publicKey []byte) error
UnmarshalEntryFunc func(entryBytes []byte) (any, error)
}
func (tl *MockTL) UploadLogEntry(ctx context.Context, subject string, payload, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
if tl.UploadLogEntryFunc != nil {
return tl.UploadLogEntryFunc(ctx, subject, payload, signature, signer)
}
return nil, nil
}
func (tl *MockTL) VerifyLogEntry(ctx context.Context, entryBytes []byte) (time.Time, error) {
if tl.VerifyLogEntryFunc != nil {
return tl.VerifyLogEntryFunc(ctx, entryBytes)
}
return time.Time{}, nil
}
func (tl *MockTL) VerifyEntryPayload(entryBytes, payload, publicKey []byte) error {
if tl.VerifyEntryPayloadFunc != nil {
return tl.VerifyEntryPayloadFunc(entryBytes, payload, publicKey)
}
return nil
}
func (tl *MockTL) UnmarshalEntry(entryBytes []byte) (any, error) {
if tl.UnmarshalEntryFunc != nil {
return tl.UnmarshalEntryFunc(entryBytes)
}
return nil, nil
}
type RekorTL struct{}
// UploadLogEntry submits a PK token signature to the transparency log
func (tl *RekorTL) UploadLogEntry(ctx context.Context, subject string, payload, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
// generate self-signed x509 cert
pubCert, err := CreateX509Cert(subject, signer)
if err != nil {
return nil, fmt.Errorf("Error creating x509 cert: %w", err)
}
// generate hash of payload
hasher := sha256.New()
hasher.Write(payload)
// upload entry
rekorClient, err := rclient.GetRekorClient(DefaultRekorURL)
if err != nil {
return nil, fmt.Errorf("Error creating rekor client: %w", err)
}
entry, err := cosign.TLogUpload(ctx, rekorClient, signature, hasher, pubCert)
if err != nil {
return nil, fmt.Errorf("Error uploading tlog: %w", err)
}
entryBytes, err := entry.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("error marshalling TL entry: %w", err)
}
return entryBytes, nil
}
// VerifyLogEntry verifies a transparency log entry
func (tl *RekorTL) VerifyLogEntry(ctx context.Context, entryBytes []byte) (time.Time, error) {
zeroTime := time.Time{}
entry, err := tl.UnmarshalEntry(entryBytes)
if err != nil {
return zeroTime, fmt.Errorf("error failed to unmarshal TL entry: %w", err)
}
le, ok := entry.(*models.LogEntryAnon)
if !ok {
return zeroTime, fmt.Errorf("expected entry to be of type *models.LogEntryAnon, got %T", entry)
}
err = le.Validate(strfmt.Default)
if err != nil {
return zeroTime, fmt.Errorf("TL entry failed validation: %w", err)
}
// TODO: get rekor public keys from TUF (ours or theirs?), and/or embed the public key in the binary
rekorPubKeys, err := cosign.GetRekorPubs(ctx)
if err != nil {
return zeroTime, fmt.Errorf("error failed to get rekor public keys: %w", err)
}
err = cosign.VerifyTLogEntryOffline(ctx, le, rekorPubKeys)
if err != nil {
return zeroTime, fmt.Errorf("TL entry failed verification: %w", err)
}
integratedTime := time.Unix(*le.IntegratedTime, 0)
return integratedTime, nil
}
// CreateX509Cert generates a self-signed x509 cert for TL submission
func CreateX509Cert(subject string, signer dsse.SignerVerifier) ([]byte, error) {
// encode ephemeral public key
ecPub, err := x509.MarshalPKIXPublicKey(signer.Public())
if err != nil {
return nil, fmt.Errorf("error marshalling public key: %w", err)
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: subject},
RawSubjectPublicKeyInfo: ecPub,
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour), // valid for 1 year
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
BasicConstraintsValid: true,
DNSNames: []string{subject},
IsCA: false,
}
// dsse.SignerVerifier doesn't implement cypto.Signer exactly
csigner, ok := signer.(*signerverifier.ECDSA256_SignerVerifier)
if !ok {
return nil, fmt.Errorf("expected signer to be of type *signerverifier.ECDSA_SignerVerifier, got %T", signer)
}
// create a self-signed X.509 certificate
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, signer.Public(), csigner.Signer)
if err != nil {
return nil, fmt.Errorf("error creating X.509 certificate: %w", err)
}
certBlock := &pem.Block{Type: "CERTIFICATE", Bytes: certDER}
return pem.EncodeToMemory(certBlock), nil
}
// VerifyEntryPayload checks that the TL entry payload matches envelope payload
func (tl *RekorTL) VerifyEntryPayload(entryBytes, payload, publicKey []byte) error {
entry, err := tl.UnmarshalEntry(entryBytes)
if err != nil {
return fmt.Errorf("error failed to unmarshal TL entry: %w", err)
}
le, ok := entry.(*models.LogEntryAnon)
if !ok {
return fmt.Errorf("expected tl entry to be of type *models.LogEntryAnon, got %T", entry)
}
tlBody, ok := le.Body.(string)
if !ok {
return fmt.Errorf("expected tl body to be of type string, got %T", entry)
}
rekord, err := extractHashedRekord(tlBody)
if err != nil {
return fmt.Errorf("error extract HashedRekord from TL entry: %w", err)
}
// compare payload hashes
payloadHash := hex.EncodeToString(util.S256(payload))
if rekord.Hash != payloadHash {
return fmt.Errorf("error payload and tl entry hash mismatch")
}
// compare public keys
cert, err := base64.StdEncoding.Strict().DecodeString(rekord.PublicKey)
if err != nil {
return fmt.Errorf("failed to decode public key: %w", err)
}
p, _ := pem.Decode(cert)
result, err := x509.ParseCertificate(p.Bytes)
if err != nil {
return fmt.Errorf("failed to parse certificate: %w", err)
}
if string(result.RawSubjectPublicKeyInfo) != string(publicKey) {
return fmt.Errorf("error payload and tl entry public key mismatch")
}
return nil
}
func (tl *RekorTL) UnmarshalEntry(entry []byte) (any, error) {
le := new(models.LogEntryAnon)
err := le.UnmarshalBinary(entry)
if err != nil {
return nil, fmt.Errorf("error failed to unmarshal TL entry: %w", err)
}
return le, nil
}
func extractHashedRekord(Body string) (*TlPayload, error) {
sig := new(TlPayload)
pe, err := models.UnmarshalProposedEntry(base64.NewDecoder(base64.StdEncoding, strings.NewReader(Body)), runtime.JSONConsumer())
if err != nil {
return nil, err
}
impl, err := types.UnmarshalEntry(pe)
if err != nil {
return nil, err
}
switch entry := impl.(type) {
case *hashedrekord_v001.V001Entry:
sig.Algorithm = *entry.HashedRekordObj.Data.Hash.Algorithm
sig.Hash = *entry.HashedRekordObj.Data.Hash.Value
sig.Signature = entry.HashedRekordObj.Signature.Content.String()
sig.PublicKey = entry.HashedRekordObj.Signature.PublicKey.Content.String()
return sig, nil
default:
return nil, fmt.Errorf("failed to extract haskedrekord, unsupported type: %T", entry)
}
}

93
pkg/tlog/tl_test.go Normal file
View File

@@ -0,0 +1,93 @@
package tlog
import (
"context"
"crypto/x509"
"encoding/pem"
"testing"
"time"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/signerverifier"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/stretchr/testify/assert"
)
const (
// test artifacts
TestPayload = "test"
TestPublicKey = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAED4V+REhx+aqWH7ylMMDHahNMnMLS\nOJP/9kAm9lp+3mqYTAhURra6OD5Qx8Zbd+euPyPk9y+w/gWGDB9zn/Il1A==\n-----END PUBLIC KEY-----"
)
func TestCreateX509Cert(t *testing.T) {
// TODO - replace with mock KMS
// generate test signing keys
signer, err := signerverifier.GenKeyPair()
assert.NoError(t, err)
// create x509 cert
cert, err := CreateX509Cert("test", signer)
assert.NoError(t, err)
p, _ := pem.Decode(cert)
result, err := x509.ParseCertificate(p.Bytes)
assert.NoError(t, err)
// test cert RawSubjectPublicKeyInfo field contains ephemeral public key
ecPub, err := x509.MarshalPKIXPublicKey(signer.Public())
assert.NoError(t, err)
assert.Equalf(t, string(result.RawSubjectPublicKeyInfo), string(ecPub), "certificate raw subject public key info does not match ephemeral public key")
// test cert common name == subject
assert.Equalf(t, result.Subject.CommonName, "test", "cert common name does not equal subject id")
}
func TestUploadAndVerifyLogEntry(t *testing.T) {
// message digest
payload := []byte("test")
hash := util.S256(payload)
// generate ephemeral keys to sign message digest
signer, err := signerverifier.GenKeyPair()
assert.NoError(t, err)
sig, err := signer.Sign(context.Background(), hash)
assert.NoError(t, err)
var tl TL
if USE_MOCK_TL {
tl = &MockTL{
UploadLogEntryFunc: func(ctx context.Context, subject string, payload []byte, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
return []byte(TestEntry), nil
},
VerifyLogEntryFunc: func(ctx context.Context, entryBytes []byte) (time.Time, error) {
return time.Time{}, nil
},
VerifyEntryPayloadFunc: func(entryBytes, payload, publicKey []byte) error {
return nil
},
}
} else {
tl = &RekorTL{}
}
// test upload log entry
ctx := WithTL(context.Background(), tl)
entry, err := tl.UploadLogEntry(ctx, "test", payload, sig, signer)
assert.NoError(t, err)
// test verify log entry
_, err = tl.VerifyLogEntry(ctx, entry)
assert.NoError(t, err)
// verify TL entry payload
ecPub, err := x509.MarshalPKIXPublicKey(signer.Public())
assert.NoError(t, err)
err = tl.VerifyEntryPayload(entry, payload, ecPub)
assert.NoError(t, err)
}
func TestVerifyEntryPayload(t *testing.T) {
tl := &RekorTL{}
p, _ := pem.Decode([]byte(TestPublicKey))
err := tl.VerifyEntryPayload([]byte(TestEntry), []byte(TestPayload), p.Bytes)
assert.NoError(t, err)
}

View File

@@ -10,7 +10,6 @@ import (
"testing"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/internal/util"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
@@ -49,8 +48,8 @@ func TestRegistryFetcher(t *testing.T) {
targetsRepo := regAddr.Host + "/tuf-targets"
targetFile := "test.txt"
delegatedRole := "test-role"
dir := test.CreateTempDir(t, "", "tuf_temp")
delegatedDir := test.CreateTempDir(t, dir, delegatedRole)
dir := CreateTempDir(t, "", "tuf_temp")
delegatedDir := CreateTempDir(t, dir, delegatedRole)
delegatedTargetFile := fmt.Sprintf("%s/%s", delegatedRole, targetFile)
cfg, err := config.New(metadataRepo, embed.DevRoot)

View File

@@ -4,24 +4,40 @@ import (
"context"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/internal/test"
"github.com/stretchr/testify/assert"
)
var (
HttpTufTestDataPath = filepath.Join("..", "..", "internal", "test", "testdata", "test-repo")
OciTufTestDataPath = filepath.Join("..", "..", "internal", "test", "testdata", "test-repo-oci")
HttpTufTestDataPath = filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo")
OciTufTestDataPath = filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo-oci")
)
func CreateTempDir(t *testing.T, dir, pattern string) string {
// Create a temporary directory for output oci layout
tempDir, err := os.MkdirTemp(dir, pattern)
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
// Register a cleanup function to delete the temp directory when the test exits
t.Cleanup(func() {
if err := os.RemoveAll(tempDir); err != nil {
t.Errorf("Failed to remove temp directory: %v", err)
}
})
return tempDir
}
// NewTufClient creates a new TUF client
func TestRootInit(t *testing.T) {
tufPath := test.CreateTempDir(t, "", "tuf_temp")
tufPath := CreateTempDir(t, "", "tuf_temp")
// Start a test HTTP server to serve data from ./testdata/test-repo/ paths
// Start a test HTTP server to serve data from /test/testdata/tuf/test-repo/ paths
server := httptest.NewServer(http.FileServer(http.Dir(HttpTufTestDataPath)))
defer server.Close()

48
test/testdata/example_attestation.json vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,58 @@
config:
doi:
keys:
- id: "f6a29392b1c08891ff456100aa448b4f6bf9c315850e11cc0883fe9c3c4412db"
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+XOm2uWjLJhpsJtHCFdGic26suOy
mCl2pBgCof+AHGFZFca40JL833OT+nRSZJRMPKBGibWqsjFrLdRCkOB7bA==
-----END PUBLIC KEY-----
from: "2024-01-01T00:00:00Z"
to: "2024-01-15T12:00:00Z"
# this key was rotated at a planned time
status: "rotated"
signing-format: "dssev1"
- id: "e6f4c70fbba21cbcac44915fff53fd2fdf90dd8849445795fe58014c2b5f8c64"
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZSkTE3si/JkRbuLjaYraS3//YBnX
8KtEcgdYKZQPl2DnSl4gPsu3KiVeEBWp5GK06IoZlcBAL3NF0OsUUP+yVg==
-----END PUBLIC KEY-----
from: "2024-01-15T12:00:00Z"
to: "2024-01-15T14:00:00Z"
# this key was leaked at a known time, so it revoked from that time
# this behaves the same way as "rotated" but might give another failure message
status: "revoked"
signing-format: "dssev1"
- id: "d45980c5cf39a5e1bab9febe3f16c1c0820b97a8fd061b0064e54b0826e856e4"
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEafssq2x1EDQcKDZhuSrCOxWWl5D4
JBa9iDJYDnLZp9kPKvv4RnD4rz7Ucfmd0l/zzM45qT29fSBTlguKmnOA8A==
-----END PUBLIC KEY-----
# this key was leaked at an unknown time, so it's completely distrusted
distrust: true
status: "revoked"
signing-format: "dssev1"
- id: "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4"
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN
9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==
-----END PUBLIC KEY-----
from: "2023-12-15T14:00:00Z"
to: null
# this key is still active
status: "active"
signing-format: "dssev1"
- id: "b281835e00059de24fb06bd6db06eb0e4a33d7bd7210d7027c209f14b19e812a"
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgE4Jz6FrLc3lp/YRlbuwOjK4n6ac
jVkSDAmFhi3Ir2Jy+cKeEB7iRPcLvBy9qoMZ9E93m1NdWY6KtDo+Qi52Rg==
-----END PUBLIC KEY-----
from: "2024-01-15T14:00:00Z"
to: null
# this key is still active
status: "active"
signing-format: "dssev1"

View File

@@ -0,0 +1,49 @@
package docker
import rego.v1
import data.config
splitDigest := split(input.digest, ":")
digestType := splitDigest[0]
digest := splitDigest[1]
allow if {
some env in attestations.attestation("https://slsa.dev/verification_summary/v0.1")
some statement in verified_statements(config.doi.keys, env)
}
verified_statements(keys, env) := statements if {
statements := {statement |
statement := attestations.verify_envelope(env, keys)
some subject in statement.subject
valid_subject(subject)
}
}
valid_subject(sub) if {
print("valid_subject")
print("sub.digest[digestType]:", sub.digest[digestType])
print("digest", digest)
sub.digest[digestType] == digest
print("digest matches")
valid_subject_name(sub.name)
}
valid_subject_name(name) if {
input.canonical
print("is canonical, ignoring name")
}
valid_subject_name(name) if {
not input.canonical
print("valid_subject_name...")
print("name:", name)
print("input.purl:", input.purl)
name == input.purl
print("name match")
}

View File

@@ -0,0 +1,25 @@
package docker
import rego.v1
config := {"keys": []}
envs := [{"env": "test"}]
purl := "pkg:docker/library/alpine:1.2.3"
statement := {"subject": [{"name": purl, "digest": {"sha256": "dea014f47cd49d694d3a68564eb9e6ae38a7ee9624fd52ec05ccbef3f3fab8a0"}}]}
input_digest := "sha256:dea014f47cd49d694d3a68564eb9e6ae38a7ee9624fd52ec05ccbef3f3fab8a0"
test_with_mock_data if {
allow with attestations.attestation as envs
with attestations.verify_envelope as statement
with input.digest as input_digest
with input.purl as purl
with input.canonical as false
}
layout_digest := "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"
outout_purl := "pkg:docker/test-image@test?platform=linux%2Famd64"
test_with_signed_oci_layout if {
allow with input.digest as layout_digest
with input.purl as outout_purl
with input.canonical as false
}

16
test/testdata/local-policy/mapping.yaml vendored Normal file
View File

@@ -0,0 +1,16 @@
# map repos to policies
version: v1
kind: policy-mapping
policies:
- origin:
domain: docker.io
prefix: library/
name: test-images
description: Local test images
location: doi
mirrors:
- name: test-images
mirror:
domains: [localhost:5001]
prefix: ""

48
test/testdata/vsa.json vendored Normal file
View File

@@ -0,0 +1,48 @@
{
"payloadType": "application/vnd.in-toto+json",
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3ZlcmlmaWNhdGlvbl9zdW1tYXJ5L3YwLjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicGtnOmRvY2tlci90ZXN0LWltYWdlQHRlc3Q/cGxhdGZvcm09bGludXglMkZhbWQ2NCIsImRpZ2VzdCI6eyJzaGEyNTYiOiJkYThiMTkwNjY1OTU2ZWEwNzg5MGEwMjczZTJhOWM5NmJmZTI5MTY2MmYwOGUyODYwZTg2OGVlZjY5YzM0NjIwIn19XSwicHJlZGljYXRlIjp7InZlcmlmaWVyIjp7ImlkIjoiaHR0cHM6Ly9kb2NrZXIuY29tIn0sInRpbWVWZXJpZmllZCI6IjIwMjQtMDQtMTlUMTY6MjU6MTFaIiwicmVzb3VyY2VVcmkiOiJwa2c6ZG9ja2VyL3Rlc3QtaW1hZ2VAdGVzdD9kaWdlc3Q9c2hhMjU2JTNBZGE4YjE5MDY2NTk1NmVhMDc4OTBhMDI3M2UyYTljOTZiZmUyOTE2NjJmMDhlMjg2MGU4NjhlZWY2OWMzNDYyMFx1MDAyNnBsYXRmb3JtPWxpbnV4JTJGYW1kNjQiLCJwb2xpY3kiOnsidXJpIjoiaHR0cHM6Ly9kb2NrZXIuY29tL2xpYnJhcnkvcG9saWN5In0sImlucHV0QXR0ZXN0YXRpb25zIjpudWxsLCJ2ZXJpZmljYXRpb25SZXN1bHQiOiJQQVNTRUQiLCJ2ZXJpZmllZExldmVscyI6WyJTTFNBX0JVSUxEX0xFVkVMXzMiXX19",
"signatures": [
{
"keyid": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4",
"sig": "MEUCIDtkq1k1x7exFlKmRx8asV0a/nycXxdw71pRvbtVltprAiEA4aV45oCN3tfIf4KbQaRH3RO0wCbemP3woL0gK3lkscg=",
"extension": {
"kind": "application/vnd.docker.attestation-verification.v1+json",
"ext": {
"tl": {
"kind": "Rekor",
"data": {
"body": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiODU4MTM2MWUzMDcwZjhjNzUxZTk4ODE3MWFmYzFhMjE1ZGJlZjQxMzk2NTMxOTJiZDMyYWIwYzE3ZDM2YzM1In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJRHRrcTFrMXg3ZXhGbEttUng4YXNWMGEvbnljWHhkdzcxcFJ2YnRWbHRwckFpRUE0YVY0NW9DTjN0ZklmNEtiUWFSSDNSTzB3Q2JlbVAzd29MMGdLM2xrc2NnPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTkRWRU5EUVdFclowRjNTVUpCWjBsQ1FWUkJTMEpuWjNGb2EycFBVRkZSUkVGcVFreE5WV3QzVW5kWlJGWlJVVVJGTUVKb1RVZE5lVTlVV1hjS1RXcFpNazVFVlROUFZHeHBUVzFGZVU5VVl6Vk5WRTAwVG5wb2JFOUVSbWxOUjBac1dtMWFiVTF0UlhkWmVrMTNUVmRWTlU1NlNYcE5iVmt6VFZSa2JBcE5WRkV3VFVSS2JVMHlWVEJOUWpSWVJGUkpNRTFFVVhoUFZFVXlUV3BWZUUxV2IxaEVWRWt4VFVSUmVFOVVSVEpOYWxWNFRWWnZkMU42UmtwTlJXTkhDa0V4VlVWQmVFNUJXVlJDYWsxcWF6Sk5SRWt5VG1wUk1VNTZhelZaYWtwb1RXcHJNMDlVUlhwUFJHTTBXbFJuZUZscVFtaGFWMXB0V21wS2FFMUhUWG9LVFVSR2JFOVVZM2xOZWtwdFRucEZNMXBVUlRCT1JFRjVXbXBPYkU1RVFscE5RazFIUW5seFIxTk5ORGxCWjBWSFEwTnhSMU5OTkRsQmQwVklRVEJKUVFwQ1NVSTVkSGM1V1hSMmJWTkVjbFpaTlc1M1pUUm9ZakV2UVVsV1ZHWlJhbmxsU1c1MlYzTmpVR3hyVWpaTmVIQXdZM1ptVms1dVZWbElkRGczYm1kUkNsbE9hazFJYUdSTk5qQTJSbmh2YjA1eU9USkVjVE5sYW1kWlRYZG5XVUYzUkdkWlJGWlNNRkJCVVVndlFrRlJSRUZuWlVGTlFrMUhRVEZWWkVwUlVVMEtUVUZ2UjBORGMwZEJVVlZHUW5kTlJFMUJkMGRCTVZWa1JYZEZRaTkzVVVOTlFVRjNVM2RaUkZaU01GSkNSVkYzVVc5S1FWbFVRbXBOYW1zeVRVUkpNZ3BPYWxFeFRucHJOVmxxU21oTmFtc3pUMVJGZWs5RVl6UmFWR2Q0V1dwQ2FGcFhXbTFhYWtwb1RVZE5lazFFUm14UFZHTjVUWHBLYlU1NlJUTmFWRVV3Q2s1RVFYbGFhazVzVGtSQlMwSm5aM0ZvYTJwUFVGRlJSRUZuVGtsQlJFSkdRV2xGUVRaVk5pdDBjRTFGWlhCT1JYSnpMM0JPYW1kdlFWbzNWM1JtVlZRS1kxTmxiSEp3VTJsTFdqQjNPRkpCUTBsRVlWUXpWbXBETmpoTVlVeEtTbWxDWkhSbFYzTnVNMlYyWTBST1NFZFVjMmRNVDFwcGNXdDRTakJsQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=",
"integratedTime": 1713543910,
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
"logIndex": 87076941,
"verification": {
"inclusionProof": {
"checkpoint": "rekor.sigstore.dev - 2605736670972794746\n82913511\nBaX/1AgGwb1Cxm3NpleUYCBgWPt6pj2hq/xGYEmZl/c=\n\n— rekor.sigstore.dev wNI9ajBFAiEAsrpsGf41VBvC0uci2OPfaB3y+gm4ri+Pw2892vlCPqECIAbjtBCF3fJBOxxTDcXuAHk5F4D6TmLQCJ5glIkDlCuZ\n",
"hashes": [
"1f87b20218e09db0b277220c8b32a32ca917775b1561f9ad63afd755e4ddbf62",
"508e07a2fc0a8ac9c00b908c7ac3504dee1af18c6499283619b6f00ec0369145",
"7828aaacc3c0f3e4ef486d002073e9147305b30fc9680b599ecd0e9d5473160f",
"c11b0503e880276f1d6547843000b62f1503e34d440c2cfe67c822fa9f1dec01",
"8fa564c7c1f550540d70ac5469c77b63b6eee30dbfdf935a1d27f01b90852da7",
"de82a665a685584f93b92f15cd310bdd28a33faf5b592dea5c25e6f24ead3cf7",
"5ccc2074cecc5e7e14bbc684ff420ec3d6658d2b54d95727b740761080dd4a8e",
"690cf19a242b1dc4e4eb95c7249dda2bae94d793a85b70b83080c1375a736199",
"421731b5a60ac1a92c79624f4d1b64b465ea0c36e4d67ba99f88157910bd4bec",
"de48cf7a09019cd05ee06aa911cb8f103382632fa1b363921d4c4a19d6b1026e",
"b66caf5e8b1f7b1fcd5a06ad2371b53dc1ae6524eb4775aed563ba31d565b426",
"0c60918bcf6f554648566bcad8014e99e32a101ea7f91f7a65efaf8d601906fc",
"f7c7a7ccc682fb1e6808cbc8650039cfcbeed9aa4330216f13ff77e4d7ee3f0f"
],
"logIndex": 82913510,
"rootHash": "05a5ffd40806c1bd42c66dcda6579460206058fb7aa63da1abfc4660499997f7",
"treeSize": 82913511
},
"signedEntryTimestamp": "MEQCIHkKq/L6PD0eCsNch7D89YpUScYxzW9rOEumpzBhnLRCAiBQkdPEHa5LFgvB7KwYrvs6xT29AWuIFk2qns3cmdk6IA=="
}
}
}
}
}
}
]
}