8.8 KiB
8.8 KiB
CodeQL Go Coverage RCA (2026-02-18)
1) Observed Evidence (exact commands/workflow paths/config knobs that control scope)
- Local CI-aligned command in VS Code task
Security: CodeQL Go Scan (CI-Aligned) [~60s]:codeql database create codeql-db-go --language=go --source-root=backend --codescanning-config=.github/codeql/codeql-config.yml --overwrite --threads=0codeql database analyze codeql-db-go --additional-packs=codeql-custom-queries-go --format=sarif-latest --output=codeql-results-go.sarif --sarif-add-baseline-file-info --threads=0
- Local pre-commit CodeQL Go scan command (
scripts/pre-commit-hooks/codeql-go-scan.sh):codeql database analyze codeql-db-go codeql/go-queries:codeql-suites/go-security-and-quality.qls --format=sarif-latest --output=codeql-results-go.sarif --sarif-add-baseline-file-info --threads=0
- Reproduced analyzer output from local run:
CodeQL scanned 175 out of 436 Go files in this invocation.Path filters have no effect for Go... 'paths' and 'paths-ignore' ... have no effect for this language.
- Workflow controlling CI scan:
.github/workflows/codeql.ymlon.pull_request.branches: [main, nightly]on.push.branches: [main, nightly, development]- Uses
github/codeql-action/init+autobuild+analyze. initcurrently does not setqueries, so suite selection is implicit.- Uses config file
./.github/codeql/codeql-config.yml.
- Config file:
.github/codeql/codeql-config.yml- Only
paths-ignoreentries for coverage/build artifacts; no Go-specific exclusions.
- Only
- Ground-truth file counts:
find backend -type f -name '*.go' | wc -l=>436find backend -type f -name '*.go' ! -name '*_test.go' | wc -l=>177go list -json ./... | jq -s 'map((.GoFiles|length)+(.CgoFiles|length))|add'=>175
- Target file verification:
- Local scan output includes extraction of
backend/internal/api/handlers/system_permissions_handler.go. - SARIF contains
go/path-injectionfindings in that file.
- Local scan output includes extraction of
2) Why 175/436 happens (expected vs misconfiguration)
- Expected behavior (primary):
436is a raw repository count including*_test.goand non-build files.- Go CodeQL analyzes build-resolved files (roughly Go compiler view), not all raw
.gofiles. - Build-resolved count is
175, which exactly matchesgo listcompiled files.
- Denominator inflation details:
259files are*_test.goand are not part of normal build-resolved extraction.- Two non-test files are also excluded from compiled set:
backend/internal/api/handlers/security_handler_test_fixed.go(//go:build ignore)backend/.venv/.../empty_template_main.go(not in module package graph)
- Conclusion:
175/436is mostly expected Go extractor semantics, not a direct scope misconfiguration by itself.
3) How this could miss findings
- Build tags / ignored files:
- Files behind build constraints (for example
//go:build ignore) are excluded from compiled extraction; findings there are missed.
- Files behind build constraints (for example
- Path filters:
- For Go,
paths/paths-ignoredo not reduce extraction scope (confirmed by CodeQL diagnostic). - Therefore
.github/codeql/codeql-config.ymlis not the cause of reduced Go coverage.
- For Go,
- Generated or non-module files:
- Files outside the module/package graph (for example under
.venv) can appear in raw counts but are not analyzed.
- Files outside the module/package graph (for example under
- Uncompiled packages/files:
- Any code not reachable in package resolution/build context will not be analyzed.
- Trigger gaps (CI event coverage):
pull_requestonly targetsmainandnightly; PRs todevelopmentare not scanned by CodeQL workflow.pushonly scansmain/nightly/development; feature-branch pushes are not scanned.
- Baseline behavior:
--sarif-add-baseline-file-infoadds baseline metadata; it does not itself suppress extraction.- Alert visibility can still appear delayed based on when a qualifying workflow run uploads SARIF.
- Local/CI suite drift (explicit evidence):
- CI workflow (
.github/workflows/codeql.yml) and VS Code CI-aligned task (.vscode/tasks.json) use implicit/default suite selection. - Pre-commit Go scan (
scripts/pre-commit-hooks/codeql-go-scan.sh) pins explicitgo-security-and-quality.qls.
- CI workflow (
4) Why finding appeared now (most plausible ranked causes with confidence)
- Trigger-path visibility gap (Plausible hypothesis, 0.60)
- The code likely existed before, but this remains a hypothesis unless workflow history shows explicit missing qualifying runs for the affected branch/PR path.
- Local/CI command drift labeled as “CI-aligned” (Medium-High, 0.70)
- Different entrypoints use different suite semantics (explicit in pre-commit vs implicit in workflow/task), increasing chance of inconsistent detection timing.
- Query/toolpack evolution over time (Medium, 0.55)
- Updated CodeQL packs/engines can surface dataflow paths not previously reported.
- Extractor file-count misunderstanding (Low, 0.25)
175/436itself did not hidesystem_permissions_handler.go; that file is in the extracted set.
5) Prevention controls (local + CI): exact changes to scan commands/workflows/policies
- CI workflow controls (
.github/workflows/codeql.yml):- Expand PR coverage to include
development:on.pull_request.branches: [main, nightly, development]
- Expand push coverage to active delivery branches (or remove push branch filter if acceptable).
- Pin query suite explicitly in
init(avoid implicit defaults):- add
queries: security-and-quality
- add
- Expand PR coverage to include
- Local command controls (make truly CI-aligned):
- Require one canonical local invocation path (single source of truth):
- Prefer VS Code task calling
scripts/pre-commit-hooks/codeql-go-scan.sh.
- Prefer VS Code task calling
- If task remains standalone, it must pin explicit suite:
codeql database analyze codeql-db-go codeql/go-queries:codeql-suites/go-security-and-quality.qls --additional-packs=codeql-custom-queries-go ...
- Require one canonical local invocation path (single source of truth):
- Policy controls:
- Require CodeQL checks as branch-protection gates on
main,nightly, anddevelopment. - Add a parity check that fails when suite selection diverges across workflow, VS Code local task, and pre-commit script.
- Keep reporting both metrics in documentation/logs:
- raw
.gocount - compiled/extracted
.gocount (go list-derived)
- raw
- Add metric guardrail: fail the run when extracted compiled Go count diverges from the
go listcompiled baseline beyond approved tolerance.
- Require CodeQL checks as branch-protection gates on
6) Verification checklist
- Run and record raw vs compiled counts:
find backend -type f -name '*.go' | wc -lcd backend && go list -json ./... | jq -s 'map((.GoFiles|length)+(.CgoFiles|length))|add'
- Run local CodeQL Go scan and confirm diagnostic line:
CodeQL scanned X out of Y Go files...
- Compare extraction metric to compiler baseline and fail on unexpected divergence:
- baseline:
cd backend && go list -json ./... | jq -s 'map((.GoFiles|length)+(.CgoFiles|length))|add' - extracted: parse
CodeQL scanned X out of Y Go files...and assertX == baseline(or documented tolerance)
- baseline:
- Confirm target file is extracted:
- local output includes
Done extracting .../system_permissions_handler.go
- local output includes
- Confirm SARIF includes expected finding for file:
jqfilter onsystem_permissions_handler.go
- Validate CI workflow trigger coverage includes intended PR targets/branches.
- Validate workflow and local command both use explicit
security-and-qualitysuite.
7) PR Slicing Strategy
-
Decision: Multiple PRs (3), to reduce rollout risk and simplify review.
-
Trigger reasons: Cross-domain change (workflow + local tooling + policy), security-sensitive, and high review impact if combined.
-
PR-1: CI Trigger/Suite Hardening
- Scope:
.github/workflows/codeql.yml - Changes: broaden
pull_requestbranch targets, keep/expand push coverage, set explicitqueries: security-and-quality. - Dependencies: none.
- Validation gate:
actionlint+ successful CodeQL run on PR todevelopment. - Rollback: revert workflow file only.
- Scope:
-
PR-2: Local Command Convergence
- Scope:
.vscode/tasks.jsonand/or canonical script wrapper. - Changes: enforce explicit
go-security-and-quality.qlsin local Go task, keep custom pack additive only. - Dependencies: PR-1 preferred, not hard-required.
- Validation gate: local task output shows explicit suite and reproducible SARIF.
- Rollback: revert tasks/scripts without affecting CI.
- Scope:
-
PR-3: Governance/Policy Guardrails
- Scope: branch protection requirements + parity check job/documentation.
- Changes: require CodeQL checks on
main/nightly/development; add drift guard. - Dependencies: PR-1 and PR-2.
- Validation gate: blocked merge when CodeQL missing/failing or parity check fails.