Priority: P1
Context
Two open critical Dependabot alerts, #33 and #34, both GHSA-574f-3g2m-x479 on org.bouncycastle:bcprov-jdk18on ("GOST 28147 CTR mode reuses keystream after 255 blocks").
They are two alerts for one advisory because the advisory declares multiple disjoint version ranges, and this repo has BouncyCastle on three different classpaths from two different sources:
| Alert |
Range |
Source |
Classpaths |
| #34 |
= 1.81.0 |
org.robolectric:robolectric:4.16.1 |
debugUnitTest{Compile,Runtime,LintChecks}Classpath |
| #33 |
>= 1.59, <= 1.80.1 |
com.android.tools:sdk-common:32.2.1 |
androidLintTool, UTP result-listener |
| #33 |
>= 1.59, <= 1.80.1 |
com.android.tools.build:apkzlib:9.2.1 |
root buildscript / plugin classpath |
Fixing only one side leaves 1.79 in the submitted graph and #33 stays open. Two levers are required.
Evidence
- BouncyCastle is absent from
releaseRuntimeClasspath, debugRuntimeClasspath, and app/gradle.lockfile. Build-time only — never ships in the APK.
- Reachability of the CVE itself is nil regardless: it is specific to GOST 28147 (a Russian national cipher) in CTR mode. Nothing in this codebase or the Android signing/lint toolchain invokes it.
- AGP is not a lever.
apkzlib 9.3.2 — the latest stable AGP — still declares bcprov 1.79 (verified by fetching the POM). Waiting for AGP does not fix this.
Correction to a committed claim
docs/security-posture.md (~line 340) states that plugin/buildscript-classpath transitives are AGP-version-gated and not user-forceable. That is false and was empirically disproven — see the WI-2b block below, which was actually run on this repo's Gradle 9.5.1 and produced bcpkix 1.79 -> 1.85, bcprov 1.79 -> 1.85. Tracked separately in the docs issue.
Proposed fix
Target 1.85 — not the alerts' first_patched_version
The advisory has a third range the alerts do not surface: >= 1.82, <= 1.83, fixed in 1.84. So the alerts' suggested 1.80.2 / 1.81.1 are dead-end branches.
1.85 is the newest version published for all three modules and clears every range. (bcprov alone has 1.85.2; bcpkix/bcutil stop at 1.85 — a 1.85.2 force fails with Could not find org.bouncycastle:bcpkix-jdk18on:1.85.2, confirmed by running it.) 1.85 also clears the already-dismissed #17/#18, whose rationale is now stale.
(a) :app project configurations
app/build.gradle.kts — first statement inside the existing resolutionStrategy.eachDependency { } (between current lines 309 and 310):
// GHSA-574f-3g2m-x479: AGP lint/UTP (`com.android.tools:sdk-common`) pulls
// bcpkix/bcprov/bcutil 1.79 onto `androidLintTool` and the UTP result-listener
// config; Robolectric 4.16.1 pulls bcprov 1.81 onto the debugUnitTest* classpaths.
// Build-time only — absent from both locked runtime classpaths and app/gradle.lockfile.
// 1.85 is the newest version published for all three modules and clears every range of
// this advisory, incl. the 1.82-1.83 branch the alerts' first_patched_version does NOT.
// Drop once stable AGP + Robolectric both ship BouncyCastle >= 1.84.
if (requested.group == "org.bouncycastle" && requested.version.orEmpty().startsWith("1.")) {
useVersion("1.85")
because(
"GHSA-574f-3g2m-x479: pin AGP-lint/UTP + Robolectric BouncyCastle to a " +
"patched release (build-time only; not shipped).",
)
}
(b) root buildscript (plugin) classpath
build.gradle.kts — between the header comment and the plugins { } block. In the Kotlin DSL buildscript {} must precede plugins {}:
// GHSA-574f-3g2m-x479: AGP 9.2.1's com.android.tools.build:apkzlib pulls
// bcpkix/bcprov/bcutil 1.79 onto the Gradle PLUGIN classpath — build-time only, never in
// the APK, but Automatic Dependency Submission force-resolves this classpath too, so it is
// what alert #33 sees. No stable AGP fixes it: apkzlib 9.3.2 still declares 1.79.
//
// `configurations.classpath` here IS the root buildscript classpath the `plugins {}` block
// resolves its markers on, so a force declared here reaches AGP's transitives.
// Drop once a stable AGP ships apkzlib with BouncyCastle >= 1.84.
buildscript {
configurations.classpath {
resolutionStrategy {
force(
"org.bouncycastle:bcprov-jdk18on:1.85",
"org.bouncycastle:bcpkix-jdk18on:1.85",
"org.bouncycastle:bcutil-jdk18on:1.85",
)
}
}
}
Neither block references a java.* package root, so the documented .kts shadow gotcha does not apply here.
Risk register
| Risk |
Status |
| BC 1.79 → 1.85 under apksig/apkzlib (APK signing) |
✅ Tested — debug + release packaging succeed, v2 signature verifies, signer cert ec9a4bb8…b3d9 unchanged |
BC 1.79 → 1.85 on androidLintTool |
✅ :app:lintDebug green |
| bcprov 1.81 → 1.85 under Robolectric 4.16.1 |
✅ Full testDebugUnitTest green, screenshot goldens included |
| BC on the UTP result-listener config (instrumented runs) |
⚠️ Not covered locally — gate on the required androidTest check |
useVersion/force downgrade semantics |
Accepted; guarded to the 1. line, drop-condition in comments |
| Mixed BC module versions |
Avoided by forcing all three together |
| APK / UAT impact |
None — lockfiles byte-identical → shipped artifact unchanged → no Room/UAT re-run |
Acceptance criteria
Same stop rule as the netty issue: if still open ~24 h post-merge after a successful submission run, dismiss as tolerable_risk rather than re-engineering.
Priority: P1
Context
Two open critical Dependabot alerts, #33 and #34, both
GHSA-574f-3g2m-x479onorg.bouncycastle:bcprov-jdk18on("GOST 28147 CTR mode reuses keystream after 255 blocks").They are two alerts for one advisory because the advisory declares multiple disjoint version ranges, and this repo has BouncyCastle on three different classpaths from two different sources:
= 1.81.0org.robolectric:robolectric:4.16.1debugUnitTest{Compile,Runtime,LintChecks}Classpath>= 1.59, <= 1.80.1com.android.tools:sdk-common:32.2.1androidLintTool, UTP result-listener>= 1.59, <= 1.80.1com.android.tools.build:apkzlib:9.2.1Fixing only one side leaves 1.79 in the submitted graph and #33 stays open. Two levers are required.
Evidence
releaseRuntimeClasspath,debugRuntimeClasspath, andapp/gradle.lockfile. Build-time only — never ships in the APK.apkzlib9.3.2 — the latest stable AGP — still declares bcprov 1.79 (verified by fetching the POM). Waiting for AGP does not fix this.Correction to a committed claim
docs/security-posture.md(~line 340) states that plugin/buildscript-classpath transitives are AGP-version-gated and not user-forceable. That is false and was empirically disproven — see the WI-2b block below, which was actually run on this repo's Gradle 9.5.1 and producedbcpkix 1.79 -> 1.85,bcprov 1.79 -> 1.85. Tracked separately in the docs issue.Proposed fix
Target 1.85 — not the alerts'
first_patched_versionThe advisory has a third range the alerts do not surface:
>= 1.82, <= 1.83, fixed in1.84. So the alerts' suggested 1.80.2 / 1.81.1 are dead-end branches.1.85 is the newest version published for all three modules and clears every range. (
bcprovalone has 1.85.2;bcpkix/bcutilstop at 1.85 — a 1.85.2 force fails withCould not find org.bouncycastle:bcpkix-jdk18on:1.85.2, confirmed by running it.) 1.85 also clears the already-dismissed #17/#18, whose rationale is now stale.(a)
:appproject configurationsapp/build.gradle.kts— first statement inside the existingresolutionStrategy.eachDependency { }(between current lines 309 and 310):(b) root buildscript (plugin) classpath
build.gradle.kts— between the header comment and theplugins { }block. In the Kotlin DSLbuildscript {}must precedeplugins {}:Neither block references a
java.*package root, so the documented.ktsshadow gotcha does not apply here.Risk register
ec9a4bb8…b3d9unchangedandroidLintTool:app:lintDebuggreentestDebugUnitTestgreen, screenshot goldens includedandroidTestcheckuseVersion/forcedowngrade semantics1.line, drop-condition in commentsAcceptance criteria
./gradlew --stopfirst (documented stale-daemon gotcha)./gradlew -q buildEnvironment | grep -i bouncyshows1.79 -> 1.85for bcpkix + bcprov, and bcutil 1.85./gradlew -q :app:dependencies | grep -i 'org\.bouncycastle' | grep -v '1\.85'produces no outputgit diff --stat app/gradle.lockfile settings-gradle.lockfileempty:app:assembleRelease+apksigner verify --print-certsstill reportsec9a4bb8…b3d9androidTestcheck green on the PRstate: fixedafter mergeSame stop rule as the netty issue: if still open ~24 h post-merge after a successful submission run, dismiss as
tolerable_riskrather than re-engineering.