Repository navigation
Vendored Gradle: gradle_exclusive_content_conflict refusal doesn't fire for a subproject build script or a buildSrc convention plugin, so vendor exits 0, the build then fails with "Could not find", and VEX attests not_affected #461
Description
[agent] Found by the scheduled Gradle bug-hunt routine (ledger #319).
Summary
The v5 Gradle vendoring planner refuses with gradle_exclusive_content_conflict when a user exclusiveContent rule claims the patched group. That check only runs on the settings files and the root build.gradle(.kts) (crates/socket-patch-core/src/vendor/jvm/gradle.rs:117-120, :156-158). It never sees the build scripts where repositories usually live in a multi-project build:
- a subproject's
app/build.gradle - a
buildSrc(orbuild-logic) precompiled convention plugin such asbuildSrc/src/main/groovy/bh.repos.gradle
In these shapes vendor exits 0 and reports applied. Every build of the vendored checkout then fails at resolution with Could not find org.apache.commons:commons-text:1.10.0, because the user's exclusive repository and socketPatchVendor both claim the module. vex attests not_affected for that build anyway.
This also breaks the documented hosted → vendored path. The hosted Gradle snippet (redirect_gradle_manual_snippet, docs/ecosystems.md "Gradle (hosted Maven)") is an exclusiveContent block that users paste into the build script that declares the dependency, which in a multi-project build is the subproject's script. If that snippet pins the base version (the legacy, non-suffixed form) and the user then runs vendor, they hit exactly this failure.
Impact
vendorreports success but leaves a build that can't resolve the patched dependency. It fails loudly, so the unpatched jar never ships. Still, CI breaks right after a "successful" vendor run, with an error that doesn't mention socket-patch.vexemitsnot_affected/inline_mitigations_already_existfor a build that can't run.- The same rule placed in root
allprojects {}/subprojects {}is refused correctly (control below), so this is an inconsistent refusal, not a design choice.
Repro
I used the repo's own vendored-JVM fixture: prebuilt_common::prepare_command + a staged .socket/manifest.json/blob for pkg:maven/org.apache.commons/commons-text@1.10.0, the same as e2e_vendor_jvm_build. Steps:
settings.gradle: rootProject.name = 'ex'
include 'app'
app/build.gradle: plugins { id 'java' }
repositories {
exclusiveContent {
forRepository { mavenCentral() }
filter { includeGroup 'org.apache.commons' }
}
mavenCentral()
}
dependencies { implementation 'org.apache.commons:commons-text:1.10.0' }
tasks.register('printCp') { def rc = configurations.runtimeClasspath
doLast { rc.files.each { println 'SOCKET-CP ' + it } } }
gradle :app:printCp: OK, resolves Central's jar.- Stage the manifest + blob, then
socket-patch vendor --json: exit 0,"action": "applied",summary.failed = 0. - Copy the committable files (no manifest or blobs) to a fresh checkout, then
gradle :app:printCp:> Could not resolve all dependencies for configuration ':app:runtimeClasspath'. > Could not find org.apache.commons:commons-text:1.10.0. socket-patch vex --product pkg:maven/com.example/ex@1 --output vex.json: exit 0,"status": "not_affected"for commons-text.
Variants with the same result (vendor exit 0, then the build fails):
- conv: the same
repositories { exclusiveContent … }block inbuildSrc/src/main/groovy/bh.repos.gradle(withid 'groovy-gradle-plugin'), applied byappwithplugins { id 'bh.repos' }. - snippet: the hosted snippet shape,
exclusiveContent { forRepository { maven { url "<hosted repo>" } } filter { includeVersion("org.apache.commons", "commons-text", "1.10.0") } }, inapp/build.gradle.
Control (correct): the same block inside root build.gradle allprojects { … } or subprojects { … } gives vendor exit 1, vendor_jvm_shape_unsupported, reason: gradle_exclusive_content_conflict: build.gradle has an exclusiveContent rule for org.apache.commons; drop org.apache.commons:commons-text from it, and nothing is written.
Expected vs actual
- Expected: docs/design/maven-vendoring.md (Gradle) says "Android, Kotlin Multiplatform,
available-atmodule redirects, conflicting exclusive-content rules and paths leaving the checkout are refused." Thecheck_exclusive_contentdoc comment says such a rule "would make ours unreachable ('Could not find')". Per CLI_CONTRACT a failed patch is reported as failed, sovendorshould refuse (or at least warn) for every build script it can statically find. That includesincluded subproject scripts andbuildSrc/ literalincludeBuildconvention-plugin sources, the same way it already scans settings, buildSrc settings and included builds. - Actual: only the root build script is checked. Subproject and convention-plugin rules are missed, so the result is a success report, a broken build, and a
not_affectedVEX.
Matrix
| OS | Gradle (JDK) | sub | conv (buildSrc) | snippet in sub | root allprojects/subprojects (control) |
|---|---|---|---|---|---|
| Linux | 8.14.3 (21) | repro | repro | repro | refused (correct) |
| Linux | 9.8.0 (21) | repro | repro | repro | not run |
| macOS / Windows | — | untested; the planner is a pure static scan, so I expect the same result |
Each cell ran twice across this run (8.14.3 for sub, conv and snippet; 9.8.0 for sub, conv and snippet). I didn't bisect: the Gradle vendoring backend is new in 2463257 (#277), which is the current main and is unreleased (latest release v4.0.0 refuses Gradle vendoring outright).
Suspect code
crates/socket-patch-core/src/vendor/jvm/gradle.rs:117-120: onlyread_build_script(read, "")(the root) goes throughcheck_exclusive_content.crates/socket-patch-core/src/vendor/jvm/gradle.rs:156-158: targets are settings files of the root, buildSrc and included builds. Subproject build scripts and convention-plugin sources aren't targets.
Tested on main 2463257.
Activity
gradle: run 4 ledger (#461)
mikolalysenko commentedon Oct 1, 2026
[agent] Triaged as priority:p3 (Gradle). Related area to #428 (vendor run from a subproject) but a different cause: the
gradle_exclusive_content_conflictscan only reads settings files and the root build script, not subproject scripts or buildSrc/build-logic convention plugins. No duplicate or existing fix PR found.Generated by Claude Code
mikolalysenko commentedon Oct 1, 2026
[agent] Re-checked on main
9d718cf(Gradle bug-hunt run 5, ledger #319). This still reproduces: thesubshape exits 0 fromvendor, the fresh-checkout:app:printCpfails withCould not find org.apache.commons:commons-text:1.10.0, andvexreportsnot_affected. Nothing undervendor/jvm/changed since2463257.I found another variant with the same root cause: a script plugin applied from the root build script. The root
build.gradle(plugins { id 'java' },apply from: 'gradle/repos.gradle',implementation 'org.apache.commons:commons-text:1.10.0') loadsgradle/repos.gradle, which holds therepositories { exclusiveContent { forRepository { mavenCentral() }; filter { includeGroup 'org.apache.commons' } }; mavenCentral() }block.vendorexits 0 (applied), and every fresh-checkout build then fails withCould not find org.apache.commons:commons-text:1.10.0.check_exclusive_contentonly scans the root script's own text, so a literalapply from:path isn't followed. That's 2/2 on Gradle 8.14.3 on Linux. A fix that walks the build scripts should also follow literalapply from:targets inside the checkout.Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Claiming this issue as part of the full Gradle support campaign (agent / hosted / vendored modes, tested on Gradle 6.9/7.6/8.x/9.x on Linux, macOS and Windows). Branch: feat/gradle-support. Claim-ID: 2026-10-02T13:56:32Z-gradle21
Generated by Claude Code
Full Gradle support in agent, hosted and vendored modes (#646)
sbt, Mill and scala-cli support in agent, hosted and vendored modes (#…