ADR 0072: CodeQL blocks a pull request only for the high or critical alerts it introduces
Status: Accepted Date: 2026-10-13 Amends: ADR 0014 (supply chain security stack), its CodeQL section and the CodeQL line of its CI gate summary. Everything else in ADR 0014 stands. Issues: #494 (this change), #491 (the high alert that sat unnoticed).
Context
ADR 0014 lists “CodeQL alerts of severity high or critical” among the things
that stop a merge, and says CodeQL is configured in .github/workflows/codeql.yaml
with the security-extended suite. Neither holds today:
- CodeQL runs as the
CodeQLjob of.github/workflows/security.yaml, with thesecurity-extendedandsecurity-and-qualitysuites. - That job only uploads its results. The analysis is green whatever it finds,
so nothing stopped a pull request that added a high alert. A
highgo/path-injectionalert ininternal/logs/logs.gostayed open from the repository’s first day until a manual triage found it (#491). - A check stops a merge only when the
protect-mainorprotect-release-branchesruleset lists it as required. ADR 0014’s gate summary reads as if every check it names were enforced.
Blocking on every open high alert, including those already on the base branch, would charge an old alert to whichever pull request comes next and would get the gate switched off within a week.
Decision
- A separate gate job.
CodeQL high-alert gatein.github/workflows/security.yamlruns after theCodeQLjob on pull requests and runsscripts/codeql-alert-gate.sh. It reads the open CodeQL alerts of the pull request’s merge ref and of the base branch with the defaultGITHUB_TOKEN(security-events: read), so pull requests from forks are judged too. - What blocks. An open alert of high or critical
security_severity_levelthat is open on the pull request and not on the base branch fails the gate. Quality queries carry no security severity and never block. - What warns. An alert already open on the base branch is reported as a warning. It is tracked in the Security tab and by the weekly scheduled analysis, not charged to the next pull request.
- No silent pass. The gate runs under
always()and fails when the analysis did not succeed, so a failed or cancelled analysis cannot become a skipped, and therefore passing, required check. - False positives are cleared by dismissing the alert with a reason in the Security tab and re-running the gate.
- Enforcement lives in the rulesets. A check blocks a merge only when the
protect-mainandprotect-release-branchesrulesets list it as required; every other check is advisory until it is added there. ADR 0014’s gate summary states intent, and the rulesets are the source of truth. The gate blocks onceCodeQL high-alert gateis added to both.
ADR 0014’s CI gate summary line “CodeQL alerts of severity high or critical”
reads, under this ADR, as “CodeQL alerts of severity high or critical
introduced by the pull request (CodeQL high-alert gate)”.
Consequences
- A pull request that introduces a high or critical CodeQL alert goes red, and stops merging once the job is required in the rulesets.
- Alerts that predate a pull request do not block it. They need their own triage, which this ADR does not schedule.
- The gate depends on the code scanning API. An API outage fails the gate rather than passing it.
scripts/codeql-alert-gate.shis not acheck-*.shscript: it needs a pull request and the network, andcut-release.shruns everycheck-*.shwith neither.
Alternatives considered
- Block on every open high or critical alert. Rejected: it fails unrelated pull requests for an alert they did not add, and the usual answer to that is turning the gate off.
- Edit ADR 0014 in place. Rejected: accepted ADRs are immutable, and a new decision gets a new ADR.