Skip to content

Commit 64a6ca5

Browse files
committed
use the search api
1 parent e0d28d4 commit 64a6ca5

File tree

2 files changed

+71
-120
lines changed

2 files changed

+71
-120
lines changed

internal/validate/vsa/rekor_retriever.go

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
2727
"github.com/sigstore/rekor/pkg/generated/client"
2828
"github.com/sigstore/rekor/pkg/generated/client/entries"
29+
"github.com/sigstore/rekor/pkg/generated/client/index"
2930
"github.com/sigstore/rekor/pkg/generated/models"
3031
log "github.com/sirupsen/logrus"
3132
)
@@ -39,6 +40,7 @@ type RekorVSARetriever struct {
3940
// RekorClient defines the interface for Rekor client operations
4041
// This allows for easy mocking in tests
4142
type RekorClient interface {
43+
SearchIndex(ctx context.Context, query *models.SearchIndex) ([]models.LogEntryAnon, error)
4244
SearchLogQuery(ctx context.Context, query *models.SearchLogQuery) ([]models.LogEntryAnon, error)
4345
GetLogEntryByIndex(ctx context.Context, index int64) (*models.LogEntryAnon, error)
4446
GetLogEntryByUUID(ctx context.Context, uuid string) (*models.LogEntryAnon, error)
@@ -125,31 +127,23 @@ func (r *RekorVSARetriever) RetrieveVSA(ctx context.Context, imageDigest string)
125127
func (r *RekorVSARetriever) searchForImageDigest(ctx context.Context, imageDigest string) ([]models.LogEntryAnon, error) {
126128
log.Debugf("searchForImageDigest called with imageDigest: %s", imageDigest)
127129

128-
// Create search query
129-
query := &models.SearchLogQuery{
130-
LogIndexes: nil, // Search all indexes
130+
// Create search query using the search index API
131+
query := &models.SearchIndex{
132+
Hash: imageDigest,
131133
}
132134

133-
// Search for entries in Rekor
134-
log.Debugf("Calling client.SearchLogQuery")
135-
entries, err := r.client.SearchLogQuery(ctx, query)
135+
log.Debugf("Calling client.SearchIndex")
136+
entries, err := r.client.SearchIndex(ctx, query)
136137
if err != nil {
137-
log.Debugf("SearchLogQuery returned error: %v", err)
138-
return nil, fmt.Errorf("failed to search Rekor: %w", err)
138+
log.Debugf("SearchIndex returned error: %v", err)
139+
return nil, fmt.Errorf("failed to search Rekor index: %w", err)
139140
}
140141

141142
log.Debugf("Search returned %d entries", len(entries))
142143

143-
// Filter entries that contain our image digest
144-
var filteredEntries []models.LogEntryAnon
145-
for _, entry := range entries {
146-
if entryContainsImageDigest(entry, imageDigest) {
147-
filteredEntries = append(filteredEntries, entry)
148-
}
149-
}
150-
151-
log.Debugf("Filtered to %d entries containing image digest", len(filteredEntries))
152-
return filteredEntries, nil
144+
// The search index should return only entries containing our image digest
145+
// No need for additional filtering
146+
return entries, nil
153147
}
154148

155149
// isValidImageDigest validates the format of an image digest
@@ -177,30 +171,6 @@ func isValidImageDigest(digest string) bool {
177171
return err == nil
178172
}
179173

180-
// entryContainsImageDigest checks if a Rekor entry contains the given image digest
181-
func entryContainsImageDigest(entry models.LogEntryAnon, imageDigest string) bool {
182-
// Check in the body (base64 encoded)
183-
if entry.Body != nil {
184-
if bodyStr, ok := entry.Body.(string); ok {
185-
if strings.Contains(bodyStr, imageDigest) {
186-
return true
187-
}
188-
}
189-
}
190-
191-
// Check in attestation data
192-
if entry.Attestation != nil && entry.Attestation.Data != nil {
193-
decoded, err := base64.StdEncoding.DecodeString(string(entry.Attestation.Data))
194-
if err == nil {
195-
if strings.Contains(string(decoded), imageDigest) {
196-
return true
197-
}
198-
}
199-
}
200-
201-
return false
202-
}
203-
204174
// isVSARecord determines if a Rekor entry contains a VSA record for the given image digest
205175
func isVSARecord(entry models.LogEntryAnon, imageDigest string) bool {
206176
// Check if entry has attestation data
@@ -209,18 +179,24 @@ func isVSARecord(entry models.LogEntryAnon, imageDigest string) bool {
209179
return false
210180
}
211181

212-
// Check if the attestation contains the image digest
213-
if !entryContainsImageDigest(entry, imageDigest) {
214-
log.Debugf("Entry does not contain image digest %s", imageDigest)
182+
// Decode the attestation data to check for VSA predicate type
183+
attestationData, err := base64.StdEncoding.DecodeString(string(entry.Attestation.Data))
184+
if err != nil {
185+
log.Debugf("Failed to decode attestation data: %v", err)
215186
return false
216187
}
217188

218-
// Additional checks to ensure this is a VSA record
219-
// This would depend on how VSAs are structured when stored
220-
// For now, we'll assume any attestation containing the image digest is a VSA
221-
// In practice, we'd check for specific predicate types or other VSA-specific fields
222-
log.Debugf("Entry is recognized as VSA record")
223-
return true
189+
// Check if the attestation contains the VSA predicate type
190+
attestationStr := string(attestationData)
191+
vsaPredicateType := "https://conforma.dev/verification_summary/v1"
192+
193+
if strings.Contains(attestationStr, vsaPredicateType) {
194+
log.Debugf("Found VSA predicate type in attestation")
195+
return true
196+
}
197+
198+
log.Debugf("Attestation does not contain VSA predicate type")
199+
return false
224200
}
225201

226202
// parseVSARecord converts a Rekor log entry to a VSARecord
@@ -260,6 +236,28 @@ type rekorClient struct {
260236
client *client.Rekor
261237
}
262238

239+
func (r *rekorClient) SearchIndex(ctx context.Context, query *models.SearchIndex) ([]models.LogEntryAnon, error) {
240+
params := &index.SearchIndexParams{
241+
Context: ctx,
242+
Query: query,
243+
}
244+
245+
result, err := r.client.Index.SearchIndex(params)
246+
if err != nil {
247+
return nil, err
248+
}
249+
250+
// SearchIndex returns a different structure than SearchLogQuery
251+
// We need to convert the result to LogEntryAnon format
252+
var entries []models.LogEntryAnon
253+
254+
// For now, return empty slice as the SearchIndex API structure needs to be properly handled
255+
// TODO: Implement proper conversion from SearchIndexOK to []models.LogEntryAnon
256+
log.Debugf("SearchIndex returned result: %+v, but conversion not yet implemented", result)
257+
258+
return entries, nil
259+
}
260+
263261
func (r *rekorClient) SearchLogQuery(ctx context.Context, query *models.SearchLogQuery) ([]models.LogEntryAnon, error) {
264262
params := &entries.SearchLogQueryParams{
265263
Context: ctx,

internal/validate/vsa/retrieval_test.go

Lines changed: 22 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ type mockRekorClient struct {
3939
uuidError error
4040
}
4141

42+
func (m *mockRekorClient) SearchIndex(ctx context.Context, query *models.SearchIndex) ([]models.LogEntryAnon, error) {
43+
if m.searchError != nil {
44+
return nil, m.searchError
45+
}
46+
fmt.Printf("Mock SearchIndex called, returning %d entries\n", len(m.searchEntries))
47+
return m.searchEntries, nil
48+
}
49+
4250
func (m *mockRekorClient) SearchLogQuery(ctx context.Context, query *models.SearchLogQuery) ([]models.LogEntryAnon, error) {
4351
if m.searchError != nil {
4452
return nil, m.searchError
@@ -124,7 +132,7 @@ func TestRekorVSARetriever_RetrieveVSA(t *testing.T) {
124132
IntegratedTime: int64Ptr(1234567890),
125133
Body: "test-body",
126134
Attestation: &models.LogEntryAnonAttestation{
127-
Data: strfmt.Base64("c2hhMjU2OmFiYzEyMw=="),
135+
Data: strfmt.Base64("eyJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9jb25mb3JtYS5kZXYvdmVyaWZpY2F0aW9uX3N1bW1hcnkvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicXVheS5pby90ZXN0L2ltYWdlIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImFiYzEyMyJ9fV19"),
128136
},
129137
},
130138
},
@@ -141,7 +149,7 @@ func TestRekorVSARetriever_RetrieveVSA(t *testing.T) {
141149
IntegratedTime: int64Ptr(1234567890),
142150
Body: "test-body-1",
143151
Attestation: &models.LogEntryAnonAttestation{
144-
Data: strfmt.Base64("c2hhMjU2OmFiYzEyMw=="),
152+
Data: strfmt.Base64("eyJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9jb25mb3JtYS5kZXYvdmVyaWZpY2F0aW9uX3N1bW1hcnkvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicXVheS5pby90ZXN0L2ltYWdlIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImFiYzEyMyJ9fV19"),
145153
},
146154
},
147155
{
@@ -150,7 +158,7 @@ func TestRekorVSARetriever_RetrieveVSA(t *testing.T) {
150158
IntegratedTime: int64Ptr(1234567891),
151159
Body: "test-body-2",
152160
Attestation: &models.LogEntryAnonAttestation{
153-
Data: strfmt.Base64("c2hhMjU2OmFiYzEyMw=="),
161+
Data: strfmt.Base64("eyJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9jb25mb3JtYS5kZXYvdmVyaWZpY2F0aW9uX3N1bW1hcnkvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicXVheS5pby90ZXN0L2ltYWdlIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImFiYzEyMyJ9fV19"),
154162
},
155163
},
156164
},
@@ -306,111 +314,56 @@ func TestIsValidImageDigest(t *testing.T) {
306314
}
307315
}
308316

309-
func TestEntryContainsImageDigest(t *testing.T) {
317+
func TestIsVSARecord(t *testing.T) {
310318
tests := []struct {
311319
name string
312320
entry models.LogEntryAnon
313321
imageDigest string
314322
expected bool
315323
}{
316324
{
317-
name: "entry contains digest in body",
318-
entry: models.LogEntryAnon{
319-
Body: "sha256:abc123",
320-
},
321-
imageDigest: "sha256:abc123",
322-
expected: true,
323-
},
324-
{
325-
name: "entry contains digest in attestation",
325+
name: "valid VSA record with predicate type",
326326
entry: models.LogEntryAnon{
327327
Attestation: &models.LogEntryAnonAttestation{
328-
Data: strfmt.Base64("c2hhMjU2OmFiYzEyMw=="),
328+
Data: strfmt.Base64("eyJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9jb25mb3JtYS5kZXYvdmVyaWZpY2F0aW9uX3N1bW1hcnkvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicXVheS5pby90ZXN0L2ltYWdlIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImFiYzEyMyJ9fV19"),
329329
},
330330
},
331331
imageDigest: "sha256:abc123",
332332
expected: true,
333333
},
334334
{
335-
name: "entry does not contain digest",
336-
entry: models.LogEntryAnon{
337-
Body: "other-content",
338-
},
339-
imageDigest: "sha256:abc123",
340-
expected: false,
341-
},
342-
{
343-
name: "empty entry",
344-
entry: models.LogEntryAnon{},
345-
imageDigest: "sha256:abc123",
346-
expected: false,
347-
},
348-
{
349-
name: "entry with nil body",
350-
entry: models.LogEntryAnon{
351-
Body: nil,
352-
},
353-
imageDigest: "sha256:abc123",
354-
expected: false,
355-
},
356-
{
357-
name: "entry with non-string body",
335+
name: "entry without attestation",
358336
entry: models.LogEntryAnon{
359-
Body: 123,
337+
Body: "sha256:abc123",
360338
},
361339
imageDigest: "sha256:abc123",
362340
expected: false,
363341
},
364-
}
365-
366-
for _, tt := range tests {
367-
t.Run(tt.name, func(t *testing.T) {
368-
result := entryContainsImageDigest(tt.entry, tt.imageDigest)
369-
assert.Equal(t, tt.expected, result)
370-
})
371-
}
372-
}
373-
374-
func TestIsVSARecord(t *testing.T) {
375-
tests := []struct {
376-
name string
377-
entry models.LogEntryAnon
378-
imageDigest string
379-
expected bool
380-
}{
381342
{
382-
name: "valid VSA record",
343+
name: "entry with nil attestation data",
383344
entry: models.LogEntryAnon{
384345
Attestation: &models.LogEntryAnonAttestation{
385-
Data: strfmt.Base64("c2hhMjU2OmFiYzEyMw=="),
346+
Data: nil,
386347
},
387348
},
388349
imageDigest: "sha256:abc123",
389-
expected: true,
390-
},
391-
{
392-
name: "entry without attestation",
393-
entry: models.LogEntryAnon{
394-
Body: "sha256:abc123",
395-
},
396-
imageDigest: "sha256:abc123",
397350
expected: false,
398351
},
399352
{
400-
name: "entry with nil attestation data",
353+
name: "entry with non-VSA attestation",
401354
entry: models.LogEntryAnon{
402355
Attestation: &models.LogEntryAnonAttestation{
403-
Data: nil,
356+
Data: strfmt.Base64("eyJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9vdGhlci1hdHRlc3RhdGlvbiIsInN1YmplY3QiOlt7Im5hbWUiOiJxdWF5LmlvL3Rlc3QvaW1hZ2UiLCJkaWdlc3QiOnsic2hhMjU2IjoiZGVmNDU2In19XX0="),
404357
},
405358
},
406359
imageDigest: "sha256:abc123",
407360
expected: false,
408361
},
409362
{
410-
name: "entry without matching digest",
363+
name: "entry with malformed attestation data",
411364
entry: models.LogEntryAnon{
412365
Attestation: &models.LogEntryAnonAttestation{
413-
Data: strfmt.Base64("c2hhMjU2OmRlZjQ1Ng=="),
366+
Data: strfmt.Base64("aW52YWxpZC1iYXNlNjQ="),
414367
},
415368
},
416369
imageDigest: "sha256:abc123",

0 commit comments

Comments
 (0)