Repository navigation
Model packs (<lang>/ext, <lang>/ext-library-sources) silently apply zero models when the consumer's CodeQL CLI doesn't bundle the exact extensionTargets version #206
Description
Activity
Update: reproduced with the real, currently-shipping CLI — this is a live break today, not a future risk
Just installed the actual CodeQL CLI 2.26.2 (released today, one patch after our repo's pinned
2.26.1in.codeqlversion) and ran our realjava/extpack against it, unmodified:$ codeql --version CodeQL command-line toolchain release 2.26.2. $ codeql resolve qlpacks codeql/java-all is found in 3 same-priority locations... C:\Utils\codeql\qlpacks\codeql\java-all\9.2.2 ... $ cat java/ext/qlpack.yml extensionTargets: codeql/java-all: '9.2.1' # <-- does NOT satisfy 9.2.2 $ codeql test run java/test/security/CWE-089/spring-r2dbc 0 tests passed; 1 tests failed: FAILED: .../DatabaseClientSqlInjection.ql (0 actual results vs. 6 expected — silent, no compile error, no version-mismatch warning)This is not a synthetic/edited-pin repro — it's our actual, currently-published
java/ext/qlpack.yml(extensionTargets: codeql/java-all: '9.2.1'), run against the actual current CodeQL CLI release (2.26.2, bundlingjava-all 9.2.2). CI itself is unaffected right now only becauseci.ymlhappens to pin.codeqlversion: 2.26.1(bundlesjava-all 9.2.1, an exact match) — but any consumer who has already upgraded to 2.26.2 (a routine patch upgrade, released the same day I tested this) gets zero of our Spring R2DBC SQL-injection models applied, silently, right now.This confirms the issue isn't a hypothetical edge case that might bite someone eventually — a single patch-level CLI bump on the consumer's side, with no corresponding action from us, was enough to trigger it. Given how easily/quickly this reproduced with an unmodified, currently-published pack, I think this raises the urgency of picking one of the four options in the issue description — happy to implement whichever the maintainers prefer (my empirical testing above showed option 1, widening
extensionTargetsto a range like>=9.1.0 <=9.2.2, works without any MaD schema break across that span).Re-verified the range fix directly on 2.26.2 (not just extrapolated from the earlier 2.25.6 test): temporarily set
java/ext/qlpack.yml'sextensionTargetstocodeql/java-all: '>=9.1.0 <=9.2.2', re-ran the same test under CLI 2.26.2 —DatabaseClientSqlInjection.qlnow passes (1/1 tests passed), then reverted the temporary edit (confirmed cleangit status, no changes left behind). So the range approach is now confirmed to work at both ends of the span (9.1.2under CLI 2.25.6, and9.2.2under CLI 2.26.2), with the same unmodified pack/model rows in between.Answered a follow-up question directly: does it matter if the pack is a genuine GHCR-registry install vs. local
--search-path?Short answer: no — verified this empirically too, with a fully genuine registry install, not just local disk.
Warning
The pack is not merely ignored silently — CodeQL's own compiler logs this, buried in the build output:
WARNING: Extension pack 'githubsecuritylab/codeql-java-extensions' is unused. (.../githubsecuritylab/codeql-java-extensions/0.7.2/qlpack.yml:1,1-1)This is what actually happens when the
extensionTargetsconstraint doesn't match the CLI's resolvedcodeql/java-allversion: the entire pack is discarded at pack-resolution time. It's just a low-severityWARNING, not a build failure — the job still goes green, there's no SARIF-level indication, and nothing surfaces this to a typical consumer unless they know to grep build logs for "is unused".Once
extensions (java)finished publishinggithubsecuritylab/codeql-java-extensions@0.7.2to GHCR (via the in-flight patch-release run), I:codeql pack download githubsecuritylab/codeql-java-extensions@0.7.2— real network pull into~/.codeql/packages(confirmed:Installed fresh githubsecuritylab/codeql-java-extensions@0.7.2 (library)).- Temporarily moved the local
java/extdirectory entirely out of the repo tree (not just renamed — CodeQL discovers packs byqlpack.ymlname, not folder name, so a rename alone doesn't hide it from--search-path). - Ran
codeql pack install --additional-packs ..injava/test— this genuinely could not findgithubsecuritylab/codeql-java-extensionslocally anymore, fell through to the registry, and locked it for real:(# codeql-pack.lock.yml dependencies: githubsecuritylab/codeql-java-extensions: version: 0.7.2
codeql/java-allresolved to9.2.2via the CLI's own bundle, exactly like a normal consumer with no special config.) - Ran the test suite against this fully genuine, registry-resolved dependency graph (CLI 2.26.2,
codeql/java-all@9.2.2bundled,githubsecuritylab/codeql-java-extensions@0.7.2from GHCR, pinnedextensionTargets: '9.2.1'inside that published pack) — identical silent failure, 0/6 expected results, with the sameis unusedwarning in the compile output. - Restored everything (
java/extback in place,codeql-pack.lock.ymlreverted viagit checkout), confirmedgit statusclean, and reran the original local-search-path test to confirm no regression from the experiment.
Conclusion: the
extensionTargetsversion check is evaluated purely against whatevercodeql/<lang>-allthe CLI resolves for the analysis (its own bundled version, in the common case) — it doesn't matter at all whether the model pack itself was found via local disk/--search-pathor genuinelycodeql pack install-ed from GHCR with a real lock-file entry. This closes out the last open "maybe it behaves differently for real consumers" question — it doesn't. A real user who doescodeql pack installand picks up our just-published0.7.2today, on a completely stock CLI 2.26.2, gets zero Spring R2DBC SQLi coverage, with only the low-visibilityis unusedcompiler warning above as any indication — nothing at the SARIF/results level, and no failed build to flag it.Update: reproduced in a real, live hosted GitHub Actions code scanning run (not just local CLI testing)
To rule out "maybe this only shows up in local/manual testing," reproduced the same failure via an actual hosted GitHub Actions default-setup code scanning run against a small repo referencing the published pack
githubsecuritylab/codeql-java-extensions@0.7.2directly (via repository-level custom configuration, not a workflow file).The runner auto-resolved CodeQL CLI 2.26.2 (bundles
codeql/java-all9.2.2). Pack0.7.2'sextensionTargetsis pinned to'9.2.1'. The build log shows CodeQL's own compiler dropping the pack outright:WARNING: Extension pack 'githubsecuritylab/codeql-java-extensions' is unused.And the resulting code scanning analysis for that run came back with
results_count: 0(tool version 2.26.2) — the known SQL injection finding this pack is meant to catch was not detected, purely because of the version-pin mismatch, not a query bug.This confirms the behavior is identical across all three reproduction paths tried so far: local unmodified pin, genuine
codeql pack installfrom GHCR, and now a real hosted GitHub Actions default-setup run. Any consumer running CodeQL 2.26.2 (or any CLI whose bundledcodeql/java-allfalls outside a pinned patch version) silently gets zero coverage from this pack today.Next step:
0.7.3(re-pinnedextensionTargets: '9.2.2'to match CLI 2.26.2) is now published — retesting this same scenario next and will report back with results.Confirmed fixed: retested with
0.7.3(re-pinnedextensionTargets: '9.2.2')Re-ran the identical scenario from the previous comment, changing only the pack version from
0.7.2to the just-published0.7.3:packs: java: - githubsecuritylab/codeql-java-extensions@0.7.3
Same hosted GitHub Actions default-setup run, same auto-resolved CodeQL CLI 2.26.2 (
codeql/java-all9.2.2). This time:- No
Extension pack ... is unusedwarning anywhere in the build log — the pack is accepted. - The code scanning analysis for this run came back with
results_count: 1(previously0), confirming the known SQL injection finding is now detected.
Clean before/after pair, same repo and same CLI, only the pack version changed:
Pack version extensionTargetsCLI resolved java-allPack accepted? Results 0.7.2 '9.2.1'9.2.2 No — "is unused" 0 0.7.3 '9.2.2'9.2.2 Yes 1 This closes the loop on reproducing both the break and the fix end-to-end in a real hosted default-setup run (not just local CLI testing). The remaining open question for this issue is still the structural one: whether to keep doing exact-version re-pins on every CLI bump (as done here) or move to a range-based
extensionTargetsconstraint so consumers aren't silently dropped between releases.- No
- added a commit that references this issue
on Aug 4, 2026 Final fix landed & retested end-to-end:
extensionTargetsbroadened to'*'for all four languages (PR #212), released in0.7.4Landed the structural fix flagged as the open question in the previous comment:
extensionTargetsin every language'sextpack (csharp,go,java,python) is now pinned to'*'instead of an exact/narrowcodeql/<lang>-allversion. This means future CLI bumps no longer risk silently dropping the whole pack for any consumer whose CLI doesn't happen to match the exact pin — see #212. Published incodeql-java-extensions@0.7.4(and the equivalent releases for the other three languages).Retested the identical repro scenario from this issue against
0.7.4, this time including the case this issue is actually about — a genuinely mismatched/older CLI — not just the "matches-the-current-pin" case:CLI (bundled java-all)Pack Result 2.25.6 (9.1.2) 0.7.3(exact pin'9.2.2')WARNING: Extension pack 'githubsecuritylab/codeql-java-extensions' is unused.→ 0 results (same silent-drop failure documented above)2.25.6 (9.1.2) 0.7.4(extensionTargets: '*')No warning, pack applied → 1 result (SQL injection correctly detected) 2.26.2 (9.2.2, matches current .codeqlversion)0.7.4No warning, pack applied → 1 result Same database, same query, only the pack version changes between the first two rows — a clean before/after pair proving the fix (not just the exact-repin band-aid from
0.7.3) resolves the underlying issue for CLIs that don't match whatever the current pin happens to be.Also reconfirmed in a live, hosted GitHub Actions default-setup run using
0.7.4: pack installed cleanly with no "is unused" warning, and the finding was reported in code scanning as expected.This closes the loop: the original FN is fixed, and the fix has now been validated against a genuinely stale/mismatched CLI (not only the current one), a matching CLI, and a real hosted CI run. Going forward, CLI bumps no longer require re-pinning
extensionTargetsto avoid this failure mode.- added a commit that references this issue
on Aug 4, 2026
Summary
Every published model/extension pack in this repo (
csharp/ext,csharp/ext-library-sources,go/ext,java/ext,java/ext-library-sources,python/ext) pinsextensionTargetsto a single exactcodeql/<lang>-allversion, e.g.:Per the CodeQL model pack docs, a model pack's data extensions are only injected into an analysis if the
codeql/<lang>-allversion actually resolved for that analysis satisfiesextensionTargets. I've now empirically confirmed that when it doesn't, our sink/source/summary models are silently skipped — no error, no warning that mentions the mismatch — and the analysis just quietly loses that coverage.This affects any consumer whose CodeQL CLI doesn't happen to bundle exactly the pinned version — not just contributors testing locally.
Reproduction (100% confirmed, not just doc-reading)
Environment: local CLI
2.25.6(bundlescodeql/java-all9.1.2), repo pinned to CLI2.26.1(.codeqlversion),java/ext/qlpack.ymlpinsextensionTargets: codeql/java-all: '9.2.1'.codeql pack install java/test(no flags, exactly whatci.yml's "Install Dependencies" step runs):The CLI does not attempt to fetch the pinned
9.2.1from the registry — it silently substitutes its own bundled9.1.2via the implicit "found via--additional-packs" path, with only a generic reproducibility warning (nothing that says "this doesn't satisfy the pinned version").codeql test run java/test/security/CWE-089/spring-r2dbc --failing-exitcode=122 --verbosity=progress --ram=2048 --format=json(the exact commandci.yml's "Test Queries" step runs) — theDatabaseClientSqlInjection.qltest (added in Java: model Spring R2DBCDatabaseClientandio.r2dbc.spias SQL injection sinks #204) fails silently: 0 actual results vs. 6 expected,FAILED(RESULT), no compile error, no diagnostic aboutcodeql/java-allorextensionTargetsat all.Isolated the fix: temporarily changed
java/ext/qlpack.yml'sextensionTargetsfrom the exact pin'9.2.1'to a range,'>=9.1.0 <=9.2.1', and re-ran step 2 with the same CLI (2.25.6/java-all 9.1.2, nothing else changed) — all 6 expected results now pass. This proves:sinkModel/summaryModelis compatible across9.1.2–9.2.1(no breaking schema change in that span).(Reverted both experimental edits after confirming — no code changes are included in this issue.)
Root cause (two layers)
codeql/<lang>-allover fetching a different pinned version from the registry, when the bundled one is found via the CLI's implicit--additional-packs-style search path — with no check that the bundled version actually satisfies the declared dependency/lock-file version, and only a generic "you'll need--additional-packstoo" warning (not a version-mismatch warning). This appears to be true in both directions (older CLI + newer pin, or newer CLI + older pin) since resolution isn't semver-gated at that layer.extensionTargetsthen silently fails to match whatevercodeql/<lang>-allversion actually got resolved (per (1)), so the model pack'sdataExtensionsare never injected into the analysis — with no error/warning at all from this layer either.Net effect: any consumer of a published
<lang>/extor<lang>/ext-library-sourcespack — not just local contributors — whose CodeQL CLI doesn't bundle acodeql/<lang>-allinside our pinnedextensionTargetsrange gets zero of that pack's models applied, silently, with no signal anything went wrong.Scope
Every language's
ext/ext-library-sourcespack pins an exactextensionTargetsversion today, all set by.github/scripts/pin-codeql-library-versions.shon every.codeqlversionbump:csharp/ext,csharp/ext-library-sourcescodeql/csharp-all: '7.1.0'go/extcodeql/go-all: '7.2.1'java/ext,java/ext-library-sourcescodeql/java-all: '9.2.1'python/extcodeql/python-all: '7.2.1'So this isn't specific to the Spring R2DBC models (#204) — it's a systemic, repo-wide characteristic of how every model pack we publish is pinned.
What we can do today (partial, undocumented workaround)
Historical pack versions remain published on GHCR (e.g.
githubsecuritylab/codeql-java-extensionshas 13 tags going back ~3 years), and each version's OCI manifest carries acom.github.codeql.cli.versionannotation recording which CLI built it — so a consumer could in principle pin their own dependency to an older, compatible release matching their CLI. But: (a) nothing documents this mapping anywhere today (a user would need to inspect GHCR manifests directly), and (b) it doesn't fully solve determinism on its own, since layer (1) above can still substitute an unexpected bundled version underneath even a correctly-pinned extensions pack.Options to consider (needs a maintainer decision, not solo-fixing this)
extensionTargetsto a version range (e.g.>=X.Y.0or a wider bound) instead of re-pinning to the exact bundle version on every CLI bump — confirmed technically viable above, but needs a decision on how wide is safe (risk: if the MaD schema ever changes within the chosen range, that's a real compile break, not just a silent skip) and whetherpin-codeql-library-versions.sh's "always overwrite to exact bundle version" approach should change forextensionTargetsspecifically (still fine fordependencies:, which resolves/locks correctly).com.github.codeql.cli.versionmanifest annotations) so consumers on older CLIs know which pack version to pin — doesn't fix the silent-failure mode, just makes the existing workaround discoverable.--additional-packspath aren't checked against the declared dependency version at all, with only a generic (not version-specific) warning. Searchedgithub/codeqlissues; didn't find an existing report of this exact behavior.Flagging this for a maintainer decision rather than picking one unilaterally, per this repo's convention that CLI-compatibility trade-offs are a judgment call, not something to resolve solo.