Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pkg/clouds/pulumi/docker/security_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ func buildSecurityReportScript(imageRef, imageName string, security *api.Securit

// Build the report script. Uses jq for JSON parsing if available, falls back to grep.
// The script is self-contained — no SC CLI dependency.
//
// The heading includes imageName so stacks with multiple images (e.g. web+worker)
// produce visibly-distinct report sections in $GITHUB_STEP_SUMMARY — otherwise
// identical-looking headers make them appear as duplicates.
var sb strings.Builder
sb.WriteString("set +e\n") // Don't exit on error — report is best-effort
sb.WriteString("REPORT=''\n")
sb.WriteString("REPORT=\"${REPORT}## Security Pipeline Summary\\n\\n\"\n")
sb.WriteString(fmt.Sprintf("REPORT=\"${REPORT}## Security Pipeline Summary — %s\\n\\n\"\n", shellEscape(imageName)))
sb.WriteString(fmt.Sprintf("REPORT=\"${REPORT}**Image:** \\`%s\\`\\n\\n\"\n", shellEscape(imageRef)))
sb.WriteString("REPORT=\"${REPORT}| Step | Status | Details |\\n\"\n")
sb.WriteString("REPORT=\"${REPORT}| --- | --- | --- |\\n\"\n")
Expand Down
32 changes: 28 additions & 4 deletions pkg/security/scan/trivy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

// DefaultTrivyVersion is the pinned install version. Bump here to upgrade cluster-wide,
// or override per-scan via SC config (ScanToolConfig.Version) or SC_TRIVY_VERSION env var.
const DefaultTrivyVersion = "0.69.3"
const DefaultTrivyVersion = "0.70.0"

// TrivyScanner implements Scanner interface using Trivy
type TrivyScanner struct {
Expand Down Expand Up @@ -46,6 +46,7 @@ func (t *TrivyScanner) Scan(ctx context.Context, image string) (*ScanResult, err
if err != nil {
return nil, err
}
defer cleanupTrivyCacheDir(cacheDir)

// Scan from the local Docker daemon (docker-daemon: prefix).
// The image must already be present locally — callers are expected to scan
Expand Down Expand Up @@ -303,18 +304,41 @@ func parseTrivyVersion(output string) (string, error) {
return "", fmt.Errorf("failed to parse trivy version from: %s", output)
}

// ensureTrivyCacheDir returns a per-invocation Trivy cache directory under the
// user cache root. Using a unique subdirectory per scan eliminates the cache
// lock contention that Trivy exhibits when the same directory is shared across
// concurrent processes or reused with stale locks from crashed runs. The cost
// is re-downloading the vulnerability DB per scan (~100MB, a few seconds),
// which is acceptable in CI and avoids the "cache may be in use by another
// process: timeout" failure mode documented at
// https://trivy.dev/docs/guide/references/troubleshooting#database-and-cache-lock-errors
//
// Caller is responsible for cleanup (see cleanupTrivyCacheDir).
func ensureTrivyCacheDir() (string, error) {
cacheRoot, err := os.UserCacheDir()
if err != nil {
cacheRoot = os.TempDir()
}
cacheDir := filepath.Join(cacheRoot, "trivy")
if err := os.MkdirAll(cacheDir, 0o755); err != nil {
return "", fmt.Errorf("create trivy cache directory: %w", err)
parent := filepath.Join(cacheRoot, "trivy")
if err := os.MkdirAll(parent, 0o755); err != nil {
return "", fmt.Errorf("create trivy cache parent directory: %w", err)
}
cacheDir, err := os.MkdirTemp(parent, "scan-*")
if err != nil {
return "", fmt.Errorf("create trivy cache scratch directory: %w", err)
}
return cacheDir, nil
}

// cleanupTrivyCacheDir removes a per-invocation cache directory created by
// ensureTrivyCacheDir. Best-effort: errors are ignored.
func cleanupTrivyCacheDir(cacheDir string) {
if cacheDir == "" {
return
}
_ = os.RemoveAll(cacheDir)
}

func trivyDBPresent(cacheDir string) bool {
return fileExists(filepath.Join(cacheDir, "db", "metadata.json"))
}
Expand Down
19 changes: 18 additions & 1 deletion pkg/security/scan/trivy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,24 @@ func TestEnsureTrivyCacheDir(t *testing.T) {

cacheDir, err := ensureTrivyCacheDir()
Expect(err).ToNot(HaveOccurred())
Expect(cacheDir).To(Equal(filepath.Join(cacheRoot, "trivy")))
defer cleanupTrivyCacheDir(cacheDir)

// Per-invocation cache dir lives under <cacheRoot>/trivy/scan-* so
// concurrent scans can't clobber each other's lock files.
parent := filepath.Join(cacheRoot, "trivy")
Expect(cacheDir).To(HavePrefix(parent+string(filepath.Separator)+"scan-"))
Expect(cacheDir).To(BeADirectory())

// Second call returns a different directory (thread-safety property).
cacheDir2, err := ensureTrivyCacheDir()
Expect(err).ToNot(HaveOccurred())
defer cleanupTrivyCacheDir(cacheDir2)
Expect(cacheDir2).ToNot(Equal(cacheDir))

// Cleanup removes the directory.
cleanupTrivyCacheDir(cacheDir)
_, statErr := os.Stat(cacheDir)
Expect(os.IsNotExist(statErr)).To(BeTrue())
}

func TestTrivyDBPresenceHelpers(t *testing.T) {
Expand Down
Loading