From 11d93d3baf75438c2d948801e17679409c451cff Mon Sep 17 00:00:00 2001 From: Akira Saso Date: Fri, 16 Sep 2022 09:38:36 +0900 Subject: [PATCH 1/4] fix: enable parsing of UUID-only rekor entry ID --- pkg/rekor/client.go | 28 ++++++++++++++++--------- pkg/rekor/client_test.go | 24 +++++++++++++++++---- pkg/rekor/testdata/search-response.json | 3 ++- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/pkg/rekor/client.go b/pkg/rekor/client.go index bb58848558a..d1561e35817 100644 --- a/pkg/rekor/client.go +++ b/pkg/rekor/client.go @@ -22,20 +22,28 @@ const ( var ErrNoAttestation = xerrors.Errorf("Rekor attestations not found") -// EntryID is a hex-format string. The length of the string is 80. -// It consists of two elements, the TreeID and the UUID. +// EntryID is a hex-format string. The length of the string is 80 or 64. +// If the length is 80, it consists of two elements, the TreeID and the UUID. If the length is 64, +// it consists only of the UUID. // cf. https://github.com/sigstore/rekor/blob/4923f60f4ae55ccd4baf28d182e8f55c2d8097d3/pkg/sharding/sharding.go#L25-L36 -type EntryID string +type EntryID struct { + TreeID string + UUID string +} func NewEntryID(entryID string) (EntryID, error) { - if len(entryID) == treeIDLen+uuidLen { - return EntryID(entryID), nil + switch len(entryID) { + case treeIDLen + uuidLen: + return EntryID{TreeID: entryID[:treeIDLen], UUID: entryID[treeIDLen:]}, nil + case uuidLen: + return EntryID{TreeID: "", UUID: entryID}, nil + default: + return EntryID{}, xerrors.Errorf("invalid Entry ID length") } - return "", xerrors.Errorf("invalid Entry ID length") } -func (e EntryID) UUID() string { - return string(e)[treeIDLen:] +func (e EntryID) String() string { + return e.TreeID + e.UUID } type Entry struct { @@ -79,7 +87,7 @@ func (c *Client) Search(ctx context.Context, hash string) ([]EntryID, error) { } func (c *Client) GetEntry(ctx context.Context, entryID EntryID) (Entry, error) { - params := entries.NewGetLogEntryByUUIDParamsWithContext(ctx).WithEntryUUID(string(entryID)) + params := entries.NewGetLogEntryByUUIDParamsWithContext(ctx).WithEntryUUID(entryID.String()) // TODO: bulk search resp, err := c.Entries.GetLogEntryByUUID(params) @@ -87,7 +95,7 @@ func (c *Client) GetEntry(ctx context.Context, entryID EntryID) (Entry, error) { return Entry{}, xerrors.Errorf("failed to get log entry by UUID: %w", err) } - entry, found := resp.Payload[entryID.UUID()] + entry, found := resp.Payload[entryID.UUID] if !found { return Entry{}, ErrNoAttestation } diff --git a/pkg/rekor/client_test.go b/pkg/rekor/client_test.go index 496cfb97977..a828e2d3e88 100644 --- a/pkg/rekor/client_test.go +++ b/pkg/rekor/client_test.go @@ -30,8 +30,18 @@ func TestClient_Search(t *testing.T) { hash: "92251458088c638061cda8fd8b403b76d661a4dc6b7ee71b6affcf1872557b2b", }, want: []rekor.EntryID{ - "392f8ecba72f4326eb624a7403756250b5f2ad58842a99d1653cd6f147f4ce9eda2da350bd908a55", - "392f8ecba72f4326414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a216523", + { + TreeID: "392f8ecba72f4326", + UUID: "eb624a7403756250b5f2ad58842a99d1653cd6f147f4ce9eda2da350bd908a55", + }, + { + TreeID: "392f8ecba72f4326", + UUID: "414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a216523", + }, + { + TreeID: "", + UUID: "414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a012345", + }, }, }, { @@ -80,7 +90,10 @@ func TestClient_GetEntry(t *testing.T) { name: "happy path", mockResponseFile: "testdata/log-entry.json", args: args{ - uuid: "392f8ecba72f43268b5b2debb565fd5cb05ae0d3935351fa3faabce558bede72e197b5722a742b1e", + uuid: rekor.EntryID{ + TreeID: "392f8ecba72f4326", + UUID: "8b5b2debb565fd5cb05ae0d3935351fa3faabce558bede72e197b5722a742b1e", + }, }, want: rekor.Entry{ Statement: []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"cosign.sigstore.dev/attestation/v1","subject":[{"name":"ghcr.io/aquasecurity/trivy-test-images","digest":{"sha256":"20d3f693dcffa44d6b24eae88783324d25cc132c22089f70e4fbfb858625b062"}}],"predicate":{"Data":"\"foo\\n\"\n","Timestamp":"2022-08-26T01:17:17Z"}}`), @@ -90,7 +103,10 @@ func TestClient_GetEntry(t *testing.T) { name: "no attestation", mockResponseFile: "testdata/log-entry-no-attestation.json", args: args{ - uuid: "392f8ecba72f43268b5b2debb565fd5cb05ae0d3935351fa3faabce558bede72e197b5722a742b1e", + uuid: rekor.EntryID{ + TreeID: "392f8ecba72f4326", + UUID: "8b5b2debb565fd5cb05ae0d3935351fa3faabce558bede72e197b5722a742b1e", + }, }, wantErr: rekor.ErrNoAttestation, }, diff --git a/pkg/rekor/testdata/search-response.json b/pkg/rekor/testdata/search-response.json index ed62fc97314..3607ce13df5 100644 --- a/pkg/rekor/testdata/search-response.json +++ b/pkg/rekor/testdata/search-response.json @@ -1,4 +1,5 @@ [ "392f8ecba72f4326eb624a7403756250b5f2ad58842a99d1653cd6f147f4ce9eda2da350bd908a55", - "392f8ecba72f4326414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a216523" + "392f8ecba72f4326414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a216523", + "414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a012345" ] From 0b01ceb1370acd53f9354f44ec876f7bc7f9e0a8 Mon Sep 17 00:00:00 2001 From: Akira Saso Date: Fri, 16 Sep 2022 14:27:29 +0900 Subject: [PATCH 2/4] fix: continue to inspect rekor records even after a predicate type error occurs --- pkg/fanal/artifact/image/remote_sbom.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/fanal/artifact/image/remote_sbom.go b/pkg/fanal/artifact/image/remote_sbom.go index df681164526..2dd9d092249 100644 --- a/pkg/fanal/artifact/image/remote_sbom.go +++ b/pkg/fanal/artifact/image/remote_sbom.go @@ -61,10 +61,10 @@ func (a Artifact) inspectSBOMAttestation(ctx context.Context) (ftypes.ArtifactRe for _, id := range entryIDs { log.Logger.Debugf("Inspecting Rekor entry: %s", id) ref, err := a.inspectRekorRecord(ctx, client, id) - if errors.Is(err, rekor.ErrNoAttestation) { + if errors.Is(err, rekor.ErrNoAttestation) || errors.Is(err, errNoSBOMFound) { continue } else if err != nil { - return ftypes.ArtifactReference{}, xerrors.Errorf("rekor rekord inspection error: %w", err) + return ftypes.ArtifactReference{}, xerrors.Errorf("rekor record inspection error: %w", err) } return ref, nil } From 62c4e4ec2dd1ab0ed9aa595de3e0b57e6eb6a5bd Mon Sep 17 00:00:00 2001 From: knqyf263 Date: Fri, 16 Sep 2022 10:47:12 +0300 Subject: [PATCH 3/4] refactor: use New instead of Errorf --- pkg/rekor/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/rekor/client.go b/pkg/rekor/client.go index d1561e35817..595083b8004 100644 --- a/pkg/rekor/client.go +++ b/pkg/rekor/client.go @@ -38,7 +38,7 @@ func NewEntryID(entryID string) (EntryID, error) { case uuidLen: return EntryID{TreeID: "", UUID: entryID}, nil default: - return EntryID{}, xerrors.Errorf("invalid Entry ID length") + return EntryID{}, xerrors.New("invalid Entry ID length") } } From b087cc93a55f59100c1efb1dc2d8736f1995c851 Mon Sep 17 00:00:00 2001 From: knqyf263 Date: Fri, 16 Sep 2022 10:57:41 +0300 Subject: [PATCH 4/4] fix: skip base64-encoded attestation --- pkg/fanal/artifact/image/remote_sbom.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/fanal/artifact/image/remote_sbom.go b/pkg/fanal/artifact/image/remote_sbom.go index 2dd9d092249..fc25580fa41 100644 --- a/pkg/fanal/artifact/image/remote_sbom.go +++ b/pkg/fanal/artifact/image/remote_sbom.go @@ -1,6 +1,7 @@ package image import ( + "bytes" "context" "encoding/json" "errors" @@ -111,6 +112,11 @@ func (a Artifact) inspectRekorRecord(ctx context.Context, client *rekor.Client, } func (a Artifact) parseStatement(entry rekor.Entry) (json.RawMessage, error) { + // Skip base64-encoded attestation + if bytes.HasPrefix(entry.Statement, []byte(`eyJ`)) { + return nil, errNoSBOMFound + } + // Parse statement of in-toto attestation var raw json.RawMessage statement := &in_toto.Statement{