Skip to content

Commit 3bf7c45

Browse files
leodidoona-agent
andcommitted
fix: upload and download provenance bundles in S3 cache
When building packages with SLSA enabled, provenance bundles are stored alongside artifacts as <artifact>.provenance.jsonl. These bundles are needed for dependency provenance collection during local builds. Previously, only the artifact (.tar.gz) and attestation (.tar.gz.att) were uploaded/downloaded from S3, causing builds to fail with: "error accessing provenance bundle: no attestation bundle found" This fix: - Uploads .provenance.jsonl files alongside artifacts (non-blocking) - Downloads .provenance.jsonl files after SLSA verification (best effort) - Gracefully handles missing provenance for backward compatibility The provenance download is non-critical and logs a debug message if missing, allowing older artifacts without provenance to work correctly. Fixes dependency provenance collection for SLSA L3 compliant builds. Co-authored-by: Ona <no-reply@ona.com>
1 parent c103fb2 commit 3bf7c45

File tree

1 file changed

+78
-0
lines changed
  • pkg/leeway/cache/remote

1 file changed

+78
-0
lines changed

pkg/leeway/cache/remote/s3.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,42 @@ func (s *S3Cache) downloadWithSLSAVerification(ctx context.Context, p cache.Pack
578578
continue
579579
}
580580

581+
// Step 6: Download provenance bundle if it exists (best effort, non-blocking)
582+
// Provenance bundles are stored alongside artifacts as <artifact>.provenance.jsonl
583+
// This is needed for dependency provenance collection during local builds
584+
provenanceKey := artifactKey + ".provenance.jsonl"
585+
provenancePath := localPath + ".provenance.jsonl"
586+
tmpProvenancePath := provenancePath + ".tmp"
587+
588+
// Try to download provenance bundle (non-critical, don't fail if missing)
589+
if err := s.waitForRateLimit(ctx); err == nil {
590+
downloadCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
591+
defer cancel()
592+
593+
if _, err := s.storage.GetObject(downloadCtx, provenanceKey, tmpProvenancePath); err == nil {
594+
// Successfully downloaded, move to final location
595+
if err := s.atomicMove(tmpProvenancePath, provenancePath); err != nil {
596+
log.WithError(err).WithFields(log.Fields{
597+
"package": p.FullName(),
598+
"key": provenanceKey,
599+
}).Debug("Failed to move provenance bundle, continuing")
600+
s.cleanupTempFiles(tmpProvenancePath)
601+
} else {
602+
log.WithFields(log.Fields{
603+
"package": p.FullName(),
604+
"key": provenanceKey,
605+
}).Debug("Successfully downloaded provenance bundle")
606+
}
607+
} else {
608+
// Provenance not found - this is expected for older artifacts
609+
log.WithFields(log.Fields{
610+
"package": p.FullName(),
611+
"key": provenanceKey,
612+
}).Debug("Provenance bundle not found in remote cache (expected for older artifacts)")
613+
s.cleanupTempFiles(tmpProvenancePath)
614+
}
615+
}
616+
581617
// Clean up temporary attestation file
582618
s.cleanupTempFiles(tmpAttestationPath)
583619

@@ -967,6 +1003,39 @@ func (s *S3Cache) Upload(ctx context.Context, src cache.LocalCache, pkgs []cache
9671003
"package": p.FullName(),
9681004
"key": key,
9691005
}).Debug("successfully uploaded package to remote cache")
1006+
1007+
// Upload provenance bundle if it exists
1008+
// Provenance bundles are stored alongside artifacts as <artifact>.provenance.jsonl
1009+
provenancePath := localPath + ".provenance.jsonl"
1010+
if fileExists(provenancePath) {
1011+
provenanceKey := key + ".provenance.jsonl"
1012+
1013+
// Wait for rate limiter permission
1014+
if err := s.waitForRateLimit(ctx); err != nil {
1015+
log.WithError(err).WithFields(log.Fields{
1016+
"package": p.FullName(),
1017+
"key": provenanceKey,
1018+
}).Warn("rate limiter error during provenance upload - continuing")
1019+
// Don't add to uploadErrors - provenance is optional
1020+
return nil
1021+
}
1022+
1023+
timeoutCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
1024+
defer cancel()
1025+
if err := s.storage.UploadObject(timeoutCtx, provenanceKey, provenancePath); err != nil {
1026+
log.WithError(err).WithFields(log.Fields{
1027+
"package": p.FullName(),
1028+
"key": provenanceKey,
1029+
}).Warn("failed to upload provenance bundle to remote cache - continuing")
1030+
// Don't add to uploadErrors - provenance is optional
1031+
} else {
1032+
log.WithFields(log.Fields{
1033+
"package": p.FullName(),
1034+
"key": provenanceKey,
1035+
}).Debug("successfully uploaded provenance bundle to remote cache")
1036+
}
1037+
}
1038+
9701039
return nil
9711040
})
9721041

@@ -1222,3 +1291,12 @@ func (s *S3Storage) ListObjects(ctx context.Context, prefix string) ([]string, e
12221291

12231292
return result, nil
12241293
}
1294+
1295+
// fileExists checks if a file exists and is not a directory
1296+
func fileExists(filename string) bool {
1297+
info, err := os.Stat(filename)
1298+
if err != nil {
1299+
return false
1300+
}
1301+
return !info.IsDir()
1302+
}

0 commit comments

Comments
 (0)