Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4778d3de6a | ||
|
|
a4ac09e7da | ||
|
|
9250552c5b | ||
|
|
2acc30693f | ||
|
|
5db1b5c4c1 | ||
|
|
6f94d59a96 | ||
|
|
95319494b5 | ||
|
|
64046df6f8 | ||
|
|
57b6df0ab5 | ||
|
|
857be568b5 | ||
|
|
9d39c5ae3d |
37
go.mod
37
go.mod
@@ -3,8 +3,8 @@ module github.com/docker/attest
|
|||||||
go 1.22.5
|
go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver/v3 v3.2.1
|
github.com/Masterminds/semver/v3 v3.3.0
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.28
|
github.com/aws/aws-sdk-go-v2/config v1.27.31
|
||||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8
|
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8
|
||||||
github.com/containerd/platforms v0.2.1
|
github.com/containerd/platforms v0.2.1
|
||||||
github.com/distribution/reference v0.6.0
|
github.com/distribution/reference v0.6.0
|
||||||
@@ -22,9 +22,9 @@ require (
|
|||||||
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.8
|
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.8
|
||||||
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.8
|
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.8
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/testcontainers/testcontainers-go/modules/registry v0.32.0
|
github.com/testcontainers/testcontainers-go/modules/registry v0.33.0
|
||||||
github.com/theupdateframework/go-tuf/v2 v2.0.0
|
github.com/theupdateframework/go-tuf/v2 v2.0.0
|
||||||
google.golang.org/api v0.192.0
|
google.golang.org/api v0.194.0
|
||||||
sigs.k8s.io/yaml v1.4.0
|
sigs.k8s.io/yaml v1.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,9 +32,9 @@ require (
|
|||||||
replace github.com/google/go-containerregistry => github.com/docker/go-containerregistry v0.0.0-20240808132857-c8bfc44af7c8
|
replace github.com/google/go-containerregistry => github.com/docker/go-containerregistry v0.0.0-20240808132857-c8bfc44af7c8
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.115.0 // indirect
|
cloud.google.com/go v0.115.1 // indirect
|
||||||
cloud.google.com/go/auth v0.8.1 // indirect
|
cloud.google.com/go/auth v0.9.1 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||||
cloud.google.com/go/iam v1.1.12 // indirect
|
cloud.google.com/go/iam v1.1.12 // indirect
|
||||||
cloud.google.com/go/kms v1.18.4 // indirect
|
cloud.google.com/go/kms v1.18.4 // indirect
|
||||||
@@ -42,13 +42,12 @@ require (
|
|||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.12.3 // indirect
|
|
||||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.28 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.30 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
|
||||||
@@ -60,7 +59,7 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 // indirect
|
||||||
github.com/aws/smithy-go v1.20.4 // indirect
|
github.com/aws/smithy-go v1.20.4 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
@@ -161,7 +160,7 @@ require (
|
|||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||||
github.com/testcontainers/testcontainers-go v0.32.0 // indirect
|
github.com/testcontainers/testcontainers-go v0.33.0 // indirect
|
||||||
github.com/theupdateframework/go-tuf v0.7.0 // indirect
|
github.com/theupdateframework/go-tuf v0.7.0 // indirect
|
||||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||||
@@ -182,19 +181,19 @@ require (
|
|||||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
golang.org/x/crypto v0.25.0 // indirect
|
golang.org/x/crypto v0.26.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
golang.org/x/mod v0.19.0 // indirect
|
golang.org/x/mod v0.19.0 // indirect
|
||||||
golang.org/x/net v0.27.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
golang.org/x/oauth2 v0.22.0 // indirect
|
golang.org/x/oauth2 v0.22.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
golang.org/x/term v0.22.0 // indirect
|
golang.org/x/term v0.23.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.17.0 // indirect
|
||||||
golang.org/x/time v0.6.0 // indirect
|
golang.org/x/time v0.6.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf // indirect
|
google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
google.golang.org/grpc v1.65.0 // indirect
|
google.golang.org/grpc v1.65.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
74
go.sum
74
go.sum
@@ -1,10 +1,10 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
|
||||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
|
||||||
cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo=
|
cloud.google.com/go/auth v0.9.1 h1:+pMtLEV2k0AXKvs/tGZojuj6QaioxfUjOpMsG5Gtx+w=
|
||||||
cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc=
|
cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||||
cloud.google.com/go/iam v1.1.12 h1:JixGLimRrNGcxvJEQ8+clfLxPlbeZA6MuRJ+qJNQ5Xw=
|
cloud.google.com/go/iam v1.1.12 h1:JixGLimRrNGcxvJEQ8+clfLxPlbeZA6MuRJ+qJNQ5Xw=
|
||||||
@@ -60,12 +60,10 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
|
|||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
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/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/Microsoft/hcsshim v0.12.3 h1:LS9NXqXhMoqNCplK1ApmVSfB4UnVLRDWRapB6EIlxE0=
|
|
||||||
github.com/Microsoft/hcsshim v0.12.3/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||||
@@ -106,10 +104,10 @@ github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU
|
|||||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
|
github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
|
github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.28 h1:OTxWGW/91C61QlneCtnD62NLb4W616/NM1jA8LhJqbg=
|
github.com/aws/aws-sdk-go-v2/config v1.27.31 h1:kxBoRsjhT3pq0cKthgj6RU6bXTm/2SgdoUMyrVw0rAI=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.28/go.mod h1:uzVRVtJSU5EFv6Fu82AoVFKozJi2ZCY6WRCXj06rbvs=
|
github.com/aws/aws-sdk-go-v2/config v1.27.31/go.mod h1:z04nZdSWFPaDwK3DdJOG2r+scLQzMYuJeW0CujEm9FM=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.28 h1:m8+AHY/ND8CMHJnPoH7PJIRakWGa4gbfbxuY9TGTUXM=
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.28/go.mod h1:6TF7dSc78ehD1SL6KpRIPKMA1GyyWflIkjqg+qmf4+c=
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
|
||||||
@@ -132,8 +130,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/
|
|||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 h1:iAckBT2OeEK/kBDyN/jDtpEExhjeeA/Im2q4X0rJZT8=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.4/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0=
|
||||||
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
|
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
|
||||||
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
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 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M=
|
||||||
@@ -615,10 +613,10 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
|
|||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
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 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
|
||||||
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||||
github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME=
|
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
|
||||||
github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E=
|
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
|
||||||
github.com/testcontainers/testcontainers-go/modules/registry v0.32.0 h1:b4JSSEhbGXGtQA1WXJ3BlbkVjjdXoFTtBPvLRe+9Y9Y=
|
github.com/testcontainers/testcontainers-go/modules/registry v0.33.0 h1:rpQS5KcFpyRPM3xVKERuXDqUcE5xjwE8MQUgmKVkL0o=
|
||||||
github.com/testcontainers/testcontainers-go/modules/registry v0.32.0/go.mod h1:bX3JF8vQkv3D2frmrDyQd0GCQIQGl5nPG91xUvl7UhA=
|
github.com/testcontainers/testcontainers-go/modules/registry v0.33.0/go.mod h1:qr3nJgBZ2ovQva6vadXchwi786/mBBDzhBPbrmWkYIE=
|
||||||
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
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/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 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
|
||||||
@@ -694,8 +692,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
@@ -727,8 +725,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||||
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
@@ -771,15 +769,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.8.0/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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.24.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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -787,8 +785,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.6.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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -809,19 +807,19 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
google.golang.org/api v0.192.0 h1:PljqpNAfZaaSpS+TnANfnNAXKdzHM/B9bKhwRlo7JP0=
|
google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s=
|
||||||
google.golang.org/api v0.192.0/go.mod h1:9VcphjvAxPKLmSxVSzPlSRXy/5ARMEw5bf58WoVXafQ=
|
google.golang.org/api v0.194.0/go.mod h1:AgvUFdojGANh3vI+P7EVnxj3AISHllxGCJSFmggmnd0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf h1:OqdXDEakZCVtDiZTjcxfwbHPCT11ycCEsTKesBVKvyY=
|
google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 h1:oLiyxGgE+rt22duwci1+TG7bg2/L1LQsXwfjPlmuJA0=
|
||||||
google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M=
|
google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142/go.mod h1:G11eXq53iI5Q+kyNOmCvnzBaxEA2Q/Ik5Tj7nqBE8j4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf h1:GillM0Ef0pkZPIB+5iO6SDK+4T9pf6TpaYR6ICD5rVE=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
|||||||
@@ -6,16 +6,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/attest/pkg/policy"
|
|
||||||
"github.com/docker/attest/pkg/signerverifier"
|
"github.com/docker/attest/pkg/signerverifier"
|
||||||
"github.com/docker/attest/pkg/tlog"
|
"github.com/docker/attest/pkg/tlog"
|
||||||
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UseMockTL = true
|
UseMockTL = true
|
||||||
UseMockKMS = true
|
UseMockKMS = true
|
||||||
UseMockPolicy = true
|
|
||||||
|
|
||||||
AWSRegion = "us-east-1"
|
AWSRegion = "us-east-1"
|
||||||
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
|
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
|
||||||
@@ -57,15 +55,6 @@ func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
|
|||||||
|
|
||||||
ctx := tlog.WithTL(context.Background(), tl)
|
ctx := tlog.WithTL(context.Background(), tl)
|
||||||
|
|
||||||
var policyEvaluator policy.Evaluator
|
|
||||||
if UseMockPolicy {
|
|
||||||
policyEvaluator = policy.GetMockPolicy()
|
|
||||||
} else {
|
|
||||||
policyEvaluator = policy.NewRegoEvaluator(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = policy.WithPolicyEvaluator(ctx, policyEvaluator)
|
|
||||||
|
|
||||||
var signer dsse.SignerVerifier
|
var signer dsse.SignerVerifier
|
||||||
var err error
|
var err error
|
||||||
if UseMockKMS {
|
if UseMockKMS {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ func ExampleVerify_remote() {
|
|||||||
LocalTargetsDir: filepath.Join(home, ".docker", "policy"), // location to store policy files downloaded from TUF
|
LocalTargetsDir: filepath.Join(home, ".docker", "policy"), // location to store policy files downloaded from TUF
|
||||||
LocalPolicyDir: "", // overrides TUF policy for local policy files if set
|
LocalPolicyDir: "", // overrides TUF policy for local policy files if set
|
||||||
PolicyID: "", // set to ignore policy mapping and select a policy by id
|
PolicyID: "", // set to ignore policy mapping and select a policy by id
|
||||||
|
DisableTUF: false, // set to disable TUF and rely on local policy files
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := oci.ParseImageSpec(image, oci.WithPlatform(platform))
|
src, err := oci.ParseImageSpec(image, oci.WithPlatform(platform))
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/docker/attest/pkg/attestation"
|
"github.com/docker/attest/pkg/attestation"
|
||||||
"github.com/docker/attest/pkg/oci"
|
"github.com/docker/attest/pkg/oci"
|
||||||
"github.com/docker/attest/pkg/policy"
|
"github.com/docker/attest/pkg/policy"
|
||||||
"github.com/docker/attest/pkg/tuf"
|
|
||||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||||
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -28,7 +27,6 @@ var (
|
|||||||
|
|
||||||
func TestSignVerifyOCILayout(t *testing.T) {
|
func TestSignVerifyOCILayout(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
ctx = tuf.WithDownloader(ctx, tuf.NewMockTufClient(EmptyPolicyDir, test.CreateTempDir(t, "", "tuf-dest")))
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -45,6 +43,7 @@ func TestSignVerifyOCILayout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
policyOpts := &policy.Options{
|
policyOpts := &policy.Options{
|
||||||
LocalPolicyDir: PassPolicyDir,
|
LocalPolicyDir: PassPolicyDir,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -17,68 +17,100 @@ import (
|
|||||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.Options) (result *VerificationResult, err error) {
|
type Verifier interface {
|
||||||
// so that we can resolve mapping from the image name earlier
|
Verify(ctx context.Context, src *oci.ImageSpec) (result *VerificationResult, err error)
|
||||||
detailsResolver, err := policy.CreateImageDetailsResolver(src)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create image details resolver: %w", err)
|
type tufVerifier struct {
|
||||||
}
|
opts *policy.Options
|
||||||
err = populateDefaultOptions(opts)
|
tufClient tuf.Downloader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVerifier(opts *policy.Options) (Verifier, error) {
|
||||||
|
err := populateDefaultOptions(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var tufClient tuf.Downloader
|
||||||
tufClient, ok := tuf.GetDownloader(ctx)
|
if !opts.DisableTUF {
|
||||||
if !ok {
|
|
||||||
tufClient, err = tuf.NewClient(opts.TUFClientOptions)
|
tufClient, err = tuf.NewClient(opts.TUFClientOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create TUF client: %w", err)
|
return nil, fmt.Errorf("failed to create TUF client: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return &tufVerifier{
|
||||||
|
opts: opts,
|
||||||
|
tufClient: tufClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
pctx, err := policy.ResolvePolicy(ctx, tufClient, detailsResolver, opts)
|
func (verifier *tufVerifier) Verify(ctx context.Context, src *oci.ImageSpec) (result *VerificationResult, err error) {
|
||||||
|
// so that we can resolve mapping from the image name earlier
|
||||||
|
detailsResolver, err := policy.CreateImageDetailsResolver(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create image details resolver: %w", err)
|
||||||
|
}
|
||||||
|
imageName, err := detailsResolver.ImageName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve image name: %w", err)
|
||||||
|
}
|
||||||
|
policyResolver := policy.NewResolver(verifier.tufClient, verifier.opts)
|
||||||
|
resolvedPolicy, err := policyResolver.ResolvePolicy(ctx, imageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to resolve policy: %w", err)
|
return nil, fmt.Errorf("failed to resolve policy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pctx == nil {
|
if resolvedPolicy == nil {
|
||||||
return &VerificationResult{
|
return &VerificationResult{
|
||||||
Outcome: OutcomeNoPolicy,
|
Outcome: OutcomeNoPolicy,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
// this is overriding the mapping with a referrers config. Useful for testing if nothing else
|
// this is overriding the mapping with a referrers config. Useful for testing if nothing else
|
||||||
if opts.ReferrersRepo != "" {
|
if verifier.opts.ReferrersRepo != "" {
|
||||||
pctx.Mapping.Attestations = &config.AttestationConfig{
|
resolvedPolicy.Mapping.Attestations = &config.AttestationConfig{
|
||||||
Repo: opts.ReferrersRepo,
|
Repo: verifier.opts.ReferrersRepo,
|
||||||
Style: config.AttestationStyleReferrers,
|
Style: config.AttestationStyleReferrers,
|
||||||
}
|
}
|
||||||
} else if opts.AttestationStyle == config.AttestationStyleAttached {
|
} else if verifier.opts.AttestationStyle == config.AttestationStyleAttached {
|
||||||
pctx.Mapping.Attestations = &config.AttestationConfig{
|
resolvedPolicy.Mapping.Attestations = &config.AttestationConfig{
|
||||||
Repo: opts.ReferrersRepo,
|
Repo: verifier.opts.ReferrersRepo,
|
||||||
Style: config.AttestationStyleAttached,
|
Style: config.AttestationStyleAttached,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// because we have a mapping now, we can select a resolver based on its contents (ie. referrers or attached)
|
// because we have a mapping now, we can select a resolver based on its contents (ie. referrers or attached)
|
||||||
resolver, err := policy.CreateAttestationResolver(detailsResolver, pctx.Mapping)
|
resolver, err := policy.CreateAttestationResolver(detailsResolver, resolvedPolicy.Mapping)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create attestation resolver: %w", err)
|
return nil, fmt.Errorf("failed to create attestation resolver: %w", err)
|
||||||
}
|
}
|
||||||
result, err = VerifyAttestations(ctx, resolver, pctx)
|
evaluator := policy.NewRegoEvaluator(verifier.opts.Debug)
|
||||||
|
result, err = VerifyAttestations(ctx, resolver, evaluator, resolvedPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to evaluate policy: %w", err)
|
return nil, fmt.Errorf("failed to evaluate policy: %w", err)
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.Options) (result *VerificationResult, err error) {
|
||||||
|
verifier, err := NewVerifier(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return verifier.Verify(ctx, src)
|
||||||
|
}
|
||||||
|
|
||||||
func populateDefaultOptions(opts *policy.Options) (err error) {
|
func populateDefaultOptions(opts *policy.Options) (err error) {
|
||||||
|
if opts.LocalPolicyDir == "" && opts.DisableTUF {
|
||||||
|
return fmt.Errorf("local policy dir must be set if not using TUF")
|
||||||
|
}
|
||||||
if opts.LocalTargetsDir == "" {
|
if opts.LocalTargetsDir == "" {
|
||||||
opts.LocalTargetsDir, err = defaultLocalTargetsDir()
|
opts.LocalTargetsDir, err = defaultLocalTargetsDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if opts.DisableTUF && opts.TUFClientOptions != nil {
|
||||||
if opts.TUFClientOptions == nil {
|
return fmt.Errorf("TUF client options set but TUF disabled")
|
||||||
|
} else if opts.TUFClientOptions == nil && !opts.DisableTUF {
|
||||||
opts.TUFClientOptions = tuf.NewDockerDefaultClientOptions(opts.LocalTargetsDir)
|
opts.TUFClientOptions = tuf.NewDockerDefaultClientOptions(opts.LocalTargetsDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +120,6 @@ func populateDefaultOptions(opts *policy.Options) (err error) {
|
|||||||
if opts.ReferrersRepo != "" && opts.AttestationStyle != config.AttestationStyleReferrers {
|
if opts.ReferrersRepo != "" && opts.AttestationStyle != config.AttestationStyleReferrers {
|
||||||
return fmt.Errorf("referrers repo specified but attestation source not set to referrers")
|
return fmt.Errorf("referrers repo specified but attestation source not set to referrers")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +184,7 @@ func toVerificationResult(p *policy.Policy, input *policy.Input, result *policy.
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx *policy.Policy) (*VerificationResult, error) {
|
func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, evaluator policy.Evaluator, resolvedPolicy *policy.Policy) (*VerificationResult, error) {
|
||||||
desc, err := resolver.ImageDescriptor(ctx)
|
desc, err := resolver.ImageDescriptor(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get image descriptor: %w", err)
|
return nil, fmt.Errorf("failed to get image descriptor: %w", err)
|
||||||
@@ -168,7 +199,7 @@ func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pctx.ResolvedName != "" {
|
if resolvedPolicy.ResolvedName != "" {
|
||||||
// this means the name we have is not the one we want to use for policy evaluation
|
// this means the name we have is not the one we want to use for policy evaluation
|
||||||
// so we need to replace it with the one we resolved during policy resolution.
|
// so we need to replace it with the one we resolved during policy resolution.
|
||||||
// this can happen if the name is an alias for another image, e.g. if it is a mirror
|
// this can happen if the name is an alias for another image, e.g. if it is a mirror
|
||||||
@@ -177,7 +208,7 @@ func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx
|
|||||||
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
||||||
}
|
}
|
||||||
oldName := ref.Name()
|
oldName := ref.Name()
|
||||||
name = strings.Replace(name, oldName, pctx.ResolvedName, 1)
|
name = strings.Replace(name, oldName, resolvedPolicy.ResolvedName, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
ref, err := reference.ParseNormalizedNamed(name)
|
ref, err := reference.ParseNormalizedNamed(name)
|
||||||
@@ -209,16 +240,11 @@ func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx
|
|||||||
if tag != "" {
|
if tag != "" {
|
||||||
input.Tag = tag
|
input.Tag = tag
|
||||||
}
|
}
|
||||||
|
result, err := evaluator.Evaluate(ctx, resolver, resolvedPolicy, input)
|
||||||
evaluator, err := policy.GetPolicyEvaluator(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result, err := evaluator.Evaluate(ctx, resolver, pctx, input)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("policy evaluation failed: %w", err)
|
return nil, fmt.Errorf("policy evaluation failed: %w", err)
|
||||||
}
|
}
|
||||||
verificationResult, err := toVerificationResult(pctx, input, result)
|
verificationResult, err := toVerificationResult(resolvedPolicy, input, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to convert to policy result: %w", err)
|
return nil, fmt.Errorf("failed to convert to policy result: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func TestVerifyAttestations(t *testing.T) {
|
|||||||
{"policy ok", nil, nil},
|
{"policy ok", nil, nil},
|
||||||
{"policy error", fmt.Errorf("policy error"), fmt.Errorf("policy evaluation failed: policy error")},
|
{"policy error", fmt.Errorf("policy error"), fmt.Errorf("policy evaluation failed: policy error")},
|
||||||
}
|
}
|
||||||
|
ctx := context.Background()
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
mockPE := policy.MockPolicyEvaluator{
|
mockPE := policy.MockPolicyEvaluator{
|
||||||
@@ -54,8 +54,7 @@ func TestVerifyAttestations(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := policy.WithPolicyEvaluator(context.Background(), &mockPE)
|
_, err := VerifyAttestations(ctx, resolver, &mockPE, &policy.Policy{ResolvedName: ""})
|
||||||
_, err := VerifyAttestations(ctx, resolver, &policy.Policy{ResolvedName: ""})
|
|
||||||
if tc.expectedError != nil {
|
if tc.expectedError != nil {
|
||||||
if assert.Error(t, err) {
|
if assert.Error(t, err) {
|
||||||
assert.Equal(t, tc.expectedError.Error(), err.Error())
|
assert.Equal(t, tc.expectedError.Error(), err.Error())
|
||||||
@@ -69,8 +68,6 @@ func TestVerifyAttestations(t *testing.T) {
|
|||||||
|
|
||||||
func TestVSA(t *testing.T) {
|
func TestVSA(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
|
||||||
ctx = tuf.WithDownloader(ctx, tuf.NewMockTufClient(EmptyPolicyDir, test.CreateTempDir(t, "", "tuf-dest")))
|
|
||||||
// setup an image with signed attestations
|
// setup an image with signed attestations
|
||||||
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
||||||
|
|
||||||
@@ -93,6 +90,7 @@ func TestVSA(t *testing.T) {
|
|||||||
policyOpts := &policy.Options{
|
policyOpts := &policy.Options{
|
||||||
LocalPolicyDir: PassPolicyDir,
|
LocalPolicyDir: PassPolicyDir,
|
||||||
AttestationStyle: config.AttestationStyleAttached,
|
AttestationStyle: config.AttestationStyleAttached,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
results, err := Verify(ctx, spec, policyOpts)
|
results, err := Verify(ctx, spec, policyOpts)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -122,8 +120,6 @@ func TestVSA(t *testing.T) {
|
|||||||
|
|
||||||
func TestVerificationFailure(t *testing.T) {
|
func TestVerificationFailure(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
|
||||||
ctx = tuf.WithDownloader(ctx, tuf.NewMockTufClient(EmptyPolicyDir, test.CreateTempDir(t, "", "tuf-dest")))
|
|
||||||
// setup an image with signed attestations
|
// setup an image with signed attestations
|
||||||
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
||||||
|
|
||||||
@@ -146,6 +142,7 @@ func TestVerificationFailure(t *testing.T) {
|
|||||||
policyOpts := &policy.Options{
|
policyOpts := &policy.Options{
|
||||||
LocalPolicyDir: FailPolicyDir,
|
LocalPolicyDir: FailPolicyDir,
|
||||||
AttestationStyle: config.AttestationStyleAttached,
|
AttestationStyle: config.AttestationStyleAttached,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
results, err := Verify(ctx, spec, policyOpts)
|
results, err := Verify(ctx, spec, policyOpts)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -175,17 +172,14 @@ func TestVerificationFailure(t *testing.T) {
|
|||||||
|
|
||||||
func TestSignVerify(t *testing.T) {
|
func TestSignVerify(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
|
||||||
ctx = tuf.WithDownloader(ctx, tuf.NewMockTufClient(EmptyPolicyDir, test.CreateTempDir(t, "", "tuf-dest")))
|
|
||||||
// setup an image with signed attestations
|
// setup an image with signed attestations
|
||||||
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
signTL bool
|
signTL bool
|
||||||
policyDir string
|
policyDir string
|
||||||
imageName string
|
imageName string
|
||||||
|
|
||||||
expectedNonSuccess Outcome
|
expectedNonSuccess Outcome
|
||||||
}{
|
}{
|
||||||
{name: "happy path", signTL: true, policyDir: PassNoTLPolicyDir},
|
{name: "happy path", signTL: true, policyDir: PassNoTLPolicyDir},
|
||||||
@@ -223,6 +217,7 @@ func TestSignVerify(t *testing.T) {
|
|||||||
|
|
||||||
policyOpts := &policy.Options{
|
policyOpts := &policy.Options{
|
||||||
LocalPolicyDir: tc.policyDir,
|
LocalPolicyDir: tc.policyDir,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
results, err := Verify(ctx, spec, policyOpts)
|
results, err := Verify(ctx, spec, policyOpts)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -251,6 +246,8 @@ func TestDefaultOptions(t *testing.T) {
|
|||||||
attestationStyle config.AttestationStyle
|
attestationStyle config.AttestationStyle
|
||||||
referrersRepo string
|
referrersRepo string
|
||||||
expectedError string
|
expectedError string
|
||||||
|
disableTuf bool
|
||||||
|
localPolicyDir string
|
||||||
}{
|
}{
|
||||||
{name: "empty"},
|
{name: "empty"},
|
||||||
{name: "tufClient provided", tufOpts: &tuf.ClientOptions{MetadataSource: "a", TargetsSource: "b"}},
|
{name: "tufClient provided", tufOpts: &tuf.ClientOptions{MetadataSource: "a", TargetsSource: "b"}},
|
||||||
@@ -258,6 +255,8 @@ func TestDefaultOptions(t *testing.T) {
|
|||||||
{name: "attestationStyle provided", attestationStyle: config.AttestationStyleAttached},
|
{name: "attestationStyle provided", attestationStyle: config.AttestationStyleAttached},
|
||||||
{name: "referrersRepo provided", referrersRepo: "referrers"},
|
{name: "referrersRepo provided", referrersRepo: "referrers"},
|
||||||
{name: "referrersRepo provided with attached", referrersRepo: "referrers", attestationStyle: config.AttestationStyleAttached, expectedError: "referrers repo specified but attestation source not set to referrers"},
|
{name: "referrersRepo provided with attached", referrersRepo: "referrers", attestationStyle: config.AttestationStyleAttached, expectedError: "referrers repo specified but attestation source not set to referrers"},
|
||||||
|
{name: "tuf disabled and no local-policy-dir", disableTuf: true, expectedError: "local policy dir must be set if not using TUF"},
|
||||||
|
{name: "tuf disabled but options set", disableTuf: true, tufOpts: &tuf.ClientOptions{MetadataSource: "a", TargetsSource: "b"}, localPolicyDir: "foo", expectedError: "TUF client options set but TUF disabled"},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
@@ -269,6 +268,8 @@ func TestDefaultOptions(t *testing.T) {
|
|||||||
LocalTargetsDir: tc.localTargetsDir,
|
LocalTargetsDir: tc.localTargetsDir,
|
||||||
AttestationStyle: tc.attestationStyle,
|
AttestationStyle: tc.attestationStyle,
|
||||||
ReferrersRepo: tc.referrersRepo,
|
ReferrersRepo: tc.referrersRepo,
|
||||||
|
DisableTUF: tc.disableTuf,
|
||||||
|
LocalPolicyDir: tc.localPolicyDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = populateDefaultOptions(opts)
|
err = populateDefaultOptions(opts)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/docker/attest/pkg/config"
|
"github.com/docker/attest/pkg/config"
|
||||||
"github.com/docker/attest/pkg/oci"
|
"github.com/docker/attest/pkg/oci"
|
||||||
"github.com/docker/attest/pkg/policy"
|
"github.com/docker/attest/pkg/policy"
|
||||||
"github.com/docker/attest/pkg/tuf"
|
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/google/go-containerregistry/pkg/registry"
|
"github.com/google/go-containerregistry/pkg/registry"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
@@ -28,14 +27,11 @@ var (
|
|||||||
LocalPolicyAttached = filepath.Join("..", "..", "test", "testdata", "local-policy-attached")
|
LocalPolicyAttached = filepath.Join("..", "..", "test", "testdata", "local-policy-attached")
|
||||||
PassNoTLPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-no-tl")
|
PassNoTLPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-no-tl")
|
||||||
FailPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-fail")
|
FailPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy-fail")
|
||||||
EmptyTUFDir = filepath.Join("..", "..", "test", "testdata", "local-policy-no-policies")
|
|
||||||
TestTempDir = "attest-sign-test"
|
TestTempDir = "attest-sign-test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAttestationReferenceTypes(t *testing.T) {
|
func TestAttestationReferenceTypes(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
|
||||||
ctx = tuf.WithDownloader(ctx, tuf.NewMockTufClient(EmptyTUFDir, test.CreateTempDir(t, "", "tuf-dest")))
|
|
||||||
platforms := []string{"linux/amd64", "linux/arm64"}
|
platforms := []string{"linux/amd64", "linux/arm64"}
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -142,6 +138,7 @@ func TestAttestationReferenceTypes(t *testing.T) {
|
|||||||
|
|
||||||
policyOpts := &policy.Options{
|
policyOpts := &policy.Options{
|
||||||
LocalPolicyDir: LocalPolicy,
|
LocalPolicyDir: LocalPolicy,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if tc.referrersRepo != "" {
|
if tc.referrersRepo != "" {
|
||||||
@@ -185,7 +182,6 @@ func TestAttestationReferenceTypes(t *testing.T) {
|
|||||||
|
|
||||||
func TestReferencesInDifferentRepo(t *testing.T) {
|
func TestReferencesInDifferentRepo(t *testing.T) {
|
||||||
ctx, signer := test.Setup(t)
|
ctx, signer := test.Setup(t)
|
||||||
ctx = tuf.WithDownloader(ctx, tuf.NewMockTufClient(EmptyTUFDir, test.CreateTempDir(t, "", "tuf-dest")))
|
|
||||||
repoName := "repo"
|
repoName := "repo"
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -273,6 +269,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
|
|||||||
referencedImage := fmt.Sprintf("%s@%s", indexName, mf.Digest.String())
|
referencedImage := fmt.Sprintf("%s@%s", indexName, mf.Digest.String())
|
||||||
policyOpts := &policy.Options{
|
policyOpts := &policy.Options{
|
||||||
LocalPolicyDir: PassPolicyDir,
|
LocalPolicyDir: PassPolicyDir,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
src, err := oci.ParseImageSpec(referencedImage)
|
src, err := oci.ParseImageSpec(referencedImage)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -2,29 +2,10 @@ package policy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/docker/attest/pkg/attestation"
|
"github.com/docker/attest/pkg/attestation"
|
||||||
)
|
)
|
||||||
|
|
||||||
type policyEvaluatorCtxKeyType struct{}
|
|
||||||
|
|
||||||
var PolicyEvaluatorCtxKey policyEvaluatorCtxKeyType
|
|
||||||
|
|
||||||
// sets PolicyEvaluator in context.
|
|
||||||
func WithPolicyEvaluator(ctx context.Context, pe Evaluator) context.Context {
|
|
||||||
return context.WithValue(ctx, PolicyEvaluatorCtxKey, pe)
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set.
|
|
||||||
func GetPolicyEvaluator(ctx context.Context) (Evaluator, error) {
|
|
||||||
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(Evaluator)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("no policy evaluator client set on context (set one with policy.WithPolicyEvaluator)")
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Evaluator interface {
|
type Evaluator interface {
|
||||||
Evaluate(ctx context.Context, resolver attestation.Resolver, pctx *Policy, input *Input) (*Result, error)
|
Evaluate(ctx context.Context, resolver attestation.Resolver, pctx *Policy, input *Input) (*Result, error)
|
||||||
}
|
}
|
||||||
|
|||||||
65
pkg/policy/match.go
Normal file
65
pkg/policy/match.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package policy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/attest/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type matchType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
matchTypePolicy matchType = "policy"
|
||||||
|
matchTypeMatchNoPolicy matchType = "match_no_policy"
|
||||||
|
matchTypeNoMatch matchType = "no_match"
|
||||||
|
)
|
||||||
|
|
||||||
|
type policyMatch struct {
|
||||||
|
matchType matchType
|
||||||
|
policy *config.PolicyMapping
|
||||||
|
rule *config.PolicyRule
|
||||||
|
matchedName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPolicyMatch(imageName string, mappings *config.PolicyMappings) (*policyMatch, error) {
|
||||||
|
if mappings == nil {
|
||||||
|
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
|
||||||
|
}
|
||||||
|
return findPolicyMatchImpl(imageName, mappings, make(map[*config.PolicyRule]bool))
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPolicyMatchImpl(imageName string, mappings *config.PolicyMappings, matched map[*config.PolicyRule]bool) (*policyMatch, error) {
|
||||||
|
for _, rule := range mappings.Rules {
|
||||||
|
if rule.Pattern.MatchString(imageName) {
|
||||||
|
switch {
|
||||||
|
case rule.PolicyID == "" && rule.Replacement == "":
|
||||||
|
return nil, fmt.Errorf("rule %s has neither policy-id nor rewrite", rule.Pattern)
|
||||||
|
case rule.PolicyID != "" && rule.Replacement != "":
|
||||||
|
return nil, fmt.Errorf("rule %s has both policy-id and rewrite", rule.Pattern)
|
||||||
|
case rule.PolicyID != "":
|
||||||
|
policy := mappings.Policies[rule.PolicyID]
|
||||||
|
if policy != nil {
|
||||||
|
return &policyMatch{
|
||||||
|
matchType: matchTypePolicy,
|
||||||
|
policy: policy,
|
||||||
|
rule: rule,
|
||||||
|
matchedName: imageName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &policyMatch{
|
||||||
|
matchType: matchTypeMatchNoPolicy,
|
||||||
|
rule: rule,
|
||||||
|
matchedName: imageName,
|
||||||
|
}, nil
|
||||||
|
case rule.Replacement != "":
|
||||||
|
if matched[rule] {
|
||||||
|
return nil, fmt.Errorf("rewrite loop detected")
|
||||||
|
}
|
||||||
|
matched[rule] = true
|
||||||
|
imageName = rule.Pattern.ReplaceAllString(imageName, rule.Replacement)
|
||||||
|
return findPolicyMatchImpl(imageName, mappings, matched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
|
||||||
|
}
|
||||||
@@ -1,248 +1,13 @@
|
|||||||
package policy
|
package policy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/attest/internal/util"
|
|
||||||
"github.com/docker/attest/pkg/attestation"
|
"github.com/docker/attest/pkg/attestation"
|
||||||
"github.com/docker/attest/pkg/config"
|
"github.com/docker/attest/pkg/config"
|
||||||
"github.com/docker/attest/pkg/oci"
|
"github.com/docker/attest/pkg/oci"
|
||||||
"github.com/docker/attest/pkg/tuf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resolveLocalPolicy(opts *Options, mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
|
|
||||||
if opts.LocalPolicyDir == "" {
|
|
||||||
return nil, fmt.Errorf("local policy dir not set")
|
|
||||||
}
|
|
||||||
var URI string
|
|
||||||
var digest map[string]string
|
|
||||||
files := make([]*File, 0, len(mapping.Files))
|
|
||||||
for _, f := range mapping.Files {
|
|
||||||
filename := f.Path
|
|
||||||
filePath := path.Join(opts.LocalPolicyDir, 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, &File{
|
|
||||||
Path: filename,
|
|
||||||
Content: fileContents,
|
|
||||||
})
|
|
||||||
// if the file is a policy file, store the URI and digest
|
|
||||||
if filepath.Ext(filename) == ".rego" {
|
|
||||||
// TODO: support multiple rego files, need some way to identify the main policy file
|
|
||||||
if URI != "" {
|
|
||||||
return nil, fmt.Errorf("multiple policy files found in policy mapping")
|
|
||||||
}
|
|
||||||
URI = filePath
|
|
||||||
digest = map[string]string{"sha256": util.SHA256Hex(fileContents)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if URI == "" {
|
|
||||||
return nil, fmt.Errorf("no policy file found in policy mapping")
|
|
||||||
}
|
|
||||||
policy := &Policy{
|
|
||||||
InputFiles: files,
|
|
||||||
Mapping: mapping,
|
|
||||||
URI: URI,
|
|
||||||
Digest: digest,
|
|
||||||
}
|
|
||||||
if imageName != matchedName {
|
|
||||||
policy.ResolvedName = matchedName
|
|
||||||
}
|
|
||||||
return policy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveTUFPolicy(opts *Options, tufClient tuf.Downloader, mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
|
|
||||||
var URI string
|
|
||||||
var digest map[string]string
|
|
||||||
files := make([]*File, 0, len(mapping.Files))
|
|
||||||
for _, f := range mapping.Files {
|
|
||||||
filename := f.Path
|
|
||||||
file, err := tufClient.DownloadTarget(filename, filepath.Join(opts.LocalTargetsDir, filename))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
|
|
||||||
}
|
|
||||||
files = append(files, &File{
|
|
||||||
Path: filename,
|
|
||||||
Content: file.Data,
|
|
||||||
})
|
|
||||||
// if the file is a policy file, store the URI and digest
|
|
||||||
if filepath.Ext(filename) == ".rego" {
|
|
||||||
// TODO: support multiple rego files, need some way to identify the main policy file
|
|
||||||
if URI != "" {
|
|
||||||
return nil, fmt.Errorf("multiple policy files found in policy mapping")
|
|
||||||
}
|
|
||||||
URI = file.TargetURI
|
|
||||||
digest = map[string]string{"sha256": file.Digest}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if URI == "" {
|
|
||||||
return nil, fmt.Errorf("no policy file found in policy mapping")
|
|
||||||
}
|
|
||||||
policy := &Policy{
|
|
||||||
InputFiles: files,
|
|
||||||
Mapping: mapping,
|
|
||||||
URI: URI,
|
|
||||||
Digest: digest,
|
|
||||||
}
|
|
||||||
if imageName != matchedName {
|
|
||||||
policy.ResolvedName = matchedName
|
|
||||||
}
|
|
||||||
return policy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type matchType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
matchTypePolicy matchType = "policy"
|
|
||||||
matchTypeMatchNoPolicy matchType = "match_no_policy"
|
|
||||||
matchTypeNoMatch matchType = "no_match"
|
|
||||||
)
|
|
||||||
|
|
||||||
type policyMatch struct {
|
|
||||||
matchType matchType
|
|
||||||
policy *config.PolicyMapping
|
|
||||||
rule *config.PolicyRule
|
|
||||||
matchedName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPolicyMatch(imageName string, mappings *config.PolicyMappings) (*policyMatch, error) {
|
|
||||||
if mappings == nil {
|
|
||||||
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
|
|
||||||
}
|
|
||||||
return findPolicyMatchImpl(imageName, mappings, make(map[*config.PolicyRule]bool))
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPolicyMatchImpl(imageName string, mappings *config.PolicyMappings, matched map[*config.PolicyRule]bool) (*policyMatch, error) {
|
|
||||||
for _, rule := range mappings.Rules {
|
|
||||||
if rule.Pattern.MatchString(imageName) {
|
|
||||||
switch {
|
|
||||||
case rule.PolicyID == "" && rule.Replacement == "":
|
|
||||||
return nil, fmt.Errorf("rule %s has neither policy-id nor rewrite", rule.Pattern)
|
|
||||||
case rule.PolicyID != "" && rule.Replacement != "":
|
|
||||||
return nil, fmt.Errorf("rule %s has both policy-id and rewrite", rule.Pattern)
|
|
||||||
case rule.PolicyID != "":
|
|
||||||
policy := mappings.Policies[rule.PolicyID]
|
|
||||||
if policy != nil {
|
|
||||||
return &policyMatch{
|
|
||||||
matchType: matchTypePolicy,
|
|
||||||
policy: policy,
|
|
||||||
rule: rule,
|
|
||||||
matchedName: imageName,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &policyMatch{
|
|
||||||
matchType: matchTypeMatchNoPolicy,
|
|
||||||
rule: rule,
|
|
||||||
matchedName: imageName,
|
|
||||||
}, nil
|
|
||||||
case rule.Replacement != "":
|
|
||||||
if matched[rule] {
|
|
||||||
return nil, fmt.Errorf("rewrite loop detected")
|
|
||||||
}
|
|
||||||
matched[rule] = true
|
|
||||||
imageName = rule.Pattern.ReplaceAllString(imageName, rule.Replacement)
|
|
||||||
return findPolicyMatchImpl(imageName, mappings, matched)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolvePolicyByID(opts *Options, tufClient tuf.Downloader) (*Policy, error) {
|
|
||||||
if opts.PolicyID != "" {
|
|
||||||
localMappings, err := config.LoadLocalMappings(opts.LocalPolicyDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
|
|
||||||
}
|
|
||||||
if localMappings != nil {
|
|
||||||
policy := localMappings.Policies[opts.PolicyID]
|
|
||||||
if policy != nil {
|
|
||||||
return resolveLocalPolicy(opts, policy, "", "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// must check tuf
|
|
||||||
tufMappings, err := config.LoadTUFMappings(tufClient, opts.LocalTargetsDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load tuf policy mappings by id: %w", err)
|
|
||||||
}
|
|
||||||
policy := tufMappings.Policies[opts.PolicyID]
|
|
||||||
if policy != nil {
|
|
||||||
return resolveTUFPolicy(opts, tufClient, policy, "", "")
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("policy with id %s not found", opts.PolicyID)
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResolvePolicy(ctx context.Context, tufClient tuf.Downloader, detailsResolver oci.ImageDetailsResolver, opts *Options) (*Policy, error) {
|
|
||||||
p, err := resolvePolicyByID(opts, tufClient)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to resolve policy by id: %w", err)
|
|
||||||
}
|
|
||||||
if p != nil {
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
imageName, err := detailsResolver.ImageName(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get image name: %w", err)
|
|
||||||
}
|
|
||||||
imageName, err = normalizeImageName(imageName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
|
||||||
}
|
|
||||||
localMappings, err := config.LoadLocalMappings(opts.LocalPolicyDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
|
|
||||||
}
|
|
||||||
match, err := findPolicyMatch(imageName, localMappings)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if match.matchType == matchTypePolicy {
|
|
||||||
return resolveLocalPolicy(opts, match.policy, imageName, match.matchedName)
|
|
||||||
}
|
|
||||||
// must check tuf
|
|
||||||
tufMappings, err := config.LoadTUFMappings(tufClient, opts.LocalTargetsDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load tuf policy mappings as fallback: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's a mirror of a tuf policy
|
|
||||||
if match.matchType == matchTypeMatchNoPolicy {
|
|
||||||
for _, mapping := range tufMappings.Policies {
|
|
||||||
if mapping.ID == match.rule.PolicyID {
|
|
||||||
return resolveTUFPolicy(opts, tufClient, mapping, imageName, match.matchedName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to resolve a tuf policy directly
|
|
||||||
match, err = findPolicyMatch(imageName, tufMappings)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if match.matchType == matchTypePolicy {
|
|
||||||
return resolveTUFPolicy(opts, tufClient, match.policy, imageName, match.matchedName)
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeImageName(imageName string) (string, error) {
|
|
||||||
named, err := reference.ParseNormalizedNamed(imageName)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to parse image name: %w", err)
|
|
||||||
}
|
|
||||||
return named.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateImageDetailsResolver(imageSource *oci.ImageSpec) (oci.ImageDetailsResolver, error) {
|
func CreateImageDetailsResolver(imageSource *oci.ImageSpec) (oci.ImageDetailsResolver, error) {
|
||||||
switch imageSource.Type {
|
switch imageSource.Type {
|
||||||
case oci.OCI:
|
case oci.OCI:
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/docker/attest/pkg/config"
|
"github.com/docker/attest/pkg/config"
|
||||||
"github.com/docker/attest/pkg/oci"
|
"github.com/docker/attest/pkg/oci"
|
||||||
"github.com/docker/attest/pkg/policy"
|
"github.com/docker/attest/pkg/policy"
|
||||||
"github.com/docker/attest/pkg/tuf"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -43,27 +42,27 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
repo string
|
policyPath string
|
||||||
expectSuccess bool
|
expectSuccess bool
|
||||||
isCanonical bool
|
isCanonical bool
|
||||||
resolver attestation.Resolver
|
resolver attestation.Resolver
|
||||||
policy *policy.Options
|
opts *policy.Options
|
||||||
policyID string
|
policyID string
|
||||||
resolveErrorStr string
|
resolveErrorStr string
|
||||||
}{
|
}{
|
||||||
{repo: "testdata/mock-tuf-allow", expectSuccess: true, resolver: defaultResolver},
|
{policyPath: "testdata/policies/allow", expectSuccess: true, resolver: defaultResolver},
|
||||||
{repo: "testdata/mock-tuf-allow", expectSuccess: true, resolver: defaultResolver, policyID: "docker-official-images"},
|
{policyPath: "testdata/policies/allow", expectSuccess: true, resolver: defaultResolver, policyID: "docker-official-images"},
|
||||||
{repo: "testdata/mock-tuf-allow", resolver: defaultResolver, policyID: "non-existent-policy-id", resolveErrorStr: resolveErrorStr},
|
{policyPath: "testdata/policies/allow", resolver: defaultResolver, policyID: "non-existent-policy-id", resolveErrorStr: resolveErrorStr},
|
||||||
{repo: "testdata/mock-tuf-deny", resolver: defaultResolver},
|
{policyPath: "testdata/policies/deny", resolver: defaultResolver},
|
||||||
{repo: "testdata/mock-tuf-verify-sig", expectSuccess: true, resolver: defaultResolver},
|
{policyPath: "testdata/policies/verify-sig", expectSuccess: true, resolver: defaultResolver},
|
||||||
{repo: "testdata/mock-tuf-wrong-key", resolver: defaultResolver},
|
{policyPath: "testdata/policies/wrong-key", resolver: defaultResolver},
|
||||||
{repo: "testdata/mock-tuf-allow-canonical", expectSuccess: true, isCanonical: true, resolver: defaultResolver},
|
{policyPath: "testdata/policies/allow-canonical", expectSuccess: true, isCanonical: true, resolver: defaultResolver},
|
||||||
{repo: "testdata/mock-tuf-allow-canonical", resolver: defaultResolver},
|
{policyPath: "testdata/policies/allow-canonical", resolver: defaultResolver},
|
||||||
{repo: "testdata/mock-tuf-no-rego", resolver: defaultResolver, resolveErrorStr: "no policy file found in policy mapping"},
|
{policyPath: "testdata/policies/no-rego", resolver: defaultResolver, resolveErrorStr: "no policy file found in policy mapping"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.repo, func(t *testing.T) {
|
t.Run(tc.policyPath, func(t *testing.T) {
|
||||||
input := &policy.Input{
|
input := &policy.Input{
|
||||||
Digest: "sha256:test-digest",
|
Digest: "sha256:test-digest",
|
||||||
PURL: "test-purl",
|
PURL: "test-purl",
|
||||||
@@ -72,22 +71,18 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
|||||||
input.Tag = "test"
|
input.Tag = "test"
|
||||||
}
|
}
|
||||||
|
|
||||||
tufClient := tuf.NewMockTufClient(tc.repo, test.CreateTempDir(t, "", "tuf-dest"))
|
if tc.opts == nil {
|
||||||
if tc.policy == nil {
|
tc.opts = &policy.Options{
|
||||||
tc.policy = &policy.Options{
|
|
||||||
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
|
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
|
||||||
PolicyID: tc.policyID,
|
PolicyID: tc.policyID,
|
||||||
|
LocalPolicyDir: tc.policyPath,
|
||||||
|
DisableTUF: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
imageName, err := tc.resolver.ImageName(ctx)
|
imageName, err := tc.resolver.ImageName(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
platform, err := tc.resolver.ImagePlatform(ctx)
|
resolver := policy.NewResolver(nil, tc.opts)
|
||||||
require.NoError(t, err)
|
policy, err := resolver.ResolvePolicy(ctx, imageName)
|
||||||
src, err := oci.ParseImageSpec(imageName, oci.WithPlatform(platform.String()))
|
|
||||||
require.NoError(t, err)
|
|
||||||
resolver, err := policy.CreateImageDetailsResolver(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
policy, err := policy.ResolvePolicy(ctx, tufClient, resolver, tc.policy)
|
|
||||||
if tc.resolveErrorStr != "" {
|
if tc.resolveErrorStr != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), tc.resolveErrorStr)
|
assert.Contains(t, err.Error(), tc.resolveErrorStr)
|
||||||
@@ -108,7 +103,7 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadingMappings(t *testing.T) {
|
func TestLoadingMappings(t *testing.T) {
|
||||||
policyMappings, err := config.LoadLocalMappings(filepath.Join("testdata", "mock-tuf-allow"))
|
policyMappings, err := config.LoadLocalMappings(filepath.Join("testdata", "policies", "allow"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, len(policyMappings.Rules), 3)
|
assert.Equal(t, len(policyMappings.Rules), 3)
|
||||||
for _, mirror := range policyMappings.Rules {
|
for _, mirror := range policyMappings.Rules {
|
||||||
|
|||||||
194
pkg/policy/resolver.go
Normal file
194
pkg/policy/resolver.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
package policy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/distribution/reference"
|
||||||
|
"github.com/docker/attest/internal/util"
|
||||||
|
"github.com/docker/attest/pkg/config"
|
||||||
|
"github.com/docker/attest/pkg/tuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Resolver struct {
|
||||||
|
tufClient tuf.Downloader
|
||||||
|
opts *Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResolver(tufClient tuf.Downloader, opts *Options) *Resolver {
|
||||||
|
return &Resolver{
|
||||||
|
tufClient: tufClient,
|
||||||
|
opts: opts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) ResolvePolicy(_ context.Context, imageName string) (*Policy, error) {
|
||||||
|
p, err := r.resolvePolicyByID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve policy by id: %w", err)
|
||||||
|
}
|
||||||
|
if p != nil {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
imageName, err = normalizeImageName(imageName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
||||||
|
}
|
||||||
|
localMappings, err := config.LoadLocalMappings(r.opts.LocalPolicyDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
|
||||||
|
}
|
||||||
|
match, err := findPolicyMatch(imageName, localMappings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if match.matchType == matchTypePolicy {
|
||||||
|
return r.resolveLocalPolicy(match.policy, imageName, match.matchedName)
|
||||||
|
}
|
||||||
|
if !r.opts.DisableTUF {
|
||||||
|
tufMappings, err := config.LoadTUFMappings(r.tufClient, r.opts.LocalTargetsDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load tuf policy mappings as fallback: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's a mirror of a tuf policy
|
||||||
|
if match.matchType == matchTypeMatchNoPolicy {
|
||||||
|
for _, mapping := range tufMappings.Policies {
|
||||||
|
if mapping.ID == match.rule.PolicyID {
|
||||||
|
return r.resolveTUFPolicy(mapping, imageName, match.matchedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to resolve a tuf policy directly
|
||||||
|
match, err = findPolicyMatch(imageName, tufMappings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if match.matchType == matchTypePolicy {
|
||||||
|
return r.resolveTUFPolicy(match.policy, imageName, match.matchedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) resolveLocalPolicy(mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
|
||||||
|
if r.opts.LocalPolicyDir == "" {
|
||||||
|
return nil, fmt.Errorf("local policy dir not set")
|
||||||
|
}
|
||||||
|
var URI string
|
||||||
|
var digest map[string]string
|
||||||
|
files := make([]*File, 0, len(mapping.Files))
|
||||||
|
for _, f := range mapping.Files {
|
||||||
|
filename := f.Path
|
||||||
|
filePath := path.Join(r.opts.LocalPolicyDir, 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, &File{
|
||||||
|
Path: filename,
|
||||||
|
Content: fileContents,
|
||||||
|
})
|
||||||
|
// if the file is a policy file, store the URI and digest
|
||||||
|
if filepath.Ext(filename) == ".rego" {
|
||||||
|
// TODO: support multiple rego files, need some way to identify the main policy file
|
||||||
|
if URI != "" {
|
||||||
|
return nil, fmt.Errorf("multiple policy files found in policy mapping")
|
||||||
|
}
|
||||||
|
URI = filePath
|
||||||
|
digest = map[string]string{"sha256": util.SHA256Hex(fileContents)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if URI == "" {
|
||||||
|
return nil, fmt.Errorf("no policy file found in policy mapping")
|
||||||
|
}
|
||||||
|
policy := &Policy{
|
||||||
|
InputFiles: files,
|
||||||
|
Mapping: mapping,
|
||||||
|
URI: URI,
|
||||||
|
Digest: digest,
|
||||||
|
}
|
||||||
|
if imageName != matchedName {
|
||||||
|
policy.ResolvedName = matchedName
|
||||||
|
}
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) resolveTUFPolicy(mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
|
||||||
|
var URI string
|
||||||
|
var digest map[string]string
|
||||||
|
files := make([]*File, 0, len(mapping.Files))
|
||||||
|
for _, f := range mapping.Files {
|
||||||
|
filename := f.Path
|
||||||
|
file, err := r.tufClient.DownloadTarget(filename, filepath.Join(r.opts.LocalTargetsDir, filename))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
|
||||||
|
}
|
||||||
|
files = append(files, &File{
|
||||||
|
Path: filename,
|
||||||
|
Content: file.Data,
|
||||||
|
})
|
||||||
|
// if the file is a policy file, store the URI and digest
|
||||||
|
if filepath.Ext(filename) == ".rego" {
|
||||||
|
// TODO: support multiple rego files, need some way to identify the main policy file
|
||||||
|
if URI != "" {
|
||||||
|
return nil, fmt.Errorf("multiple policy files found in policy mapping")
|
||||||
|
}
|
||||||
|
URI = file.TargetURI
|
||||||
|
digest = map[string]string{"sha256": file.Digest}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if URI == "" {
|
||||||
|
return nil, fmt.Errorf("no policy file found in policy mapping")
|
||||||
|
}
|
||||||
|
policy := &Policy{
|
||||||
|
InputFiles: files,
|
||||||
|
Mapping: mapping,
|
||||||
|
URI: URI,
|
||||||
|
Digest: digest,
|
||||||
|
}
|
||||||
|
if imageName != matchedName {
|
||||||
|
policy.ResolvedName = matchedName
|
||||||
|
}
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) resolvePolicyByID() (*Policy, error) {
|
||||||
|
if r.opts.PolicyID != "" {
|
||||||
|
localMappings, err := config.LoadLocalMappings(r.opts.LocalPolicyDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
|
||||||
|
}
|
||||||
|
if localMappings != nil {
|
||||||
|
policy := localMappings.Policies[r.opts.PolicyID]
|
||||||
|
if policy != nil {
|
||||||
|
return r.resolveLocalPolicy(policy, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !r.opts.DisableTUF {
|
||||||
|
tufMappings, err := config.LoadTUFMappings(r.tufClient, r.opts.LocalTargetsDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load tuf policy mappings by id: %w", err)
|
||||||
|
}
|
||||||
|
policy := tufMappings.Policies[r.opts.PolicyID]
|
||||||
|
if policy != nil {
|
||||||
|
return r.resolveTUFPolicy(policy, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("policy with id %s not found", r.opts.PolicyID)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeImageName(imageName string) (string, error) {
|
||||||
|
named, err := reference.ParseNormalizedNamed(imageName)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse image name: %w", err)
|
||||||
|
}
|
||||||
|
return named.Name(), nil
|
||||||
|
}
|
||||||
65
pkg/policy/resolver_test.go
Normal file
65
pkg/policy/resolver_test.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package policy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/attest/internal/test"
|
||||||
|
"github.com/docker/attest/pkg/policy"
|
||||||
|
"github.com/docker/attest/pkg/tuf"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResolvePolicy(t *testing.T) {
|
||||||
|
localPolicyPath := "testdata/policies/allow"
|
||||||
|
tufPolicyPath := "testdata/policies/allow-canonical"
|
||||||
|
noLocalPolicyPath := "testdata/policies/no-policy"
|
||||||
|
testPolicyID := "docker-official-images"
|
||||||
|
testImageName := "localhost:5001/test/repo:tag"
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
policyPath string
|
||||||
|
policyID string
|
||||||
|
localOverridesTUF bool // if a policy is provided locally, it should override TUF
|
||||||
|
DisableTUF bool
|
||||||
|
}{
|
||||||
|
{name: "resolve by id (TUF only)", policyID: testPolicyID, DisableTUF: false},
|
||||||
|
{name: "resolve by id (local mapping, TUF policy)", policyPath: noLocalPolicyPath, policyID: testPolicyID, DisableTUF: false},
|
||||||
|
{name: "resolve by id (local mapping, local policy, no TUF)", policyPath: localPolicyPath, policyID: testPolicyID, DisableTUF: true},
|
||||||
|
{name: "resolve by id (local mapping, local policy)", policyPath: localPolicyPath, policyID: testPolicyID, DisableTUF: false, localOverridesTUF: true},
|
||||||
|
{name: "resolve by match (TUF only)", DisableTUF: false},
|
||||||
|
{name: "resolve by match (local mapping, TUF policy)", policyPath: noLocalPolicyPath, DisableTUF: false},
|
||||||
|
{name: "resolve by match (local mapping, local policy, no TUF)", policyPath: localPolicyPath, DisableTUF: true},
|
||||||
|
{name: "resolve by match (local mapping, local policy)", policyPath: localPolicyPath, DisableTUF: false, localOverridesTUF: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
var tufClient tuf.Downloader
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
opts := &policy.Options{}
|
||||||
|
tempDir := test.CreateTempDir(t, "", "tuf-dest")
|
||||||
|
if !tc.DisableTUF {
|
||||||
|
tufClient = tuf.NewMockTufClient(tufPolicyPath)
|
||||||
|
}
|
||||||
|
if tc.policyID != "" {
|
||||||
|
opts.PolicyID = tc.policyID
|
||||||
|
}
|
||||||
|
if tc.policyPath != "" {
|
||||||
|
opts.LocalPolicyDir = tc.policyPath
|
||||||
|
}
|
||||||
|
opts.DisableTUF = tc.DisableTUF
|
||||||
|
opts.LocalTargetsDir = tempDir
|
||||||
|
resolver := policy.NewResolver(tufClient, opts)
|
||||||
|
policy, err := resolver.ResolvePolicy(context.Background(), testImageName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, policy)
|
||||||
|
if tc.DisableTUF || tc.localOverridesTUF {
|
||||||
|
assert.Contains(t, policy.URI, localPolicyPath)
|
||||||
|
} else {
|
||||||
|
assert.Contains(t, policy.URI, tufPolicyPath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
10
pkg/policy/testdata/policies/no-policy/mapping.yaml
vendored
Normal file
10
pkg/policy/testdata/policies/no-policy/mapping.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# map repos to policies
|
||||||
|
version: v1
|
||||||
|
kind: policy-mapping
|
||||||
|
rules:
|
||||||
|
- pattern: "^docker[.]io/library/(.*)$"
|
||||||
|
policy-id: docker-official-images
|
||||||
|
- pattern: ^localhost:5001/(.*)$
|
||||||
|
rewrite: docker.io/library/$1
|
||||||
|
- pattern: ^registry[.]local:5000/(.*)$
|
||||||
|
rewrite: docker.io/library/$1
|
||||||
@@ -28,11 +28,13 @@ type Result struct {
|
|||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
TUFClientOptions *tuf.ClientOptions
|
TUFClientOptions *tuf.ClientOptions
|
||||||
|
DisableTUF bool
|
||||||
LocalTargetsDir string
|
LocalTargetsDir string
|
||||||
LocalPolicyDir string
|
LocalPolicyDir string
|
||||||
PolicyID string
|
PolicyID string
|
||||||
ReferrersRepo string
|
ReferrersRepo string
|
||||||
AttestationStyle config.AttestationStyle
|
AttestationStyle config.AttestationStyle
|
||||||
|
Debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
|
|||||||
@@ -10,23 +10,18 @@ import (
|
|||||||
|
|
||||||
type MockTufClient struct {
|
type MockTufClient struct {
|
||||||
srcPath string
|
srcPath string
|
||||||
dstPath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockTufClient(srcPath string, dstPath string) *MockTufClient {
|
func NewMockTufClient(srcPath string) *MockTufClient {
|
||||||
if srcPath == "" {
|
if srcPath == "" {
|
||||||
panic("srcPath must be set")
|
panic("srcPath must be set")
|
||||||
}
|
}
|
||||||
if dstPath == "" {
|
|
||||||
panic("dstPath must be set")
|
|
||||||
}
|
|
||||||
return &MockTufClient{
|
return &MockTufClient{
|
||||||
srcPath: srcPath,
|
srcPath: srcPath,
|
||||||
dstPath: dstPath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *MockTufClient) DownloadTarget(target string, filePath string) (file *TargetFile, err error) {
|
func (dc *MockTufClient) DownloadTarget(target string, _ string) (file *TargetFile, err error) {
|
||||||
targetPath := filepath.Join(dc.srcPath, target)
|
targetPath := filepath.Join(dc.srcPath, target)
|
||||||
src, err := os.Open(targetPath)
|
src, err := os.Open(targetPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -34,32 +29,12 @@ func (dc *MockTufClient) DownloadTarget(target string, filePath string) (file *T
|
|||||||
}
|
}
|
||||||
defer src.Close()
|
defer src.Close()
|
||||||
|
|
||||||
var dstFilePath string
|
b, err := io.ReadAll(src)
|
||||||
if filePath == "" {
|
|
||||||
dstFilePath = filepath.Join(dc.dstPath, filepath.FromSlash(target))
|
|
||||||
} else {
|
|
||||||
dstFilePath = filePath
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(filepath.Dir(dstFilePath), os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dst, err := os.Create(dstFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer dst.Close()
|
|
||||||
|
|
||||||
// reading from tee will read from src and write to dst at the same time
|
|
||||||
tee := io.TeeReader(src, dst)
|
|
||||||
|
|
||||||
b, err := io.ReadAll(tee)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &TargetFile{ActualFilePath: dstFilePath, TargetURI: targetPath, Data: b, Digest: util.SHA256Hex(b)}, nil
|
return &TargetFile{TargetURI: targetPath, Data: b, Digest: util.SHA256Hex(b)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type MockVersionChecker struct {
|
type MockVersionChecker struct {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/google/go-containerregistry/pkg/v1/static"
|
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/testcontainers/testcontainers-go/modules/registry"
|
"github.com/testcontainers/testcontainers-go/modules/registry"
|
||||||
"github.com/theupdateframework/go-tuf/v2/metadata"
|
"github.com/theupdateframework/go-tuf/v2/metadata"
|
||||||
"github.com/theupdateframework/go-tuf/v2/metadata/config"
|
"github.com/theupdateframework/go-tuf/v2/metadata/config"
|
||||||
@@ -55,33 +56,35 @@ func TestRegistryFetcher(t *testing.T) {
|
|||||||
delegatedDir := CreateTempDir(t, dir, delegatedRole)
|
delegatedDir := CreateTempDir(t, dir, delegatedRole)
|
||||||
delegatedTargetFile := fmt.Sprintf("%s/%s", delegatedRole, targetFile)
|
delegatedTargetFile := fmt.Sprintf("%s/%s", delegatedRole, targetFile)
|
||||||
|
|
||||||
cfg, err := config.New(metadataRepo, DockerTUFRootDev.Data)
|
// note - url is ignored here - needed to make http url parsing happy even when using oci
|
||||||
assert.NoError(t, err)
|
cfg, err := config.New("", DockerTUFRootDev.Data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataImgTag, targetsRepo)
|
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataImgTag, targetsRepo)
|
||||||
cfg.LocalMetadataDir = dir
|
cfg.LocalMetadataDir = dir
|
||||||
cfg.LocalTargetsDir = dir
|
cfg.LocalTargetsDir = dir
|
||||||
cfg.RemoteTargetsURL = targetsRepo
|
cfg.RemoteTargetsURL = targetsRepo
|
||||||
|
cfg.RemoteMetadataURL = metadataRepo
|
||||||
|
|
||||||
// create a new Updater instance
|
// create a new Updater instance
|
||||||
up, err := updater.New(cfg)
|
up, err := updater.New(cfg)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// refresh the metadata
|
// refresh the metadata
|
||||||
err = up.Refresh()
|
err = up.Refresh()
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// download top-level target
|
// download top-level target
|
||||||
targetInfo, err := up.GetTargetInfo(targetFile)
|
targetInfo, err := up.GetTargetInfo(targetFile)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, _, err = up.DownloadTarget(targetInfo, filepath.Join(dir, targetInfo.Path), "")
|
_, _, err = up.DownloadTarget(targetInfo, filepath.Join(dir, targetInfo.Path), "")
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// download delegated target
|
// download delegated target
|
||||||
targetInfo, err = up.GetTargetInfo(delegatedTargetFile)
|
targetInfo, err = up.GetTargetInfo(delegatedTargetFile)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, _, err = up.DownloadTarget(targetInfo, filepath.Join(delegatedDir, targetFile), "")
|
_, _, err = up.DownloadTarget(targetInfo, filepath.Join(delegatedDir, targetFile), "")
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRoleFromConsistentName(t *testing.T) {
|
func TestRoleFromConsistentName(t *testing.T) {
|
||||||
@@ -355,9 +358,6 @@ func RunTestRegistry(t *testing.T) (*registry.RegistryContainer, *url.URL) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to parse container address: %s", err)
|
t.Fatalf("failed to parse container address: %s", err)
|
||||||
}
|
}
|
||||||
if addr.Hostname() == "127.0.0.1" {
|
|
||||||
addr.Host = "localhost:" + addr.Port()
|
|
||||||
}
|
|
||||||
return registryContainer, addr
|
return registryContainer, addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package tuf
|
package tuf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -12,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/distribution/reference"
|
||||||
"github.com/docker/attest/internal/embed"
|
"github.com/docker/attest/internal/embed"
|
||||||
"github.com/docker/attest/internal/util"
|
"github.com/docker/attest/internal/util"
|
||||||
"github.com/theupdateframework/go-tuf/v2/metadata"
|
"github.com/theupdateframework/go-tuf/v2/metadata"
|
||||||
@@ -21,24 +21,6 @@ import (
|
|||||||
"github.com/theupdateframework/go-tuf/v2/metadata/updater"
|
"github.com/theupdateframework/go-tuf/v2/metadata/updater"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tufCtxKeyType struct{}
|
|
||||||
|
|
||||||
var DownloaderCtxKey tufCtxKeyType
|
|
||||||
|
|
||||||
// WithDownloader sets Downloader in context.
|
|
||||||
func WithDownloader(ctx context.Context, downloader Downloader) context.Context {
|
|
||||||
return context.WithValue(ctx, DownloaderCtxKey, downloader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloader returns the Downloader from context and `true` if it exists, otherwise `nil` and `false`.
|
|
||||||
func GetDownloader(ctx context.Context) (Downloader, bool) {
|
|
||||||
t, ok := ctx.Value(DownloaderCtxKey).(Downloader)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return t, true
|
|
||||||
}
|
|
||||||
|
|
||||||
type Source string
|
type Source string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -126,20 +108,28 @@ func NewClient(opts *ClientOptions) (*Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create updater configuration
|
// create updater configuration
|
||||||
cfg, err := config.New(opts.MetadataSource, rootBytes) // default config
|
// this is parsed as an HTTP url (which doesn't work for OCI). We're setting this to make TUF happy
|
||||||
|
// and overwriding the configuration below
|
||||||
|
cfg, err := config.New("", rootBytes) // default config
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create TUF updater configuration: %w", err)
|
return nil, fmt.Errorf("failed to create TUF updater configuration: %w", err)
|
||||||
}
|
}
|
||||||
cfg.LocalMetadataDir = metadataPath
|
cfg.LocalMetadataDir = metadataPath
|
||||||
cfg.LocalTargetsDir = filepath.Join(metadataPath, "download")
|
cfg.LocalTargetsDir = filepath.Join(metadataPath, "download")
|
||||||
|
cfg.RemoteMetadataURL = opts.MetadataSource
|
||||||
cfg.RemoteTargetsURL = opts.TargetsSource
|
cfg.RemoteTargetsURL = opts.TargetsSource
|
||||||
|
|
||||||
if tufSource == OCISource {
|
if tufSource == OCISource {
|
||||||
metadataRepo, metadataTag, found := strings.Cut(opts.MetadataSource, ":")
|
ref, err := reference.ParseNormalizedNamed(opts.MetadataSource)
|
||||||
if !found {
|
if err != nil {
|
||||||
fmt.Printf("metadata tag not found in URL, using latest\n")
|
return nil, fmt.Errorf("failed to parse metadata source: %w", err)
|
||||||
metadataTag = LatestTag
|
|
||||||
}
|
}
|
||||||
|
// add latest tag
|
||||||
|
metadataTag := LatestTag
|
||||||
|
if tag, ok := ref.(reference.Tagged); ok {
|
||||||
|
metadataTag = tag.Tag()
|
||||||
|
}
|
||||||
|
metadataRepo := ref.Name()
|
||||||
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataTag, opts.TargetsSource)
|
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataTag, opts.TargetsSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func TestDownloadTarget(t *testing.T) {
|
|||||||
|
|
||||||
// download delegated target
|
// download delegated target
|
||||||
targetInfo, err := tufClient.updater.GetTargetInfo(delegatedTargetFile)
|
targetInfo, err := tufClient.updater.GetTargetInfo(delegatedTargetFile)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = tufClient.DownloadTarget(targetInfo.Path, filepath.Join(tufPath, targetInfo.Path))
|
_, err = tufClient.DownloadTarget(targetInfo.Path, filepath.Join(tufPath, targetInfo.Path))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user