Skip to content

ci: own the CodeQL configuration instead of GitHub's default setup - #1603

Closed
Jaro-c wants to merge 1 commit into
developfrom
ci/codeql-advanced-setup
Closed

Jaro-c wants to merge 1 commit into
developfrom
ci/codeql-advanced-setup

Conversation

@Jaro-c

@Jaro-c Jaro-c commented Aug 30, 2026

Copy link
Copy Markdown
Member

Summary

Step one of two. This replaces GitHub's CodeQL default setup with a workflow this repository owns. It deliberately does not fix the seven rust/cleartext-logging alerts; that is step two, and separating them is what makes either one measurable.

Why own it

Default setup only analyses the default branch. Every pull request lands on develop unscanned, and its alerts surface later attributed to whichever commit promoted develop to main. A finding is never attached to the change that introduced it, and a promotion presents a batch at once. It is also why measuring whether a fix worked has needed a promotion rather than a pull request — which is exactly what happened with #1599 earlier today.

And it cannot load a model pack, which is what step two needs.

What the seven alerts actually are

Worth writing down, because the first attempt at them was aimed at the wrong thing. rust/cleartext-logging takes its sources from SensitiveDataCall:

HeuristicNames::nameIndicatesSensitiveData(name, classification)

The source is the name of the called function. create_project_secrets matches on "secret", so its return value is treated as sensitive regardless of the fact that the return type is Result<()> and cannot carry a payload. Taint then follows the ? into Engine::run's error and reaches seven assertions that format it.

Two measurements that pin this down:

  • resources_health.rs calls engine.up(&file).await.unwrap() eighteen times on the same tainted value and not one is flagged. Only assert! interpolating {result:?} is a sink.
  • resources_health.rs:209 already asserts on the same tainted result with a plain message and no interpolation, and is not flagged.

So the fix belongs at the source, not at the seven sinks, and the query supplies ModelsAsDataBarrier for correcting a model. Rust has the extensible predicate:

extensible predicate barrierModel(path, output, kind, provenance, madId)

That is step two.

What this pull request changes, and what it must not

Where the analysis runs, not what it covers. The matrix is the same three languages default setup ran — rust, python, actions — at the same build-mode: none, on ubuntu-latest, with the same job names.

The control is that the alert count does not move. Seven open, one closed as fixed, measured on main at 88f6c36. If it moves after the switch, the migration is wrong, not the reasoning about the alerts.

Test plan

  • actionlint clean on the new file, and the YAML parses
  • 42 code lines, under the 300 soft limit
  • Baseline captured before writing anything: run 33285480496 on 88f6c36 ran Analyze (python), Analyze (rust) and Analyze (actions), all success, event dynamic
  • The action is pinned to a full SHA with a version comment, matching every other third-party action here, and github-actions is already a daily Dependabot ecosystem targeting develop
  • The workflow cannot run yet. While default setup is enabled, GitHub does not run a repository's own CodeQL workflow. It starts working when the repository is switched over in Settings, which is why workflow_dispatch is here.

Order of operations

  1. Merge this to develop.
  2. Promote to main, so the workflow exists on the default branch.
  3. Switch Settings → Advanced Security → CodeQL analysis from default to advanced. That disables default setup.
  4. workflow_dispatch on main, and check three languages analysed and the alert count unchanged.
  5. Only then, step two: the barrier model, verified by removing it and watching the seven come back. A barrierModel whose canonical path does not match fails silently and looks exactly like one that works, so "the alerts went away" is not evidence on its own.

Known gap, not fixed here

If somebody re-enables default setup in Settings, this workflow silently stops running and nothing in the repository would say so. A test cannot read repository settings without the API, so this is recorded rather than gated.

Checklist

  • Targets develop
  • Commits are signed off (DCO, git commit -s) and signed
  • Labels applied
  • No secrets, keys or credentials in code, logs or fixtures
  • Cargo.toml and debian/changelog untouched
  • Docs updated if behaviour changed. No product behaviour changed

Refs #3, #4, #5, #6, #7, #8, #9.

Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com

Default setup analysed this repository from a server-side configuration nothing
here could see or change. Two things it cannot do, and the second is the one that
pays for the first.

It only analyses the default branch. Every pull request landed on `develop`
unscanned, and its alerts surfaced later attributed to whichever commit promoted
`develop` to `main`, so a finding was never attached to the change that caused it
and a promotion presented a batch of them at once. That is why measuring the
effect of a fix on the seven `rust/cleartext-logging` alerts needed a promotion
rather than a pull request. This workflow runs on pull requests to both branches.

And it cannot load a model pack. Those seven alerts are a false positive with a
readable cause: `rust/cleartext-logging` takes its sources from
`SensitiveDataCall`, which matches on the NAME of the called function, so
`create_project_secrets` is a source because it contains "secret" and not because
of anything its `Result<()>` can carry. The query provides `ModelsAsDataBarrier`
for correcting a model like this, and a barrier model needs a configuration this
repository owns. That correction is deliberately NOT in this commit.

This change is meant to move where the analysis runs, not what it covers, so the
matrix is the same three languages default setup ran (rust, python, actions) at
the same `build-mode: none`, on the same runner. The control is that the alert
count does not move: seven open, one closed as fixed. If it moves, the migration
is wrong rather than the reasoning about it.

`workflow_dispatch` is here so the first run after the repository is switched over
does not have to wait for a push.

The pin is a full SHA with a version comment, like every other third-party action
here, and `github-actions` is already a daily Dependabot ecosystem targeting
`develop`, so it is maintained the same way the rest are.

Refs #3, #4, #5, #6, #7, #8, #9.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
@Jaro-c Jaro-c added type:ci CI/CD and automation type:security Security-relevant change prio:P2 Medium priority effort:M Medium status:review In review labels Aug 30, 2026
@Jaro-c

Jaro-c commented Aug 30, 2026

Copy link
Copy Markdown
Member Author

Blocked, and not by anything in this pull request.

Switching this repository to advanced setup is refused at the repository level. Settings → Advanced Security → CodeQL analysis offers both "Switch to advanced" and "Disable CodeQL" greyed out, each labelled "Blocked by organization".

The source is the org security configuration "Glyndor baseline", applied to 15 repositories and set as the default for new ones. Its own description names what it pins:

dependency graph, Dependabot alerts + security updates + malware alerts, secret scanning + push protection + validity checks, CodeQL default setup, private vulnerability reporting

So the workflow here is correct and inert: the CodeQL checks on this pull request fail because GitHub will not accept an uploaded analysis from a repository whose CodeQL is owned by an enforced configuration. That is the same block seen from the other side, which is the only useful thing this pull request has measured so far.

I checked feasibility after recommending the approach rather than before, which is the mistake worth recording here: I priced the trade-off between fixing the model and fixing the seven assertions carefully, and never asked whether the repository was permitted to make the change at all.

Leaving this open rather than closing it. The code is what would be merged if the organization configuration changes, and closing it would throw away the measurement. Marking status:blocked on the decision below.

What unblocking costs. Three shapes, and only the third is scoped to this repository:

  1. Detach podup from "Glyndor baseline". It would then lose the whole baseline — Dependabot, secret scanning, push protection — not just the CodeQL pin. Wrong trade.
  2. Relax the configuration's enforcement so repositories can override it. That applies to all 15, weakening the baseline everywhere to solve one repository's problem.
  3. A second configuration, identical to the baseline except code scanning is left unset, applied to podup alone. The other fourteen keep the baseline exactly as it is.

Third is the right shape, and it is still an organization-level change rather than a repository one, so it is the owner's call and not mine.

Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com

@Jaro-c Jaro-c added status:blocked Blocked on something else and removed status:review In review labels Aug 30, 2026
@Jaro-c

Jaro-c commented Aug 30, 2026

Copy link
Copy Markdown
Member Author

The run finished and the error is explicit, so recording it here rather than leaving the cause as my reading of the settings page:

##[error]Code Scanning could not process the submitted SARIF file:
CodeQL analyses from advanced configurations cannot be processed when the default setup is enabled

All three jobs build their database fine; only the upload is rejected, on all three languages. So the workflow itself is correct and would start working the moment the organization configuration stops pinning CodeQL to default setup. Nothing to change in this branch.

Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com

@Jaro-c

Jaro-c commented Aug 30, 2026

Copy link
Copy Markdown
Member Author

Closing. podup stays on CodeQL default setup, so this workflow has no use: while the organization configuration pins code scanning, GitHub refuses the analysis it uploads, which is what the three red checks here are.

What this decides, stated plainly rather than left implicit:

The barrier model is out of reach. rust/cleartext-logging takes its sources from SensitiveDataCall, which matches on the name of the called function — create_project_secrets is a source because it contains "secret", not because of anything its Result<()> can carry. The query provides ModelsAsDataBarrier for correcting exactly that, and Rust has the extensible predicate for it, but a model pack needs a configuration this repository owns. Default setup cannot load one. So the fix that would have addressed the cause rather than the seven sinks is not available here.

CodeQL still will not see a pull request. It runs on the default branch only, so everything lands on develop unscanned and its alerts surface later attributed to whichever commit promoted develop to main. That is why measuring the effect of #1599 needed a promotion rather than a pull request, and it stays that way.

What remains for the seven alerts is the narrower fix. Remove the tainted value from the seven assertion messages, which is certain to work — resources_health.rs:209 already asserts on the same tainted result with a plain message and is not flagged — at the cost of those seven losing the error they print, and without preventing the eighth. That is the option to take up if the alerts are to be closed by code rather than by a dismissal.

Not deleting the branch. The workflow here is what would be merged if the organization configuration ever changes, and its three red checks are the measurement that the block is upstream rather than in this file:

Code Scanning could not process the submitted SARIF file:
CodeQL analyses from advanced configurations cannot be processed when the default setup is enabled

Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com

@Jaro-c Jaro-c closed this Aug 30, 2026
Jaro-c added a commit that referenced this pull request Aug 30, 2026
…ns (#1611)

Seven rust/cleartext-logging alerts are open on main, all the same shape: an assertion in the integration suite formatting a Result that CodeQL traced back to create_project_secrets.

The source is the function's NAME. SensitiveDataCall matches on HeuristicNames::nameIndicatesSensitiveData, so create_project_secrets is a taint source because it contains 'secret', not because of anything its Result<()> can carry -- and #1599 already made it impossible for a payload to reach one of those errors. The fix that corrects the model rather than the sinks needs a barrier model, which needs a configuration this repository owns, which the organization's Glyndor baseline pins away. That was #1603 and it is closed.

Removing the formatted value removes the sink, and that is certain rather than hoped: resources_health.rs:209 already asserts on the same tainted result with a plain message and is not flagged, and eighteen .unwrap() calls on the same values are not flagged either. Only assert! interpolating the value is a sink.

Seven changed, eleven left alone. There are eighteen occurrences and CodeQL flagged seven; the rest are on paths the query does not reach. Nothing syntactic separates them, so each of the three files gains a note saying the inconsistency is deliberate -- without it the next reader tidies it up and the alerts come back.

The matches! assertion lost the most, so it is split rather than flattened: expect_err first, which prints the Ok value -- here () -- then the variant check. That keeps the difference between 'the run succeeded' and 'it failed with the wrong variant'.

The cost, stated rather than discovered: these seven no longer say which error they got, and nothing prevents an eighth, because a syntactic gate cannot tell the seven from the eleven.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Jaro-c added a commit that referenced this pull request Aug 30, 2026
Eight pull requests, no version bump and no tag. Both branches stay at 5.4.0 and release.yml fires only on a v* tag or a manual dispatch.

Update path: #1612 podup update's refusal on an apt-owned binary now says when apt will never do it either; #1609 that refusal now happens for Homebrew and Scoop too, which it never did; #1607 glyndor-archive-keyring becomes a hard Depends.
Security: #1611 the seven assertions CodeQL flagged stop printing the Result they assert on.
Housekeeping: #1606 podup stops publishing to crates.io; #1604 the two crates excluded from the workspace get their own Dependabot entries; #1605 the two files closest to the line-limit cap are split; #1601 the README says what depending on unattended-upgrades does not buy.

This promotion also measured the code_scanning rule added to protect-main earlier today. With seven rust/cleartext-logging alerts open on main, it came back CLEAN. GitHub's documented behaviour -- that the rule blocks on alerts introduced by the pull request rather than pre-existing ones -- is confirmed rather than read.

What it does not fix: code scanning still runs on the default branch only, so none of the eight was scanned on its own pull request. #1603 would have changed that and is closed, because the organization's Glyndor baseline pins CodeQL to default setup across fifteen repositories.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
@Jaro-c
Jaro-c deleted the ci/codeql-advanced-setup branch September 2, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort:M Medium prio:P2 Medium priority status:blocked Blocked on something else type:ci CI/CD and automation type:security Security-relevant change

Development

Successfully merging this pull request may close these issues.

1 participant