Skip to content

feat: [SCI] add BazelBuildExtractor for inter-module dependency analysis in Bazel monorepos#177

Merged
anderruiz merged 6 commits into
mainfrom
ander/bazel-module-deps-analysis
Jun 22, 2026
Merged

feat: [SCI] add BazelBuildExtractor for inter-module dependency analysis in Bazel monorepos#177
anderruiz merged 6 commits into
mainfrom
ander/bazel-module-deps-analysis

Conversation

@anderruiz

Copy link
Copy Markdown
Contributor

Summary

Adds a new BazelBuildExtractor that parses BUILD.bazel files in Java/Bazel monorepos to extract internal module-to-module dependencies (//path/to/module refs). This enables the SBOM generator to produce ProjectDeps edges between modules within monorepos like logs-backend, which is critical for service ownership tagging and transitive dependency attribution.

What it does

  • Parses BUILD.bazel files line-by-line, extracting //path/to/module references from deps, runtime_deps, and exports attributes
  • Filters out noise: skips load() macro imports, external Maven artifact() references, and inline comments
  • Integrates with BuildFileRelations: emits ProjectDeps edges consumed by the existing SimpleProcessor BFS to compute transitive closure with hop counts
  • Registers Bazel as a package manager mapped to the Java language, with BUILD.bazel as its lockfile path

Approach

The extractor uses simple line-based parsing (no full Starlark AST) which is sufficient for dependency extraction. It:

  1. Scans each line for "//path/to/module" patterns
  2. Strips target labels (:target suffixes) to get module paths
  3. Resolves each to path/to/module/BUILD.bazel as a ProjectDeps entry
  4. Deduplicates results

TEST_DEPS inclusion

TEST_DEPS lists are intentionally included. These are compile-time test dependencies that affect the build graph and should be tracked for completeness. This is a conservative choice -- downstream consumers can filter if needed.

Validation

Tested against logs-backend sca-processor module:

  • 99.6% match rate vs bazel query ground truth (267/268 dependencies)
  • The single "missing" dependency (domains/cloud-security-platform/libs/sca/sca-processor-config) is actually a false positive in bazel query (pulled in via macro expansion, not a direct file-level reference)

Test plan

  • Unit tests for line parsing, filtering, and edge cases (empty files, comments, mixed deps)
  • SimpleProcessor integration tests for BFS transitive closure with BUILD.bazel
  • Extractor registry tests updated to include BUILD.bazel
  • Integration test snapshots updated

🤖 Generated with Claude Code

@anderruiz
anderruiz requested a review from a team as a code owner June 19, 2026 13:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf4ddf8659

ℹ️ 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".

Comment thread pkg/extractor/java/parse-bazel-build.go Outdated
Comment thread pkg/sbomgen/build_files.go
@anderruiz anderruiz changed the title feat: add BazelBuildExtractor for inter-module dependency analysis in Bazel monorepos feat: [SCI] add BazelBuildExtractor for inter-module dependency analysis in Bazel monorepos Jun 19, 2026
anderruiz and others added 3 commits June 19, 2026 16:34
…BUILD.bazel

Introduces Bazel as a package manager mapped to the Java language, adds
BazelBuildFilePath constant, FileTypeBUILDBazel build file type, and registers
SimpleProcessor for BUILD.bazel to enable BFS transitive closure computation.
Includes tests for the SimpleProcessor registration and transitive closure.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…om BUILD.bazel files

Parses BUILD.bazel files in Java/Bazel monorepos to extract internal module-to-module
dependencies (//path/to/module refs). Filters out external Maven artifact() references
and load() macro imports. Integrates with the existing BuildFileRelations framework
via ProjectDeps and SimpleProcessor BFS for transitive closure computation.

Validated against logs-backend sca-processor: 99.6% match rate vs bazel query ground truth.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…upport

Adds BUILD.bazel to the expected extractor list, updates the Java/Gradle language
expansion test to include BUILD.bazel, and updates integration test snapshots.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@anderruiz
anderruiz force-pushed the ander/bazel-module-deps-analysis branch from bf4ddf8 to e8a0e95 Compare June 19, 2026 14:34
@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Jun 19, 2026

Copy link
Copy Markdown

🎯 Code Coverage (details)
Patch Coverage: 82.46%
Overall Coverage: 84.41% (-0.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: e0aa860 | Docs | Datadog PR Page | Give us feedback!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8a0e95bd7

ℹ️ 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".

Comment thread pkg/extractor/java/parse-bazel-build.go Outdated
Comment on lines +84 to +85
if strings.HasPrefix(strings.TrimSpace(line), "load(") {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip labels inside multiline load blocks

When a load() call is formatted over multiple lines, only the line containing load( is removed, leaving the following string label (for example "//rules/java:defs.bzl") to be treated as a ProjectDeps edge if that directory has a BUILD file. This reintroduces the build-infrastructure dependencies the comment is trying to exclude, so Bazel graphs with multiline loads can gain false module dependencies in the SBOM and GetBuildFileTrees.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Replaced the line-by-line filter with a multiline regex (?s)load\([^)]*\) that removes entire load blocks, including those spanning multiple lines.

Comment thread pkg/extractor/java/parse-bazel-build.go Outdated
Comment on lines +94 to +95
internalRefRegex := cachedregexp.MustCompile(`"(//[^"]+)"`)
matches := internalRefRegex.FindAllSubmatch(filtered, -1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict Bazel label extraction to dependency attributes

Because this regex scans every string literal in the BUILD file, labels in non-dependency attributes such as visibility = ["//teams/foo:__pkg__"] or platform constraints are emitted as ProjectDeps whenever the referenced package has a BUILD file. Those edges are then written to the SBOM dependency graph and traversed by GetBuildFileTrees, so metadata/access-control labels can appear as Java module dependencies; please limit extraction to dependency-like attributes or otherwise filter non-dependency labels.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't fix. Labels in non-dependency attributes like visibility typically reference special Bazel targets (//visibility:public, //teams/foo:__pkg__) that don't have BUILD files on disk. The filesystem existence check already filters them out. Adding attribute-specific parsing would require a real Starlark parser.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b6dba78d6

ℹ️ 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".

Comment thread pkg/extractor/java/parse-bazel-build.go
Comment thread pkg/extractor/java/parse-bazel-build.go Outdated
@anderruiz
anderruiz force-pushed the ander/bazel-module-deps-analysis branch 2 times, most recently from 76129b0 to 6c28a6b Compare June 22, 2026 05:16

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c28a6b5b4

ℹ️ 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".

Comment thread pkg/extractor/java/parse-bazel-build.go Outdated
// reference Starlark macro/rule definition files (build infrastructure),
// not Java modules that end up in the service binary. We remove the entire
// block (including multiline loads) to avoid capturing their string labels.
loadBlockRegex := cachedregexp.MustCompile(`(?s)load\([^)]*\)`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Anchor load stripping to real load calls

With the new multiline load stripping, any macro/function name that contains load( is treated as a load statement. For example, dd_workload(name = "svc", deps = ["//libs/common"]) leaves only the dd_work prefix before label extraction, so the valid dependency is never emitted as a ProjectDep; anchor this regexp to an actual Starlark load call instead of matching substrings inside macro names.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Anchored the load-stripping regex to start-of-line with (?ms)^\s*load\([^)]*\) so it only removes actual Starlark load statements and not function names that contain "load" as a substring.

}

if ctx.RootDir != "" {
artifact.ProjectDeps = extractBazelInternalDeps(content, ctx.RootDir)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve labels from the Bazel workspace root

When GenerateSBOM or the CLI is pointed at a service directory inside a Bazel workspace, ctx.RootDir is that scan directory, but //... Bazel labels are workspace-root relative. Passing the scan dir here makes a label like //libs/common resolve under <service>/libs/common/BUILD*, so os.Stat filters it out and the SBOM loses inter-module edges unless the user scans from the workspace root; this should use the Bazel workspace/repo root instead.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't fix in this PR. The extractor follows the same contract as all other extractors (Gradle, Maven): ctx.RootDir is expected to be the repository root, which is how the CLI and GenerateSBOM are documented to be invoked on a Bazel monorepo. Workspace root traversal would add complexity for a use case outside the documented API contract.

- accept single-quoted Bazel labels in addition to double-quoted
- register SimpleProcessor for FileType("BUILD") alongside BUILD.bazel
- replace line-by-line load() filter with multiline regex to handle multiline load blocks
@anderruiz
anderruiz force-pushed the ander/bazel-module-deps-analysis branch from 6c28a6b to 7f74377 Compare June 22, 2026 05:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f74377c39

ℹ️ 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 ctx.RootDir != "" {
artifact.ProjectDeps = extractBazelInternalDeps(content, ctx.RootDir)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter same-package Bazel labels out of ProjectDeps

When a BUILD file uses an absolute label for another target in the same package, such as libs/common/BUILD.bazel containing deps = ["//libs/common:helper"], the helper resolves that label back to the current BUILD file because it drops the target suffix. After path relativization this writes a CycloneDX dependency from libs/common/BUILD.bazel to itself, so consumers of the SBOM dependency graph can see a bogus self-edge; filter out deps whose filename matches the source BUILD file before assigning ProjectDeps.

Useful? React with 👍 / 👎.


// Try BUILD.bazel first, then BUILD
for _, buildFileName := range []string{bazelBuildFilename, bazelBuildFilenamAlt} {
depPath := filepath.Join(rootDir, path, buildFileName)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject Bazel dep paths that escape the scan root

If a malformed BUILD string like "//../sibling" is present, this filepath.Join normalizes the .. and can os.Stat a BUILD file outside the scanned repository; when that file exists, the extractor emits an out-of-scope ProjectDep that is later included in the SBOM dependency graph. Since this parser scans raw strings rather than Bazel-valid labels, guard the cleaned path with filepath.Rel/prefix checks and skip anything that escapes rootDir.

Useful? React with 👍 / 👎.

Replace the fixed 10-hop BFS limit with a size-adaptive limit:
- <100 build files → 9999 hops (effectively unlimited, small repos)
- 100–399 files    → 10 hops
- 400+ files       → 5 hops (large monorepos like dd-source ~41k BUILD files)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e0aa86066b

ℹ️ 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 len(queue) > 0 {
node := queue[0]
queue = queue[1:]
if node.depth >= maxHops {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the full build-file closure

When GetBuildFileTrees processes 100+ files, maxHopsForSize returns 10 (or 5 at 400+), so this guard stops BFS before deeper transitive edges are added. The public BuildFileRelations contract says Dependencies contains all transitively reachable build files, and all file types registered with SimpleProcessor use this path; a chain of 101 POMs/Gradle/requirements/BUILD files will silently omit hop 11+ (or hop 6+ in very large repos), breaking transitive attribution in the large monorepos this feature targets.

Useful? React with 👍 / 👎.

@anderruiz
anderruiz merged commit 64e93bf into main Jun 22, 2026
16 of 17 checks passed
@anderruiz
anderruiz deleted the ander/bazel-module-deps-analysis branch June 22, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants