feat(java): resolve Gradle artifact names from settings.gradle#174
Conversation
- Add parseGradleSettingsProjectName function that reads settings.gradle/kts
from the root directory and resolves the canonical project name
- Parse rootProject.name for root projects, include declarations for
subprojects, and project(':x').name for name overrides
- Support both Groovy DSL (single quotes) and Kotlin DSL (double quotes)
- Handle nested subproject paths (e.g. :sub:module -> sub/module)
- Fall back to empty string when no settings file or project not declared
- Add 11 unit tests covering all parsing scenarios and edge cases
Rationale: Gradle projects define their canonical names in settings.gradle,
not by directory basename. The existing GetArtifact logic used directory names,
producing incorrect artifact IDs when rootProject.name or project overrides
differ from the directory structure. This parser enables correct name resolution.
This commit made by [/dd:git:commit:atomic](https://github.com/DataDog/claude-marketplace/tree/main/dd/commands/git/commit/atomic.md)
- Update GradleLockExtractor.GetArtifact to resolve project name from settings.gradle via parseGradleSettingsProjectName, with directory basename fallback - Update GradleVerificationMetadataExtractor.GetArtifact with the same settings.gradle resolution logic - Add integration tests for both extractors verifying settings.gradle name resolution (root project and subproject with name override) - Existing tests continue to pass, confirming fallback behavior when no settings.gradle is present Rationale: With the settings.gradle parser in place, the Gradle extractors can now derive correct artifact IDs from the authoritative source instead of relying on directory basenames, which are often different from the project's declared name in multi-module Gradle builds. This commit made by [/dd:git:commit:atomic](https://github.com/DataDog/claude-marketplace/tree/main/dd/commands/git/commit/atomic.md)
|
🎯 Code Coverage (details) 🔗 Commit SHA: d7d2b46 | 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: d00b9e6a00
ℹ️ 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".
Subprojects commonly inherit group from the root via allprojects { group = '...' }
without redeclaring it in their own build.gradle. GetArtifact now falls back to
extractGroupFromRootBuildFile(ctx.RootDir) when the subproject's own build file
has no group declaration, so artifact.Name is populated for all subprojects.
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: 4fc4a2b3a1
ℹ️ 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".
…deRefRe - Restrict includeRefRe to only match Gradle project paths (leading ':'), avoiding false positives from inline comments or other quoted strings - Add MultilineInclude_SingleItem and MultilineInclude_MultiItem_NotSupported tests to document actual regex behaviour around multiline include statements - Add InheritsGroupFromRootBuildFile integration test for GradleVerificationMetadataExtractor, mirroring the equivalent lock extractor test 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: 0dacafb47b
ℹ️ 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".
- Normalize rootDir with filepath.Abs in parseGradleSettingsProjectName so filepath.Rel works when the scanner is invoked with a relative path (e.g. 'datadog-sbom-generator scan .'), where ctx.RootDir is '.' but DepFile.Path() is absolute. - Restrict extractGroupFromRootBuildFile to allprojects/subprojects blocks only. A top-level 'group = x' in the root build file applies only to the root project in Gradle and must not be used as a fallback for subprojects. Introduces inheritedGroupRe with (?s) flag to span the block body. - Update tests to document the new behaviour: top-level group is not inherited; allprojects/subprojects block group is. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The file is a utility module (no extractor registered), not a parser implementation. Keeping the parse- prefix caused TestExtractDeps_FindsExpectedExtractor to count it as an extractor via the */parse-*.go glob and fail with an off-by-one mismatch.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 389c4a5efd
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 315c8aa01f
ℹ️ 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".
Replace `(?s).*?` with `[^{]*` so the pattern stops at the first nested
block opening. This prevents a task's `group = "verification"` inside an
allprojects block from being mistakenly captured as the project group when
it appears before the real `group = 'com.example'` assignment.
315c8aa to
947c91d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 947c91d6ff
ℹ️ 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".
…fallback
1. Replace inheritedGroupRe (unbounded (?s).*? regex) with extractBlockGroup,
a brace-counting function that stays strictly within the allprojects/subprojects
block body. This prevents a top-level group = that appears after the block's
closing } from being incorrectly captured as an inherited group.
2. Guard the extractGroupFromRootBuildFile fallback in both GetArtifact
implementations: only call it when projectDir != ctx.RootDir. The root
project is excluded from subprojects {} blocks in Gradle, so a root build
file that contains only subprojects { group = 'x' } must not assign that
group to the root artifact itself.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2dd76e332c
ℹ️ 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".
| projectDir := filepath.Dir(filepath.Dir(f.Path())) | ||
| group := extractTopLevelGroup(content) | ||
| if group == "" { | ||
| if ctx.RootDir != "" && projectDir != ctx.RootDir { |
There was a problem hiding this comment.
Allow allprojects groups on root artifacts
When verification-metadata.xml belongs to the root project and the root build file declares allprojects { group = 'com.datadoghq' }, projectDir equals ctx.RootDir, so this guard skips the only fallback that can read that group and leaves artifact.Name empty. Gradle allprojects applies to the root project too; only subprojects should be excluded, and the added TestGradleVerificationMetadataExtractor_GetArtifact_InheritsGroupFromRootBuildFile covers this exact scenario and would fail under normal test execution.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed. Added extractAllProjectsGroupFromRootBuildFile that searches only allprojects { } blocks (not subprojects { }), and updated the root-project path in both parse-gradle-verification-metadata.go and parse-gradle-lock.go to call it when projectDir == absRootDir.
| // Only fall back to root build file for subprojects: the root project is | ||
| // excluded from subprojects { } blocks, so a subprojects-only group in the | ||
| // root build file must not be assigned to the root artifact itself. | ||
| if ctx.RootDir != "" && projectDir != ctx.RootDir { |
There was a problem hiding this comment.
Normalize the root before the subproject guard
Fresh evidence: scanDir still passes the raw scan argument as ScanContext.RootDir, while DepFile.Path() is absolute, so a common scan . invocation makes the root lockfile's projectDir != ctx.RootDir comparison true. In a root build that only has subprojects { group = ... }, this then assigns the subproject-only group to the root artifact, which can make findArtifact classify unrelated external coordinates as internal; normalize ctx.RootDir before this comparison as is already done for filepath.Rel.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed. Added filepath.Abs normalization of ctx.RootDir before the root-vs-subproject comparison in parse-gradle-lock.go, matching the pattern already used in parseGradleSettingsProjectName.
| } | ||
|
|
||
| blockBody := src[absOpen+1 : blockEnd] | ||
| if m := groupAssignRe.FindSubmatch(blockBody); m != nil { |
There was a problem hiding this comment.
Ignore nested Gradle task groups
Fresh evidence: although extractBlockGroup now bounds the search to the outer allprojects/subprojects block, this FindSubmatch(blockBody) still scans nested blocks. For allprojects { tasks.register("check") { group = "verification" } group = "com.acme" }, it returns the task group before the project group, so subprojects get coordinates like verification:api and findArtifact can miss or misclassify internal Gradle dependencies.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed. Added findGroupAtTopLevel which blanks out nested block content (via depth tracking) before applying groupAssignRe, so a task group = "verification" inside a nested block is never captured instead of the real project group.
…fallback
- Add allProjectsBlockHeaderRe and extractAllProjectsGroupFromRootBuildFile to
restrict the inherited-group fallback for root artifacts to allprojects { }
only (Gradle allprojects applies to root; subprojects does not)
- Normalize ctx.RootDir with filepath.Abs before root-vs-subproject comparison
in both GetArtifact implementations so "scan ." (relative path) works correctly
- Add findGroupAtTopLevel to skip group = assignments inside nested blocks
(e.g. task registrations) when searching for inherited group in extractBlockGroup
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: d62f538dfc
ℹ️ 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 g := findGroupAtTopLevel(blockBody); g != "" { | ||
| return g |
There was a problem hiding this comment.
Use later Gradle group overrides
When a subproject has no local group and the root build file configures both blocks, e.g. allprojects { group = 'com.root' } followed by subprojects { group = 'com.sub' }, the later assignment is the effective group for subprojects. This returns as soon as it finds the first block group, so GetArtifact emits com.root:api instead of com.sub:api, which can make findArtifact miss or misclassify internal Gradle project dependencies; keep scanning and use the last applicable assignment in file order.
Useful? React with 👍 / 👎.
…erride order
When a root build file declares both allprojects { group = 'com.root' } and
subprojects { group = 'com.sub' }, Gradle applies both in order so the later
assignment wins for subprojects. extractBlockGroup was returning on the first
match, producing com.root:api instead of com.sub:api. Now scans all matching
blocks and returns the last group found.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f1f937b28
ℹ️ 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".
| func extractBlockGroup(src []byte, findHeader func([]byte) []int) string { | ||
| last := "" | ||
| for searchFrom := 0; searchFrom < len(src); { | ||
| loc := findHeader(src[searchFrom:]) |
There was a problem hiding this comment.
Skip commented Gradle blocks when inheriting groups
When a root build file has an old allprojects/subprojects block commented out after the real one, this scans the raw bytes for block headers and treats the commented block as a later override. For example, a trailing // subprojects { ... group = 'old' ... } makes subprojects without a local group get old:<name>, so findArtifact can miss or misclassify internal Gradle dependencies; strip/ignore comments before looking for inherited group blocks.
Useful? React with 👍 / 👎.
…blocks
Commented-out allprojects/subprojects blocks (e.g. // subprojects { group = 'old' })
were treated as live overrides by extractBlockGroup, causing later commented blocks
to override real ones. Add stripGradleComments to blank out // and /* */ comment
content (preserving newlines and byte positions) before block scanning.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2fc0a78316
ℹ️ 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".
| group = extractGroupFromRootBuildFile(ctx.RootDir) | ||
| } | ||
| } | ||
| if group != "" { | ||
| projectName := parseGradleSettingsProjectName(ctx.RootDir, projectDir) |
There was a problem hiding this comment.
Resolve Gradle metadata from the project root
When the CLI scans a repository root that contains a Gradle build in a subdirectory, ctx.RootDir is the scan root, not the nearest Gradle root. For a layout like repo/service/settings.gradle plus a lockfile under repo/service/api, this reads repo/build.gradle for the inherited group and then looks for repo/settings.gradle, so the service's inherited group and settings name overrides are missed; the artifact name is left empty or falls back to the directory basename, which prevents findArtifact from linking internal Gradle dependencies. Resolve the Gradle root from projectDir/buildFilePath before calling these helpers.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid concern, but out of scope for this PR. Resolving the Gradle root from projectDir requires walking up the directory tree to find the nearest settings.gradle, which is a separate discovery feature. This PR is scoped to improving artifact naming when ctx.RootDir is already the Gradle root (the typical CLI usage).
| if i+1 < len(out) && out[i] == '/' && out[i+1] == '/' { | ||
| // Single-line comment: blank everything up to (but not including) the newline. | ||
| for i < len(out) && out[i] != '\n' { |
There was a problem hiding this comment.
Preserve URL strings while stripping comments
When an inherited group appears after an inline repository declaration such as repositories { maven { url = uri("https://repo") } }, this treats the // inside the URL string as a comment and blanks the rest of that line, including the closing braces. The brace counter in extractBlockGroup then never reaches the real end of the allprojects/subprojects block, so extractGroupFromRootBuildFile returns empty and subprojects that rely on that group lose their artifact.Name, breaking internal Gradle dependency matching.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I think this is a legit feedback
There was a problem hiding this comment.
Fixed in 01a533c — stripGradleComments now skips over string literals (single and double quoted) before looking for // or /*, so url = uri("https://repo") is left untouched. Added TestStripGradleComments_PreservesURLsInStrings.
|
|
||
| // Matches Gradle project path references like ':subA' or ":sub:module" within an | ||
| // include argument list. Requiring the leading ':' avoids false positives from | ||
| // inline comments or other quoted strings in the same line. |
There was a problem hiding this comment.
could we have some reg101 links here to easy test/confirm matches?
There was a problem hiding this comment.
Added regex101 links to all regex declarations in d7d2b46.
| // Matches Gradle project path references like ':subA' or ":sub:module" within an | ||
| // include argument list. Requiring the leading ':' avoids false positives from | ||
| // inline comments or other quoted strings in the same line. | ||
| includeRefRe = cachedregexp.MustCompile(`['"](:(?:[^'"]+))['"]`) |
There was a problem hiding this comment.
apparently the refs can be prefixed with AND without a column, and seem to be valid in both case.
// settings.gradle
// Form 1: with leading colon (handled by PR)
include ':app'
// Form 2: WITHOUT leading colon (silently ignored by PR regex)
include 'communication'
// Form 2 + name override (override still works via separate regex)
include 'lib-core'
project(':lib-core').name = 'comms-core'see how we have both type of include ':app' and communication, and when I ran the tree, it was correctly parsed:
$ gradle projects
Projects:
------------------------------------------------------------
Root project 'my-renamed-root'
------------------------------------------------------------
Location: ....
Project hierarchy:
Root project 'my-renamed-root'
+--- Project ':app'
+--- Project ':comms-core'
\--- Project ':communication'so we should likely adjust it for:
| includeRefRe = cachedregexp.MustCompile(`['"](:(?:[^'"]+))['"]`) | |
| includeRefRe = cachedregexp.MustCompile(`['"]:?([^'"]+)['"]`) |
and then in parseGradleSettingsProjectName, drop the leading : from gradlePath and normalize the override regex's capture the same way
// Build the Gradle project path WITHOUT the leading ':' so it matches the
// normalized captures from includeRefRe / the trimmed projectNameRe key.
gradlePath := strings.ReplaceAll(filepath.ToSlash(relPath), "/", ":")There was a problem hiding this comment.
Fixed in cc1f37d. Changed includeRefRe to make the leading : optional (consumed outside the capture group). The captured bare path is then normalized with a : prefix before comparing with gradlePath. This handles both include ':app' and include 'app' forms. Added test TestParseGradleSettingsProjectName_IncludeWithoutLeadingColon covering both the colon-less include and the name override case.
|
|
||
| // include ':subA', ':subB' or include(':subA', ':subB') | ||
| // Captures the full argument string after include. | ||
| includeRe = cachedregexp.MustCompile(`(?m)^\s*include\s*\(?\s*([^)\n]+?)\s*\)?\s*$`) |
There was a problem hiding this comment.
it seems that includeRe matches includeBuild (no word boundary after include), so composite-build paths can be treated as subprojects.
include ':app' // i assume it should match
include(':app') // i assume it should match
includeBuild '../lib' // i assume it should NOT match
includeBuild('../lib') // i assume it should NOT match
includeFlat 'sibling' // i assume it should NOT match
There was a problem hiding this comment.
Fixed in cc1f37d — added \b word boundary after include in includeRe so includeBuild, includeFlat, etc. no longer match as subproject declarations. Added test TestParseGradleSettingsProjectName_IncludeBuildNotMatchedAsSubproject.
| rootDir = abs | ||
| } | ||
|
|
||
| content := readSettingsFile(rootDir) |
There was a problem hiding this comment.
we might have to add something here to strip comments:
content = stripGradleComments(content)because i'm not fully sure we support cases like those:
Don't we report old-name here?
// settings.gradle
// rootProject.name = 'old-name'
rootProject.name = 'new-name'
Or those, projectNameRe matches inside the block comment. Result: :app resolves to deprecated-name even though the override is commented out.
// settings.gradle
include ':app'
/*
project(':app').name = 'deprecated-name'
*/
or also, block-commented include:
// settings.gradle
/*
include ':removed-module'
*/
include ':app'
There was a problem hiding this comment.
Fixed in two commits: 01a533c makes stripGradleComments string-aware (skips string literals so // inside URLs is not treated as a comment delimiter), and 9472cd6 applies the strip at the start of parseGradleSettingsProjectName. Added tests for all three cases: line-commented name, block-commented include, and block-commented name override.
| if i+1 < len(out) && out[i] == '/' && out[i+1] == '/' { | ||
| // Single-line comment: blank everything up to (but not including) the newline. | ||
| for i < len(out) && out[i] != '\n' { |
There was a problem hiding this comment.
I think this is a legit feedback
| // - Root project: inherits from allprojects { } only (subprojects { } does not apply). | ||
| // - Subproject: inherits from allprojects { } or subprojects { }. | ||
| // Name: prefer settings.gradle canonical name; fall back to directory basename. | ||
| projectDir := filepath.Dir(f.Path()) |
There was a problem hiding this comment.
this logic seems to be duplicated in parse-gradle-verification-metadata.go should we extract it to a resolveGradleArtifactName(ctx, projectDir, ownGroup) helper
There was a problem hiding this comment.
Extracted to resolveGradleArtifactName(ctx, projectDir, buildFileContent) in 8e7aad9. Both GradleLockExtractor.GetArtifact and GradleVerificationMetadataExtractor.GetArtifact now delegate to it.
| } | ||
| if projectDir == absRootDir { | ||
| // Root project: only allprojects { } applies. | ||
| group = extractAllProjectsGroupFromRootBuildFile(ctx.RootDir) |
There was a problem hiding this comment.
should we pass the normalized path? absRootDir
There was a problem hiding this comment.
Fixed in f7163e5 — extractAllProjectsGroupFromRootBuildFile and extractGroupFromRootBuildFile now receive absRootDir instead of ctx.RootDir. This is also preserved in the extracted resolveGradleArtifactName helper.
…ubproject include The includeRe regex lacked a word boundary after 'include', causing includeBuild, includeFlat, etc. to be parsed as subproject declarations. Added \b word boundary to restrict matches to the bare 'include' keyword. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…gradle Gradle allows both include ':app' and include 'app' (without leading colon) as valid project declarations. The previous includeRefRe required the colon, silently ignoring the colon-less form. Now the colon is optional and the captured path is normalized before comparison. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The comment stripper was treating '//' inside quoted strings (e.g.
url = uri("https://repo")) as a comment delimiter, blanking the rest
of the line including closing braces. This broke brace-counting in
extractBlockGroup, causing inherited group values to be missed.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
parseGradleSettingsProjectName was applying regexes to raw file content, so commented-out rootProject.name, include, and project().name statements were incorrectly matched. Strip comments first so only live declarations affect project name resolution. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…lpers absRootDir was computed for the root-vs-subproject comparison but the actual calls to extractGroupFromRootBuildFile and extractAllProjectsGroupFromRootBuildFile still used the original ctx.RootDir. Pass the normalized absolute path to both calls for consistency. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… duplication Both GradleLockExtractor.GetArtifact and GradleVerificationMetadataExtractor.GetArtifact contained identical group/name resolution logic. Extract to resolveGradleArtifactName in gradle_settings.go where the helpers it calls already live. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add regex101 playground links next to each compiled regex so reviewers and future contributors can quickly verify and experiment with the patterns without setting up a local environment. 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: d7d2b4668e
ℹ️ 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 m := groupAssignRe.FindSubmatch(flat); m != nil { | ||
| return string(m[1]) |
There was a problem hiding this comment.
Use the last top-level group assignment
When an allprojects/subprojects block assigns group more than once at top level, Gradle leaves the project with the later value, but this FindSubmatch returns the first one from the flattened block. For example, allprojects { group = 'old'; group = 'new' } makes subprojects use new, while the scanner records old:<project>, so internal Gradle dependencies can be missed or matched to the wrong artifact.
Useful? React with 👍 / 👎.
piloulacdog
left a comment
There was a problem hiding this comment.
would be interesting to find real life usecase, and compare what we output when scanning those to the other tools: trivy/snyk/GHAS
Summary
settings.gradle/settings.gradle.ktsto resolve canonical project names in GradleGetArtifact()methodsrootProject.name,includedeclarations, andproject(':x').nameoverrides (both Groovy and Kotlin DSL)GradleLockExtractorandGradleVerificationMetadataExtractor, with fallback to directory basename when no settings file existsProblem
The Gradle extractors derive artifact IDs using directory basenames (e.g.,
filepath.Base(dir)), but Gradle projects define their canonical names insettings.gradle. This produces incorrect artifact IDs whenrootProject.nameorproject(':x').nameoverrides differ from the directory structure -- which is common in multi-module Gradle builds.Changes
New file:
pkg/extractor/java/parse-gradle-settings.goparseGradleSettingsProjectName(rootDir, projectDir)readssettings.gradle/.ktsand returns the canonical namerootProject.name,includedeclarations,project(':x').nameoverrides:sub:module->sub/module)""(caller uses directory basename) when settings file is absent or project not declaredModified:
parse-gradle-lock.goandparse-gradle-verification-metadata.goGetArtifact()methods now callparseGradleSettingsProjectNamebefore falling back tofilepath.Base()Test plan
parseGradleSettingsProjectNamecovering: root project (single/double quotes), subproject from include, name overrides, Kotlin DSL, multiple includes, nested subprojects, no settings file, project not in settings, empty rootDirGradleLockExtractor.GetArtifact(root project name, subproject override)GradleVerificationMetadataExtractor.GetArtifact(root project name)🤖 Generated with Claude Code