[PoC] Add GetBuildFileTrees API to extract manifest build files and their relations from SBOMs#157
Conversation
…from CycloneDX SBOMs - Add FileType constants for 12 manifest file types (pom.xml, Cargo.toml, package.json, etc.) - Add BuildFile struct with FileType, FilePath, and RepoPath fields - Implement GetBuildFileTrees parsing CycloneDX evidence occurrences to identify manifest files - Support filtering by FileType, deduplication, and wildcard .csproj matching - Add 10 comprehensive tests covering manifest extraction, lockfile exclusion, filtering, deduplication, subdirectory paths, csproj wildcard, empty/nil input, and unknown manifest types Rationale: Downstream consumers (e.g. the transitive dependency tree feature) need to identify which build/manifest files are present in a generated SBOM. This function parses CycloneDX evidence occurrences and returns a deduplicated map of manifest BuildFiles, laying the groundwork for build file tree construction in a future PR. This commit made by [/dd:git:commit:atomic](https://github.com/DataDog/claude-marketplace/tree/main/dd/commands/git/commit/atomic.md)
- Update examples/main.go to call GetBuildFileTrees on the generated SBOM - Print count of manifest build files found and each BuildFile's type, path, and repo path - Add Cargo.toml to testdata so the example produces visible output by default - Sort output deterministically for reproducible display Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
🎯 Code Coverage (details) 🔗 Commit SHA: d359f61 | Docs | Datadog PR Page | Give us feedback! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb475d2808
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| // exactFileTypes maps exact basenames to their FileType. | ||
| // build.gradle.kts is checked before build.gradle to avoid false matches. | ||
| var exactFileTypes = map[string]FileType{ |
There was a problem hiding this comment.
Include all supported manifest basenames
GetBuildFileTrees drops every manifest whose basename is not in this classifier. That misses SBOMs this generator actually emits for supported manifests such as Go (parse-go-lock.go marks go.mod occurrences as LocationRoleManifest) and requirements variants (RequirementsTxtExtractor.ShouldExtract accepts any requirements*.txt), so Go projects or files like requirements-dev.txt have manifest evidence in the SBOM but are absent from the returned build-file map. Please extend this classifier/pattern matching to cover the supported manifest inputs before applying the filter.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6dda04a7e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| continue | ||
| } | ||
|
|
||
| ft := fileTypeFromBasename(filepath.Base(loc.Block.FileName)) |
There was a problem hiding this comment.
Normalize requirements variants before filtering
When callers pass FileTypeRequirementsTxt to filter for requirements manifests, supported files such as requirements-dev.txt are silently excluded: RequirementsTxtExtractor.ShouldExtract accepts any basename containing requirements and ending in .txt, and those occurrences are emitted with role=manifest, but this classifier returns the exact basename unless it is .csproj. As a result GetBuildFileTrees(sbom, FileTypeRequirementsTxt) returns nothing for these supported requirements SBOMs even though an unfiltered call would include them under a different ad-hoc FileType.
Useful? React with 👍 / 👎.
a9399ea to
8b52b94
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b52b94fe0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| r.Infof("Scanning directory '%s', resolved absolute path '%s'\n", dir, absolutePath) | ||
| pkgs, artifacts, err := scanDir(r, dir, repoRoot, actions.Recursive, !actions.NoIgnore, enabledParsers, actions.ExcludePaths, configExcludePaths) | ||
| pkgs, artifacts, err := scanDir(r, dir, repoRoot, actions.Recursive, !actions.NoIgnore, enabledParsers, actions.ExcludePaths, configExcludePaths, actions.ExtractMavenPomArtifactIds) |
There was a problem hiding this comment.
Preserve Maven artifact extraction by default
When the CLI calls DoScan, cmd/datadog-sbom-generator/scan/main.go constructs ScannerActions without setting this newly added option, so the zero value false is passed here and ExtractDeps skips every ArtifactExtractor. For Maven scans this drops the pom.xml file components and dependency edges that were previously emitted in CycloneDX output, which is visible in the updated CLI snapshots; either make the scanner default preserve the old behavior or set the new option in the CLI path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
No, it is intended to be removed
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2123c40977
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| DirectoryPaths: dirs, | ||
| ExcludePaths: opts.ExcludePaths, | ||
| Recursive: opts.Recursive, | ||
| ExtractMavenPomArtifactIds: opts.ExtractMavenPomArtifactIds, |
There was a problem hiding this comment.
Restore Maven artifacts for custom options
When library callers build Options themselves (for example Options{Recursive: true} or any existing literal that predated this field), this new bool remains false and is passed through to DoScan, so ExtractDeps no longer runs ArtifactExtractor.GetArtifact for Maven pom.xml files. Before this change those callers always got the Maven file components and parent dependency edges in the generated CycloneDX SBOM; only callers that happen to use DefaultOptions() preserve that behavior.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
No, it is intended to be removed
2123c40 to
667ba52
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 667ba52cbe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if loc.Block.Role != "manifest" || loc.Block.FileName == "" { | ||
| continue |
There was a problem hiding this comment.
Inspect all occurrence sublocations for manifests
When a dependency occurrence has manifest evidence in name, namespace, or version whose filename differs from the declaration block, this only reads block and drops those files. Maven inheritance/property resolution already emits such occurrences (for example a child dependency block can have its version location in a parent pom.xml), so GetBuildFileTrees can omit a parent build file if it has no dependency block of its own. Please scan every PackageLocations sublocation with role=manifest, not just block.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This is a partial case, covering with the following comment
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e4709f1185
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if comp.Evidence == nil || comp.Evidence.Occurrences == nil { | ||
| continue |
There was a problem hiding this comment.
Include file-only POM components in the tree
When a Maven parent/aggregator POM has no dependency declarations of its own, the generated CycloneDX SBOM can still contain it as a file component and dependency target, but it has no evidence occurrences. This branch skips every component without Evidence.Occurrences, so that POM is never added to filesByPath; MavenProcessor then ignores child → parent edges to it and returns children with no parent, producing an incomplete build-file tree for common multi-module layouts where the root POM only defines modules/properties.
Useful? React with 👍 / 👎.
…name - Remove exactFileTypes map — FileType is now the basename from the SBOM occurrence - fileTypeFromBasename simplified: only normalises dynamic-suffix types (*.csproj) - Any role=manifest occurrence is now included regardless of known FileType constants - Update TestDefaultFileTypeFromFilename → TestFileTypeFromBasename to match new signature - Update TestGetBuildFileTrees_UnknownManifestIgnored → UnknownManifestIncluded Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…ArtifactIds flag The Maven pom.xml filename and dependsOn relationship tags are only needed for SBOM-to-artifact mapping in downstream resolution logic, not for general SBOM generation. Gate this behind ScanContext.ExtractArtifacts (false by default) and thread it through ScannerActions.ExtractArtifacts. In the library API (pkg/sbomgen), DefaultOptions() sets ExtractArtifacts: true so callers using GenerateSBOM get the richer output without extra configuration. The CLI leaves the flag unset, keeping its output free of file components and dependsOn entries. Also fix a pre-existing typo: BuildFclle -> BuildFile in pkg/sbomgen/build_files.go. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…BuildFileTrees
Introduce BuildFileProcessor interface and a global registry so that each
FileType can have a dedicated processor. GetBuildFileTrees now:
1. Collects and deduplicates manifest occurrences from the SBOM
2. Groups them by FileType
3. Dispatches each group to its registered processor (noopProcessor as
fallback, preserving existing behaviour)
4. Merges all processor results into the returned map
RegisterBuildFileProcessor lets future processor packages (e.g. Maven)
register themselves via init(). No behaviour change for existing callers.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
e4709f1 to
b9f302d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9f302dd6d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for _, dep := range ctx.FileDependencies[f.FilePath] { | ||
| if _, ok := filesByPath[dep]; ok { | ||
| parentByChild[f.FilePath] = dep | ||
| break |
There was a problem hiding this comment.
Don't treat module dependencies as Maven parents
When a Maven POM has dependencies on other local modules, the SBOM dependency entry can contain those module POMs as well as the real parent POM: internal/output/sbom/cyclonedx.go adds artifact.Filename for internal Maven dependencies and sorts the list. This loop then treats the first known POM dependency as the parent, so cases like a module depending on a sibling module (or a root POM depending on a child module) produce incorrect parent/child build-file trees even though the edge is not from the <parent> element.
Useful? React with 👍 / 👎.
b9f302d to
1644730
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1644730886
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // addBuildFile adds a build file identified by filePath to the grouped map if it | ||
| // passes the filterSet check and has not been seen before. | ||
| func addBuildFile(filePath string, filterSet map[FileType]struct{}, seen map[BuildFile]struct{}, grouped map[FileType][]BuildFile) { | ||
| ft := fileTypeFromBasename(filepath.Base(filePath)) |
There was a problem hiding this comment.
Normalize SBOM paths before deriving file types
When GetBuildFileTrees parses an SBOM generated on Windows but is called on a Unix host, occurrence paths such as backend\Cargo.toml are passed to filepath.Base, which treats backslashes as ordinary characters on Unix. The resulting FileType becomes backend\Cargo.toml instead of Cargo.toml, so filtering with FileTypeCargoToml drops the manifest and unfiltered results are grouped under the wrong type; normalize \ separators or use a platform-independent basename before classification.
Useful? React with 👍 / 👎.
1644730 to
e5532c0
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5532c0d99
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| } | ||
|
|
||
| bf := BuildFile{FileType: ft, FilePath: filePath} |
There was a problem hiding this comment.
Normalize build file paths to the repo root
When callers pass an SBOM produced by GenerateSBOM or the CLI, this stores the raw file_name/BOMRef in BuildFile.FilePath, but those values are absolute paths: scanDir converts walked files with filepath.Abs, OpenLocalDepFile also absolutizes them, and extractors write f.Path() into package locations/artifacts. That makes the new API return machine-specific absolute paths despite the BuildFile.FilePath contract saying it is repo-relative, so consumers cannot reliably compare results across workspaces or use the documented path semantics.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 606306f36d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if strings.HasPrefix(basename, "requirements") && strings.HasSuffix(basename, ".txt") { | ||
| return FileTypeRequirementsTxt |
There was a problem hiding this comment.
Handle requirements.in manifests in filters
When a requirements.in source file exists, RequirementsInMatcher marks matched packages with LocationRoleManifest and uses the .in path as the occurrence filename, but this classifier only normalizes requirements*.txt. As a result GetBuildFileTrees(sbom, FileTypeRequirementsTxt) silently drops supported requirements manifests such as requirements.in or requirements-dev.in even though they are present in the SBOM; normalize these .in basenames to a requirements file type or expose a filterable FileType for them.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38648fd8e4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if id := parseMavenArtifactID(prop.Value); id != "" { | ||
| ctx.MavenArtifactIDs[comp.BOMRef] = id |
There was a problem hiding this comment.
Populate Maven IDs when groupId is inherited
When a Maven module omits <groupId> and inherits it from its parent, the file component purl generated by the existing artifact extractor has no namespace (the repo fixture other-dir/pom.xml is one such case, producing a value like pkg:maven/kafka-commons-client@...). Since the new API derives BuildFileRelations.ID only through parseMavenArtifactID, that common module shape gets an empty ID even though the README/API contract says Maven IDs are groupId:artifactId; resolve inherited group IDs or derive them through the parent metadata before storing this map entry.
Useful? React with 👍 / 👎.
Introduce typed parent/child relationships for build files:
- BuildFileRelations{Parent *BuildFile, Children []BuildFile} replaces the
previously untyped []BuildFile map value in GetBuildFileTrees. This makes
the parent-child direction explicit without embedding slices in the map key.
- ProcessorContext{FileDependencies map[string][]string} carries the SBOM
dependency graph to processors. GetBuildFileTrees now parses bom.Dependencies
and passes it to each processor, enabling enrichment without filesystem access.
- BuildFileProcessor.Process now receives a ProcessorContext alongside the files.
- MavenProcessor implements BuildFileProcessor for FileTypePomXML. It reads
ctx.FileDependencies to resolve parent/child relationships between pom.xml
files and populates BuildFileRelations accordingly. Registers via init().
- Tests cover: single root POM, simple parent-child, flat multi-module project,
deep three-level hierarchy, and external/unknown parent (silently ignored).
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
38648fd to
d1880d8
Compare
Modules that use BOM import (<dependencyManagement>) without a <parent> cannot resolve dependency versions at extraction time, producing packages with an empty version string. findArtifact previously required an exact name+version match, so it returned nil for these packages, and createFileComponents skipped adding dependency edges for those pom.xml files. Fix by relaxing findArtifact to accept a name-only match when version is the empty string, so that internal module edges are still recorded even when version resolution fails. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
d1880d8 to
d7e7791
Compare
piloulacdog
left a comment
There was a problem hiding this comment.
approved with feedback to not block you.
| // ExtractMavenPomArtifactIds controls whether Maven pom.xml artifact IDs | ||
| // and parent dependency relationships are extracted and included in the SBOM. | ||
| // When false, ScannedArtifact entries are never produced. | ||
| ExtractMavenPomArtifactIds bool |
There was a problem hiding this comment.
the fact that we make it a default-ignore, means that we are changing the default behavior of the tool (which used to be default-add). I guess it's not a big deal if we didn't use it! did we? 👀
There was a problem hiding this comment.
It was intentional and something @marcwieserdev and I discussed
| @@ -62,7 +62,7 @@ func Test_scanDir(t *testing.T) { | |||
| var excludedGlobs []string | |||
There was a problem hiding this comment.
now in the datadog-sbom-generator it seems we only have tests with false. which means we no longer have a single example of main_test.snap with the flag true. Could we add at least one?
There was a problem hiding this comment.
the main_test.snap is a very easy way to test and understand actual generated sbom (they are more human readable than the Go tests)
| // the parent POM path for a Maven file-type component. It mirrors the constant | ||
| // in internal/output/sbom/models.go; kept here to avoid an internal→pkg import | ||
| // cycle. | ||
| const mavenParentPomProperty = "maven:parentPom" |
There was a problem hiding this comment.
could we make it consistent with the other component properties we have? they are prefixed with datadog.
| const mavenParentPomProperty = "maven:parentPom" | |
| const mavenParentPomProperty = "datadog:maven-parent-pom" |
Except of course if it's a global known property
| // osvScannerPackageProperty is the CycloneDX component property name that | ||
| // records the package URL for a file-type component. For Maven components the | ||
| // purl format is "pkg:maven/{groupId}/{artifactId}@{version}". | ||
| const osvScannerPackageProperty = "osv-scanner:package" |
There was a problem hiding this comment.
if we now afford to change the default to the EXCLUDE the artifact, i assume it means we don't really have consumer of this property: osv-scanner:package ?
Should we take this opportunity to also standardize the name to: datadog:package? or whatever else which would make sense?
| func buildProcessorContext(bom *cyclonedx.BOM) ProcessorContext { | ||
| ctx := ProcessorContext{ | ||
| FileDependencies: make(map[string][]string), | ||
| MavenParents: make(map[string]string), |
There was a problem hiding this comment.
looks like this map is set/populated but never read. Is that a building block which is a leftover? or a component which will be used later?
| // purl format is "pkg:maven/{groupId}/{artifactId}@{version}". | ||
| const osvScannerPackageProperty = "osv-scanner:package" | ||
|
|
||
| // buildProcessorContext extracts enrichment data from the SBOM into a |
There was a problem hiding this comment.
nitpick: i think this comment is due next to: buildProcessorContext definition? :p
…op unused MavenParents Rename the property value from "maven:parentPom" to "datadog:maven-parent-pom" to be consistent with other Datadog-prefixed CycloneDX properties. Remove the MavenParents field from ProcessorContext — it was populated from the property but never consumed by any processor (the MavenProcessor uses FileDependencies for BFS traversal instead). Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b525b0ea5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| DirectoryPaths: dirs, | ||
| ExcludePaths: opts.ExcludePaths, | ||
| Recursive: opts.Recursive, | ||
| ExtractMavenPomArtifactIds: opts.ExtractMavenPomArtifactIds, |
There was a problem hiding this comment.
Return SBOMs when only Maven build files are found
With this option enabled, scanDir can now return Maven artifacts even when no packages were extracted, such as a repository that only contains parent/aggregator pom.xml files with no dependency declarations. GenerateSBOM still bubbles DoScan's NoPackagesFoundErr because DoScan checks only len(scannedPackages), so callers cannot produce the SBOM needed by GetBuildFileTrees for these build-file-only projects; treat non-empty artifacts as a successful scan or include them before the empty-package check.
Useful? React with 👍 / 👎.
| } | ||
| } | ||
|
|
||
| bf := BuildFile{FileType: ft, FilePath: filePath} |
There was a problem hiding this comment.
Preserve scan root when deduplicating build files
When GenerateSBOM is called with multiple scan roots, DoScan serializes locations and artifacts relative to each root, so two different roots can both contribute a pom.xml/Cargo.toml path. Because RepoPath is left empty here and seen keys only on FileType and FilePath, GetBuildFileTrees collapses those distinct build files into one entry, losing a root and merging its relations; populate/use the root identity or otherwise make the SBOM refs unique across scan roots.
Useful? React with 👍 / 👎.
…ckage Standardize the Maven file-type component property to use the datadog: prefix, consistent with datadog:maven-parent-pom. The property is only emitted for Maven POMs (the only extractor implementing GetArtifact), so there are no other ecosystem consumers to migrate. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Motivation
Downstream consumers of the
pkg/sbomgenlibrary need to identify which build/manifest files are present in a generated SBOM and understand their inter-module relationships — without having to parse CycloneDX JSON themselves.This is currently being developed as a POC for Source Code Integration (SCI), which needs to map each build file to its transitive internal dependencies in order to attribute vulnerabilities to the correct folder scope.
Documentation
Changes overview
This PR contains two independent layers of changes with different audiences:
Layer 1 — SBOM generation (
internal/,pkg/scanner/,pkg/extractor/) — affects all clientsThese changes improve the SBOM that
datadog-sbom-generatorproduces. They are independent of the library API and benefit every consumer of the generated SBOM, not just SCI.internal/output/sbom/cyclonedx.goaddFileDependencies: when a Maven artifact has a<parent>, emits amaven:parentPomproperty on the file-type component so that parent/module relationships can be unambiguously distinguished inbom.Dependencies.findArtifact: fixed a bug where modules using BOM import (<dependencyManagement>) without a<parent>element had their dependency versions extracted as empty strings, causingfindArtifactto return nil and nobom.Dependenciesedges to be emitted for those modules. Fixed by accepting a name-only match when version is empty.internal/output/sbom/models.gomavenParentPomProperty = "maven:parentPom"constant.pkg/extractor/extract.goGetArtifactto theExtractorinterface andExtractMavenPomArtifactIdsoption toScanContext, enabling opt-in emission of file-type components and dependency edges for Maven POMs.pkg/scanner/datadog_sbom_generator.goExtractMavenPomArtifactIdsfromOptionsthrough to the scan context so callers can control the feature.Layer 2 — Library API (
pkg/sbomgen/) — specific to the SCI POCThese changes add a new public Go API on top of the SBOM, intended for programmatic consumers (currently only the SCI POC in
code-workload-runner-sca). They do not affect the CLI output.pkg/sbomgen/build_files.go(new)FileType— string enum with 12 constants covering all manifest types.BuildFile—{FileType, FilePath, RepoPath}— identifies a build/manifest file within a repo.GetBuildFileTrees(sbom []byte, filters ...FileType) map[BuildFile]BuildFileRelations— parses a CycloneDX SBOM and returns all manifest build files, each enriched with its transitive dependencies and an ecosystem-specific ID. Collects build files from bothrole=manifestevidence occurrences and file-type components (so parent POMs with no package occurrences are never missed). Dispatches each file type to a registeredBuildFileProcessor.pkg/sbomgen/build_file_processor.go(new)BuildFileRelations—{ID string, Dependencies []BuildFile}— flat list of ALL transitively reachable build files (sorted by path) plus an ecosystem-specific identifier.BuildFileProcessorinterface andProcessorContext(carriesFileDependencies,MavenParents,MavenArtifactIDs).noopProcessor— returns emptyDependenciesfor unregistered file types.RegisterBuildFileProcessor— allows processors to self-register viainit().pkg/sbomgen/maven_processor.go(new)MavenProcessor— computes the full transitive closure of build-file dependencies via BFS overProcessorContext.FileDependencies.FileDependenciescarries both<parent>edges and local-module<dependency>edges, so BFS captures all reachable in-repo build files in one pass. PopulatesBuildFileRelations.IDwithgroupId:artifactIdparsed from theosv-scanner:packagepurl. Handles cycles and external parents (filtered out if not present in the SBOM).pkg/sbomgen/sbomgen.goDefaultOptions()enablesExtractMavenPomArtifactIdsby default soGetBuildFileTreesworks out of the box.README.md/examples/main.goGenerateSBOM+GetBuildFileTrees.Testing
Layer 1 — SBOM generation:
internal/output/updated to reflect the newmaven:parentPomproperty and correctedbom.Dependenciesedges for BOM-import modules.Layer 2 — Library API:
pkg/sbomgen/build_files_test.go— 10 tests: manifest extraction, lockfile exclusion, FileType filtering, deduplication, file-type component collection, unknown types.pkg/sbomgen/maven_processor_test.go— 7 tests: single root POM, parent-child, multi-module, deep 3-level hierarchy (transitive closure), external parent ignored, sibling module as dependency, parent POM via file-type component only.Staging validation
Recovery