diff --git a/internal/mock/mocks.go b/internal/mock/mocks.go index a4ef4ae6..5aa2e66f 100644 --- a/internal/mock/mocks.go +++ b/internal/mock/mocks.go @@ -65,6 +65,7 @@ var MockCaCompatiblePluginVerSigEnv_1_0_0 []byte var ( SampleArtifactUri = "registry.acme-rockets.io/software/net-monitor@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e" SampleDigest = digest.Digest("sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e") + ZeroDigest = digest.Digest("sha256:0000000000000000000000000000000000000000000000000000000000000000") Annotations = map[string]string{"key": "value"} ImageDescriptor = ocispec.Descriptor{ MediaType: "application/vnd.docker.distribution.manifest.v2+json", @@ -110,6 +111,7 @@ type Repository struct { ListSignaturesError error FetchSignatureBlobResponse []byte FetchSignatureBlobError error + MissMatchDigest bool } func NewRepository() Repository { @@ -121,6 +123,14 @@ func NewRepository() Repository { } func (t Repository) Resolve(ctx context.Context, reference string) (ocispec.Descriptor, error) { + if t.MissMatchDigest { + return ocispec.Descriptor{ + MediaType: "application/vnd.docker.distribution.manifest.v2+json", + Digest: ZeroDigest, + Size: 528, + Annotations: Annotations, + }, nil + } return t.ResolveResponse, t.ResolveError } diff --git a/notation.go b/notation.go index 0ec45662..59b96461 100644 --- a/notation.go +++ b/notation.go @@ -324,6 +324,8 @@ func Verify(ctx context.Context, verifier Verifier, repo registry.Repository, ve // artifactRef is not a digest reference logger.Infof("Resolved artifact tag `%s` to digest `%s` before verification", ref.Reference, artifactDescriptor.Digest.String()) logger.Warn("The resolved digest may not point to the same signed artifact, since tags are mutable") + } else if ref.Reference != artifactDescriptor.Digest.String() { + return ocispec.Descriptor{}, nil, ErrorSignatureRetrievalFailed{Msg: fmt.Sprintf("user input digest %s does not match the resolved digest %s", ref.Reference, artifactDescriptor.Digest.String())} } var verificationOutcomes []*VerificationOutcome diff --git a/notation_test.go b/notation_test.go index 9c199471..e2603be1 100644 --- a/notation_test.go +++ b/notation_test.go @@ -148,6 +148,23 @@ func TestVerifyTagReferenceFailed(t *testing.T) { } } +func TestVerifyDigestNotMatchResolve(t *testing.T) { + policyDocument := dummyPolicyDocument() + repo := mock.NewRepository() + repo.MissMatchDigest = true + verifier := dummyVerifier{&policyDocument, mock.PluginManager{}, false, *trustpolicy.LevelStrict} + + errorMessage := fmt.Sprintf("user input digest %s does not match the resolved digest %s", mock.SampleDigest, mock.ZeroDigest) + expectedErr := ErrorSignatureRetrievalFailed{Msg: errorMessage} + + // mock the repository + opts := VerifyOptions{ArtifactReference: mock.SampleArtifactUri, MaxSignatureAttempts: 50} + _, _, err := Verify(context.Background(), &verifier, repo, opts) + if err == nil || err.Error() != errorMessage { + t.Fatalf("VerifyTagReference expected: %v got: %v", expectedErr, err) + } +} + func TestSkippedSignatureVerification(t *testing.T) { policyDocument := dummyPolicyDocument() repo := mock.NewRepository()