Skip to content

Commit

Permalink
fix: enable parsing of UUID-only rekor entry ID (#2887)
Browse files Browse the repository at this point in the history
Co-authored-by: knqyf263 <knqyf263@gmail.com>
  • Loading branch information
otms61 and knqyf263 committed Sep 16, 2022
1 parent 018eda6 commit ba29ce6
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 17 deletions.
10 changes: 8 additions & 2 deletions pkg/fanal/artifact/image/remote_sbom.go
@@ -1,6 +1,7 @@
package image

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -61,10 +62,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
}
Expand Down Expand Up @@ -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{
Expand Down
28 changes: 18 additions & 10 deletions pkg/rekor/client.go
Expand Up @@ -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.New("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 {
Expand Down Expand Up @@ -79,15 +87,15 @@ 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)
if err != nil {
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
}
Expand Down
24 changes: 20 additions & 4 deletions pkg/rekor/client_test.go
Expand Up @@ -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",
},
},
},
{
Expand Down Expand Up @@ -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"}}`),
Expand All @@ -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,
},
Expand Down
3 changes: 2 additions & 1 deletion pkg/rekor/testdata/search-response.json
@@ -1,4 +1,5 @@
[
"392f8ecba72f4326eb624a7403756250b5f2ad58842a99d1653cd6f147f4ce9eda2da350bd908a55",
"392f8ecba72f4326414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a216523"
"392f8ecba72f4326414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a216523",
"414eaca77bd19bf5f378725d7fd79309605a81b69cc0101f5cd3119d0a012345"
]

0 comments on commit ba29ce6

Please sign in to comment.