feat(helmExecute): generate SBOM on the publish path and build traceability - #5856
feat(helmExecute): generate SBOM on the publish path and build traceability#5856fabienfritz wants to merge 1 commit into
Conversation
|
/it-go |
1 similar comment
|
/it-go |
…bility Add CycloneDX 1.4 SBOM generation to the helmExecute step for SLC-41 compliance. When createBOM is enabled and the chart is published, helmExecute now emits: - bom-helm.xml: a chart-level SBOM (hand-built with cyclonedx-go) whose root component carries a pkg:helm PURL, with sub-chart dependencies (Chart.lock resolved versions winning over Chart.yaml ranges) and referenced images as components. - bom-docker-<N>.xml: a per-image SBOM produced by Syft, with a clean registry-free docker root PURL injected to satisfy CycloneDX validation (works around anchore/syft#1408). Container images are discovered via `helm template`, falling back to the containerImageNameTags CPE list when templating yields nothing. All SBOM generation is best-effort: a failure is logged but never fails the step. Also populate custom/buildSettingsInfo for SLC-29 build traceability, and expose the outputs through the reports framework (**/bom-*.xml, type: sbom). Metadata: add createBOM, syftDownloadUrl, containerImageNameTags and buildSettingsInfo inputs and regenerate the step. Tests: - unit coverage for image discovery, chart/container SBOM assembly, PURL injection and buildSettingsInfo. - a full publish+SBOM integration test (TestHelmIntegration) that publishes to a webdav sink and validates the generated bom-helm.xml against CycloneDX 1.4.
e95849e to
ec39ed3
Compare
|
/it-go |
fskhiri
left a comment
There was a problem hiding this comment.
No blocker, to findings worth look at it. Let me know what do you think ?
| // component of every bom-docker-*.xml produced by Syft. Syft does not emit a | ||
| // PURL for the parent component (anchore/syft#1408), which makes the BOM fail | ||
| // CycloneDX validation. Best-effort: individual failures are logged, not fatal. | ||
| func injectContainerBOMPurls() error { |
There was a problem hiding this comment.
injectContainerBOMPurls duplicates kaniko's canonical PURL-injection block
cmd/helmExecute.go adds injectContainerBOMPurls() (diff lines ~183–217). This is a near-verbatim copy of the PURL-injection half of createDockerBuildArtifactMetadata in cmd/kanikoExecute.go:455–528
Duplicating an existing canonical helper. The right move is to extract the shared logic once and call it from both steps.
| return nil | ||
| } | ||
|
|
||
| // RunHelmTemplate renders the chart locally (`helm template`) and returns the |
There was a problem hiding this comment.
RunHelmTemplate swaps global stdout on the executor — a new, race-adjacent pattern
Why this is a smell
The problem is that h.utils is shared state — it's one object the whole struct uses. This method mutates it,
does its work, then restores it. Think of it like grabbing the office printer, secretly rerouting
everyone's print jobs to your desk for 10 seconds, then putting it back. It works if you're the only person
printing at that moment. Two concrete risks:
-
Concurrency. If two things ever use the same executor at the same time, one call rebinding stdout would
hijack the other's output. Today helmExecute runs single-threaded so it's fine — but it's a landmine for
anyone who later parallelizes. -
The restore can be skipped. defer protects against most cases, but the pattern is fragile — the
correctness of every other method now quietly depends on this one method having cleaned up after itself. The
fact that the tests had to add a dedicated check "stdout is restored after the call" is the tell: you only
write a test asserting you cleaned up your mess if the design makes messes possible.
The cleaner alternative
Instead of rewiring shared state, capture at the boundary — ask the command to give you its output directly
into a local variable, without ever touching h.stdout:
// conceptually: run helm and hand ME the output, don't touch the shared writer
output, err := h.utils.RunExecutableWithOutput("helm", helmParams...)
Now nothing shared is mutated, there's no restore step, no defer, no race, and no need for a test proving
you undid your change. The output capture is local to this one call.
Add CycloneDX SBOM Generation and Build Traceability to
helmExecuteStepDescription
This PR adds CycloneDX 1.4 SBOM generation to the
helmExecutestep for SLC-41 compliance, and populatesbuildSettingsInfoin the common pipeline environment for SLC-29 build traceability.SBOM Generation (SLC-41)
When
createBOM: trueis set and the chart is published,helmExecutenow emits:bom-helm.xml: A chart-level SBOM (built withcyclonedx-go) whose root component carries apkg:helmPURL. Sub-chart dependency versions are resolved usingChart.lock(winning overChart.yamlranges), and referenced images are included as container components.bom-docker-<N>.xml: Per-image SBOMs produced by Syft. A clean, registry-freepkg:dockerPURL is injected into the root component to satisfy CycloneDX validation (workaround foranchore/syft#1408).Container images are discovered via
helm templateand fall back to thecontainerImageNameTagsCPE list when templating yields no images. SBOM generation is best-effort — failures are logged but never fail the step.Build Traceability (SLC-29)
buildSettingsInfois now populated intocommonPipelineEnvironment.custom.buildSettingsInfo, recording helm build flags for compliance processes.New Parameters
createBOMsyftDownloadUrlcontainerImageNameTagskanikoExecutebuildSettingsInfoSBOM Reports Output
SBOM files matching
**/bom-*.xmlare now registered assbomreports and can be persisted to GCS.Tests
buildSettingsInfopopulationTestHelmIntegrationPublishWithSBOM) that publishes to a WebDAV sink and validates the generatedbom-helm.xmlagainst CycloneDX 1.4 schemaChecklist
Tests
Documentation
Inner source library needs updating
🔄 Regenerate and Update Summary
PR Bot Information
Version:
1.29.676bc11a0-8b74-11f1-93c3-d9c03e937050issue_comment.edited