COR-1258: generate CycloneDX SBOMs alongside scan reports - #142
Conversation
Add `corgea scan --sbom [FILE]` (default bom.json): after a blast scan
completes, generate a CycloneDX SBOM locally from the project tree via
the offline deps engine, alongside any --out-format report.
- Upgrade the CycloneDX emitter from bare 1.4 to spec 1.7: serialNumber,
metadata (timestamp, tool, root component), bom-refs on components,
dependsOn grouped per ref. Output validates against the official
bom-1.7 JSON schema.
- Maven: resolve ${property} / ${project.version} version placeholders
and emit root->dependency graph edges (dependencies array was always
empty for pure-Maven projects).
- Warn on stderr when detected ecosystems (Go, Cargo) are not included
in the SBOM, instead of silently emitting an empty-looking document.
- Error cleanly (exit 2) on nonexistent paths in deps subcommands.
- Tests: e2e scan --sbom against the HTTP stub, Maven property/edge
coverage, warning + path-error CLI tests, emitter unit tests. Scrub
GIT_* env in the repo-info test so it passes inside git hooks.
…inherited-version deps
- Omit component version/purl when the version is unresolved (? placeholder
or ${...} Maven property) instead of emitting null versions and invalid
@? purls; bom-ref keeps the internal id.
- Dedup components by bom-ref and dependsOn per ref (schema uniqueItems).
- Maven: project.version falls back to the parent's version for child POMs
and resolves through properties (${revision} CI versioning).
- scan --sbom write failures exit 1 with a clean error instead of panicking.
All emitter outputs re-validated against the official bom-1.7 schema.
unsupported_ecosystem_label re-encoded which ecosystems produce graph nodes; derive it from ecosystems::is_graphed, defined beside the scanner dispatch so a future Go/Cargo scanner updates both in one place.
- dependencyManagement entries no longer emit components/edges; their
versions fill versionless direct declarations in the same POM.
- Property values resolve to a fixed point (bounded, cycle-safe) so
chained ${...} references fully substitute.
- Empty versions count as unresolved in the SBOM emitter (no bare @ purls).
Parent/imported BOM resolution stays deferred to COR-1733.
…lution
- to_cyclonedx filters the synthetic root by id, not display name, so a
real package named 'root' keeps its component (no dangling dependsOn).
- pom_project_version stops at the first nested section
(dependencyManagement/build/reporting/profiles) so plugin versions can't
hijack ${project.version}; parent fallback unchanged.
- Property fixed-point resolution re-runs after project.version is
inserted so properties aliasing ${project.version} resolve.
split_dependency_management and pom_project_version's parent handling used the same find-open/find-close/remove-section scanning; extract it into split_section. Compare the synthetic root via PackageId::root() instead of a raw string literal, matching the rest of the codebase.
There was a problem hiding this comment.
Automated review risk: 4/5.
The SBOM can silently contain false Maven dependencies or omit resolvable versions because parsing remains structurally overbroad and property resolution is arbitrarily capped.
Critical or high-priority changes must be addressed.
Automatic approval was not submitted: automated review found critical or high-priority findings.
…ution - Strip <profiles> and <build> sections before dependency extraction so plugin dependencies and profile dependencies no longer surface as application dependencies in the graph/SBOM. - Bound the property fixed-point loop by the property count instead of a fixed 5: a chain can't be longer than the map without a cycle.
|
Re the automated risk-4 review (no inline findings attached): both concerns it gestures at are now addressed in 7e5c718.
Remaining Maven gaps (parent POM chains, imported BOMs, profile activation) are effective-POM resolution, deliberately out of scope here and tracked in COR-1733. |
|
@juangaitanv Regarding #142 (comment): I agree with this finding and think it should be addressed. high: Inactive profile dependency management leaks into direct dependencies dependencyManagement is extracted before profiles are stripped. Consequently, an inactive profile's managed versions enter the global managed map and can assign a false version to a versionless top-level dependency. Profile activation may be out of scope, but inactive profile data must not affect the base graph. Proof or reproduction: |
| } | ||
|
|
||
| if let Some(sbom_file) = sbom { | ||
| match corgea::deps::report::sbom(std::path::Path::new(".")) { |
There was a problem hiding this comment.
high: Scan SBOM ignores the selected target
Blast receives the user's target, but SBOM generation always scans Path::new("."). Thus scan --target service-a --sbom can include dependencies from unrelated sibling projects and omit alignment with the content actually uploaded and scanned.
Proof or reproduction:
Create service-a/package.json depending on a@1 and service-b/package.json depending on b@1, then run `corgea scan --target service-a --sbom`. The SBOM scan starts at `.` and can contain both a and b rather than only the selected target.
There was a problem hiding this comment.
Automated review risk: 4/5.
The SBOM can silently misrepresent Maven dependencies and ignores the scan target, creating material SCA accuracy risks.
Critical or high-priority changes must be addressed.
Automatic approval was not submitted: automated review found critical or high-priority findings.
Summary
Implements COR-1258: generate a CycloneDX (1.7, latest) SBOM immediately after a scan, alongside the SARIF report.
corgea scan --sbom [FILE]flag (defaultbom.json): after the blast scan completes and any--out-formatreport is written, a CycloneDX SBOM is generated locally from the project tree via the offlinedepsengine — no server changes, no network.serialNumber(urn:uuid),metadata(timestamp, tool, root component),bom-refon every component,dependsOngrouped per ref. Output validates against the officialbom-1.7.schema.json;corgea deps sbomshares the same emitter.${property}/${project.version}version placeholders now resolve before purl construction, and the scanner emits root→dependency edges (thedependenciesarray was always empty for pure-Maven projects).depssubcommands error cleanly (exit 2) on nonexistent paths instead of succeeding with empty output.Compliance enrichment (licenses, scope, hashes, Go/Cargo coverage) is tracked separately in COR-1733.
Test plan
cargo test— 543 passed, 0 failed (clippy clean, fmt clean)tests/cli_scan_sbom.rs: full stubbed scan flow → defaultbom.json, custom filename, no file without the flagmaven_tests.rs+ newjava-maven-propsfixturecli_deps.rs