fix(sbom): stop sbom validation failing on duplicate component links - #326
reyreavman wants to merge 6 commits into
Conversation
Verification
Review focus
Follow-up
|
Fral738
left a comment
There was a problem hiding this comment.
Two interactions in the checker image make the newly exposed checks behave differently from their CLI contract:
- Combining
--check-source-distributionwith--check-vcs-leaf-onlysilently skips source archives on non-leaf components. A dead parent archive changes from failure to success just by enabling the VCS modifier. --check-source-distributionalone also enables VCS validation, despite--check-vcs=falseand the log reporting only a source distribution check.
The inline comments contain reproductions and concrete corrections. They were reproduced through the built CLI against checker image digest sha256:d9a95347a28b4bbbd6163b0c0dc5215e0e5b5a60acc2d519a55cc5bca86e6c49.
There is also a description/test claim to correct: "a component carrying only that reference is not an error" is not true of the source-distribution reference produced by the enricher. The ISPRAS schema requires hashes, including a STREEBOG hash, when a leaf has no vcs reference. The new acceptance test only checks the reference type; the resolver outcome supplies no hashes. A minimal otherwise-valid OSS BOM with that reference fails werf sbom validate --ispras-format oss --path <bom> with ERROR: 'hashes' is a required property, exit 1, even without URL checks. Missing hashes predate this PR; please qualify the description and the test's claimed coverage rather than presenting source-only output as proven valid. If schema-valid source-only output is intended in this PR, supply the required hash and test the serialized BOM against the checker.
Verification: task build and the full task test:unit passed. Mutations disabling duplicate suppression, changing the allow-list, and omitting the new Docker arguments were detected by the corresponding tests; restored runs passed. Full image-build e2e/integration suites were not run.
576f9b0 to
b952906
Compare
Fral738
left a comment
There was a problem hiding this comment.
The previous flag findings are addressed by the early combination guard and the updated help/logging contract. The remaining findings are in the new source-distribution repair path:
- An inherited
hashes: []is treated as an existing digest, so enrichment succeeds without repairing the hash-less reference despite a valid resolver result. - Replacing one component's hash-less reference deletes the same URL/type from the BOM-wide reference set even when another component retains that reference with a valid digest.
Both cases were reproduced with focused Go tests on this head; the inline comments include the scenarios and corrections. The first also qualifies the description's claim that an inherited source distribution without a digest is replaced: it currently only handles a nil hashes pointer.
Verification: task build and the full task test:unit passed. Mutations of hash validation/copying/replacement and checker guard/logging were detected; restored runs passed and the worktree is clean. The built CLI rejects the incompatible flags before file access or container startup. The source-only serialized BOM has not been revalidated against the checker in this pass: the local Docker socket is absent and SSH to the available Linux host timed out. Full image-build e2e/integration suites were not run.
The ISPRAS checker can verify that a source-distribution url exists and points to an archive, and can restrict vcs checks to leaf components, but neither switch was reachable: only --check-vcs was passed to the container. A dead source distribution link therefore passed validation silently. Expose both as --check-source-distribution and --check-vcs-leaf-only. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
A component that already carries an external reference of the resolved kind got a second one appended: os-pm supplies a vcs link of its own, and every downstream image re-enriches an already enriched BOM. Two links of the same type make the ISPRAS checker report the component, which werf treats as a validation failure. Keep the reference already present and skip the resolved one. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…ccept The resolver response was accepted for any CycloneDX reference type, while the ISPRAS schema demands a vcs or a source-distribution link per component. A website or issue-tracker answer therefore produced a green build and an SBOM that fails validation later, away from the change that caused it. Fail the build on any other kind instead. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…ut a digest The PURL resolver reports a STREEBOG (GOST R 34.11-2012) digest of the archive next to a source-distribution url, but the resolve response was decoded into a struct without a hashes field, so the digest was dropped and the external reference entered the SBOM with a url alone. The ISPRAS oss schema requires a leaf component whose only link is a source distribution to carry that digest, so every such component made the published SBOM unvalidatable while the build stayed green. Hashes now travel from the resolver into the external reference, and enrichment rejects a source distribution it could not be validated with: no digest at all, an algorithm other than STREEBOG-256 or STREEBOG-512, or content that is not 64 respectively 128 hexadecimal characters. A vcs reference stays free of hashes, as the schema expects. A source distribution left without a digest by an earlier enrichment — the merged BOM of a base image built before this change — is replaced by the resolved one instead of being kept: that base image cannot be fixed from the downstream build, and keeping its link would carry the unvalidatable reference into this SBOM too. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…bution checks The ISPRAS checker image applies --check-vcs-leaf-only before it reads a component's external references, so combined with --check-source-distribution the archives of every non-leaf component went unchecked while the run reported success. Reject the combination up front with an error naming --check-vcs as the way out. The same image also turns on VCS URL validation whenever --check-source-distribution is passed, so a run with that flag alone can fail on an unreachable repository. Say so in the flag help and in the "with ... check" header, which now lists the checks the image really runs instead of the flags that were passed. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…d keep shared links An inherited source-distribution reference with "hashes": [] passed the "already has a digest" check because only a nil pointer counted as missing, so the valid replacement from the resolver was discarded and the SBOM kept a source-only leaf without the digest the ISPRAS schema requires. Treat an empty hash list as missing too. Replacing one component's hash-less reference also deleted its URL/type from the BOM-wide externalReferences list while another component still carried that link with a valid digest. Derive the BOM-wide list from the final component references after all replacements instead of maintaining it incrementally. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
32c896c to
ea48a76
Compare
Fral738
left a comment
There was a problem hiding this comment.
The description's claim that "every component ends up with at most one reference per type" is stronger than the implementation. Enrich prevents appending another reference of that type, but does not collapse references already present on the component. DedupBOM only removes identical reference objects, so distinct inherited URLs survive that step too.
With both git://example.com/lodash.git and https://github.com/lodash/lodash already present as vcs, Enrich returns nil and retains both. Adding that second input reference to the existing keeps a single reference of a type the component already has test makes its length assertion fail (2, expected 1). An otherwise-valid OSS fixture with those references still produces the checker's contains 2 vcs links warning and CLI exit 1. This is an existing limitation, not a new regression introduced by the latest fixes.
Please qualify the description, for example: "Enrichment no longer adds a second reference of a type already present. Existing duplicate references inherited from older SBOMs are not cleaned up by this change." This documents the actual scope without requiring duplicate repair to be added to this PR.
|
Agreed, the claim overstated the implementation: |
Summary
An image SBOM could carry two external references of the same type for one component, which the ISPRAS checker reports and werf turns into a validation failure. The enrichment step also accepted any CycloneDX reference type from the PURL resolver and dropped the archive digest the resolver reports for a source distribution, so a green build could produce an SBOM that fails validation afterwards.
werf sbom validatenow reaches the checker's source-distribution and leaf-only VCS checks, and refuses the one combination the checker does not honor.What
Enrichment
DedupBOMremoves only identical reference objects, so two distinctvcsURLs on one component still survive and still trip the checker'scontains 2 vcs linkswarning.vcsorsource-distribution, withenrich: external reference kind "<kind>" is not allowed, expected "vcs" or "source-distribution". Such a kind previously produced an SBOM that failed ISPRAS validation later; a resolver returning e.g.websitenow stops the build instead.source-distributionreference carries the STREEBOG digest the resolver reports inhashes; avcsreference carries none.source-distributionwithout a usable digest — nohashes, an algorithm other thanSTREEBOG-256/STREEBOG-512, or content that is not 64/128 hex characters — withenrich: source distribution has no hashes …or the matching algorithm/content error. Previously such a component entered the SBOM as a leaf with a bare archive URL, which the ISPRAS oss schema rejects.hashes: [{"alg":"STREEBOG-256",…}]forpkg:npm/commondir@1.0.1andhashes: []for vcs results.hashesabsent orhashes: []— is replaced by the freshly resolved one instead of being kept; one that already carries a digest is kept.externalReferenceslist is derived from the final component references: a link replaced on one component stays listed while another component still carries it with a digest.werf sbom validate--check-source-distributionmakes the checker verify that each source-distribution URL exists and points to an archive; default false. It also turns on VCS URL validation of every component, exactly as--check-vcsdoes — that is how the checker image behaves, and the flag help says so.--check-vcs-leaf-onlyrestricts VCS URL checking to leaf components; default false.--check-vcs-leaf-onlytogether with--check-source-distributionis rejected before the checker runs, with--check-vcs-leaf-only cannot be combined with --check-source-distribution: the checker would skip source distributions of non-leaf components; use --check-vcs instead. The checker applies the leaf filter before reading any reference, so the combination silently left non-leaf archives unchecked.--check-source-distributionalone yieldswith VCS, source distribution check;--check-vcs --check-vcs-leaf-onlyyieldswith leaf-only VCS check.--check-source-distributiona dead tarball URL yieldsWARNING: … не указывает на архив или не существуетandResult: 0 passed, 1 failed; without the flag the same file passes that check silently.source-distributionreference with hashes has not been run through the ISPRAS checker image; the schema (component_spec→externalReferenceswith STREEBOGhashes) was read, not executed. A fixture run against the pinned checker image would settle it.Why
Two independent producers write external references — os-pm for system packages and the PURL resolver — and the resolver appended unconditionally, so a component resolved to a kind it already had ended up with two links. Enrichment also runs on the merged BOM of every downstream image, and it runs after
DedupBOM, so each merge appended another copy. The ISPRAS checker warns on a component with two distinct vcs links and werf treats any warning as a failure, which makes the published SBOM unvalidatable.Deduplicating by URL and type would not have been enough: the checker collapses identical links and only reports distinct ones, which is exactly the os-pm plus resolver case.
The enrichment allow-list was the full CycloneDX enum while the ISPRAS schema requires
vcsorsource-distributionper component, so the two disagreed and the disagreement surfaced at validation time rather than at the build that caused it. The same schema requires a leaf whose only link is a source distribution to carry a STREEBOG digest of the archive; the resolver already computes it, but the response struct had no field for it.The two validate flags leak the checker image's internals: its
--check-vcs-leaf-onlyis acontinueahead of any reference parsing, and its VCS collection is not gated by--check-vcs. Fixing the image would be the right long-term answer, but the image is consumed by a floating tag, so a werf-side guard is the only thing that makes the CLI contract hold for whatever image is pulled. Running the checker twice — leaf-only VCS in one pass, source distribution in another — is not an alternative: the second pass would still run the full VCS check.