diff --git a/.github/workflows/codeql-pr.yml b/.github/workflows/codeql-pr.yml index 240e7139d..93d865980 100644 --- a/.github/workflows/codeql-pr.yml +++ b/.github/workflows/codeql-pr.yml @@ -90,13 +90,13 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Initialize CodeQL - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: category: "/language:${{ matrix.language }}" upload: false @@ -197,13 +197,13 @@ jobs: ref: ${{ format('refs/pull/{0}/merge', github.event.pull_request.number) }} - name: Initialize CodeQL - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: category: "/language:${{ matrix.language }}-merge" upload: false diff --git a/.github/workflows/python-security.yml b/.github/workflows/python-security.yml index cd51cf81a..5994c19ab 100644 --- a/.github/workflows/python-security.yml +++ b/.github/workflows/python-security.yml @@ -191,7 +191,7 @@ jobs: if: always() && hashFiles('bandit-results.sarif') != '' # The explicit gate below still fails on every Medium+ Bandit result. continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: bandit-results.sarif category: bandit diff --git a/.github/workflows/sast-semgrep.yml b/.github/workflows/sast-semgrep.yml index b2e264988..266befd00 100644 --- a/.github/workflows/sast-semgrep.yml +++ b/.github/workflows/sast-semgrep.yml @@ -103,7 +103,7 @@ jobs: - name: Upload Semgrep SARIF to code scanning if: always() && hashFiles('semgrep-results.sarif') != '' continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: semgrep-results.sarif category: semgrep diff --git a/.github/workflows/scheduled-security-scan.yml b/.github/workflows/scheduled-security-scan.yml index dbf6b764f..5989f0df1 100644 --- a/.github/workflows/scheduled-security-scan.yml +++ b/.github/workflows/scheduled-security-scan.yml @@ -90,13 +90,13 @@ jobs: with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5 + uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis continue-on-error: true - uses: github/codeql-action/analyze@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5 + uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: category: "/language:${{ matrix.language }}-scheduled" @@ -131,7 +131,7 @@ jobs: - name: Upload Trivy SARIF to code scanning if: always() && hashFiles('trivy-results.sarif') != '' continue-on-error: true - uses: github/codeql-action/upload-sarif@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: trivy-results.sarif category: trivy-fs-scheduled diff --git a/.github/workflows/scorecard-analysis.yml b/.github/workflows/scorecard-analysis.yml index 3856a4666..2d2c4821e 100644 --- a/.github/workflows/scorecard-analysis.yml +++ b/.github/workflows/scorecard-analysis.yml @@ -65,6 +65,6 @@ jobs: # Scorecard posture is preserved in its SARIF-generation log; an # installation upload quota outage must not fail the default branch. continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: results.sarif diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 1fab22a48..7816f1a0a 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -130,7 +130,7 @@ jobs: - name: Upload gitleaks SARIF to code scanning if: always() && hashFiles('gitleaks-results.upload.sarif') != '' continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: gitleaks-results.upload.sarif category: gitleaks diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index c3b8fa5db..49299aa4d 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -225,7 +225,7 @@ jobs: # The reporter above is the vulnerability gate. Preserve an upload # quota failure in this step's log without reclassifying it as a CVE. continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: results.sarif # results.sarif is produced after checkout of the pull request head. @@ -383,7 +383,7 @@ jobs: if: always() && hashFiles('trivy-results.sarif') != '' # The parser above fails on every fixable Medium+ finding independently. continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: trivy-results.sarif category: trivy-fs @@ -457,7 +457,7 @@ jobs: id: upload_scorecard_sarif # Scorecard is soft repository-posture evidence; upload quota is external. continue-on-error: true - uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: results.sarif category: scorecard diff --git a/CHANGELOG.md b/CHANGELOG.md index f4903c2f3..dd78a9544 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ Semantic Versioning where the repository publishes a release. ### Fixed +- Aligned every `github/codeql-action` `init`/`analyze`/`upload-sarif` pin to one immutable SHA (`5595ccaf912efad79be6eef63a5619ff05969be3`, v4.37.6) after the upload-sarif-only Dependabot bump left analyzers on 4.37.0/4.37.5 (CWE-829). +- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context. - Parsed `opencode.jsonc` as JSONC (stripping `//` and `/* */` comments outside string literals) in the reasoning-effort guard and its contract tests, instead of raw `json.loads`, which rejected the file the moment it carried its first explanatory comment (added for the `contextual-orchestrator` provider block) with `Expecting property name enclosed in double quotes`. Comment markers inside string values, such as the `$schema` URL, are left untouched. - Download the pinned `uv` 0.12.1 exporter from the official GitHub Releases URL instead of `releases.astral.sh`, which now returns HTTP 403 and blocks org-wide OpenCode `coverage-evidence`. The SHA-256 pin is unchanged. The opener may follow one hop onto `release-assets.githubusercontent.com` or `objects.githubusercontent.com` and still rejects every other host, userinfo, non-HTTPS scheme, and nondefault port (ContextualWisdomLab/.github#1109). - Compared the trusted `uv` executable's post-install `--version` output against the real GitHub Releases build's full string, `uv 0.12.1 (x86_64-unknown-linux-gnu)`, instead of the bare `uv 0.12.1` the prior check required; the genuine release binary always prints the target triple, so every installation was failing the pin check immediately after the archive download itself was fixed (ContextualWisdomLab/.github#1109). diff --git a/docs/doctoring/codeql-action-single-version.md b/docs/doctoring/codeql-action-single-version.md new file mode 100644 index 000000000..3908cbe50 --- /dev/null +++ b/docs/doctoring/codeql-action-single-version.md @@ -0,0 +1,26 @@ +# CodeQL action single-version pin + +## Incident and buyer impact + +Dependabot opened an `upload-sarif` 4.37.6 bump while `codeql-pr.yml` +still ran `init`/`analyze` at 4.37.0 and `scheduled-security-scan.yml` +ran them at 4.37.5. A green upload does not prove the analyzer executed +the reviewed action. + +## Decision + +Pin every `github/codeql-action/{init,analyze,upload-sarif}` use to +`5595ccaf912efad79be6eef63a5619ff05969be3` (v4.37.6). Contract tests +reject per-file and org-wide splits. + +CWE-829 forbids including functionality from an untrusted or unreviewed +control sphere (MITRE, 2026). A second SHA is a second control sphere. + +## References + +MITRE. (2026). *CWE-829: Inclusion of functionality from untrusted +control sphere*. https://cwe.mitre.org/data/definitions/829.html + +GitHub. (n.d.). *Using the CodeQL action*. GitHub Docs. Retrieved +August 13, 2026, from +https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/using-the-codeql-action diff --git a/tests/test_codeql_pr_workflow_contract.py b/tests/test_codeql_pr_workflow_contract.py index 813385b23..9e916dc93 100644 --- a/tests/test_codeql_pr_workflow_contract.py +++ b/tests/test_codeql_pr_workflow_contract.py @@ -57,6 +57,27 @@ def test_codeql_action_steps_use_one_version_per_workflow() -> None: assert len(refs) == 1, f"{filename} mixes CodeQL action refs: {sorted(refs)}" +def test_codeql_workflows_share_one_upload_sarif_sha() -> None: + """CWE-829: every CodeQL action pin must be the Dependabot-reviewed SHA. + + An upload-sarif-only bump that leaves init/analyze on 4.37.0/4.37.5 + would analyze or upload with a second unreviewed control sphere. + """ + shas: set[str] = set() + tags: set[str] = set() + pin = re.compile( + r"github/codeql-action/(?:init|analyze|upload-sarif)@" + r"([0-9a-f]{40}) # (v\d+\.\d+\.\d+)" + ) + for path in (REPO_ROOT / ".github/workflows").glob("*.yml"): + for sha, tag in pin.findall(path.read_text(encoding="utf-8")): + shas.add(sha) + tags.add(tag) + + assert shas == {"5595ccaf912efad79be6eef63a5619ff05969be3"} + assert tags == {"v4.37.6"} + + def test_codeql_sarif_gate_logs_and_fails_only_unsuppressed_medium_plus( tmp_path: Path, ) -> None: