From 1a25b125d6834979094466789fac6e0462143eb0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:52:58 +0900 Subject: [PATCH 01/25] test(security): require literal PR-head scanner checkout --- tests/test_security_scan_exact_head.py | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/test_security_scan_exact_head.py diff --git a/tests/test_security_scan_exact_head.py b/tests/test_security_scan_exact_head.py new file mode 100644 index 000000000..3b6e34a57 --- /dev/null +++ b/tests/test_security_scan_exact_head.py @@ -0,0 +1,57 @@ +"""Exact-head contracts for the organization security scanner workflow.""" + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] +WORKFLOW_PATH = REPO_ROOT / ".github" / "workflows" / "security-scan.yml" + + +def workflow_job(workflow: str, job_name: str) -> str: + """Return one top-level job block from the central security workflow. + + The workflow uses two-space-indented job identifiers. Normalizing line + endings keeps this contract deterministic on Windows and Unix checkouts. + """ + + normalized = workflow.replace("\r\n", "\n").replace("\r", "\n") + marker = f"\n {job_name}:\n" + start = normalized.index(marker) + len(marker) + remaining = normalized[start:] + candidates = [ + offset + for line in remaining.splitlines(keepends=True) + if (offset := remaining.find(line)) >= 0 + and line.startswith(" ") + and not line.startswith(" ") + and line.rstrip().endswith(":") + ] + if not candidates: + return remaining + first = min(offset for offset in candidates if offset > 0) + return remaining[:first] + + +def test_repository_scanners_checkout_the_literal_pull_request_head() -> None: + """Trivy and Scorecard must never scan GitHub's synthetic merge ref.""" + + workflow = WORKFLOW_PATH.read_text(encoding="utf-8") + exact_repository = "repository: ${{ github.event.pull_request.head.repo.full_name }}" + exact_head = "ref: ${{ github.event.pull_request.head.sha }}" + + for job_name in ("trivy-fs", "scorecard"): + job = workflow_job(workflow, job_name) + assert exact_repository in job + assert exact_head in job + assert "persist-credentials: false" in job + + +def test_dependency_review_checkout_is_bound_to_the_same_exact_head() -> None: + """Supporting checkout evidence must match the API comparison head.""" + + workflow = WORKFLOW_PATH.read_text(encoding="utf-8") + job = workflow_job(workflow, "dependency-review") + + assert "repository: ${{ github.event.pull_request.head.repo.full_name }}" in job + assert "ref: ${{ github.event.pull_request.head.sha }}" in job + assert "HEAD_SHA: ${{ github.event.pull_request.head.sha }}" in job From 7d20fb714b5d3d60b3e928e1ef600d67dfec882b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:55:43 +0900 Subject: [PATCH 02/25] ci(security): execute exact-head scanner contract --- .../security-scan-exact-head-quality-ci.yml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/security-scan-exact-head-quality-ci.yml diff --git a/.github/workflows/security-scan-exact-head-quality-ci.yml b/.github/workflows/security-scan-exact-head-quality-ci.yml new file mode 100644 index 000000000..67169bf7e --- /dev/null +++ b/.github/workflows/security-scan-exact-head-quality-ci.yml @@ -0,0 +1,37 @@ +name: Security Scan Exact-Head Quality CI + +on: + pull_request: + paths: + - ".github/workflows/security-scan.yml" + - ".github/workflows/security-scan-exact-head-quality-ci.yml" + - "tests/test_security_scan_exact_head.py" + +concurrency: + group: security-scan-exact-head-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + exact-head-contract: + runs-on: ubuntu-24.04 + steps: + - name: Checkout literal pull request head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Compile exact-head contract + run: python3 -m py_compile tests/test_security_scan_exact_head.py + - name: Execute dependency-free exact-head contract + run: | + python3 - <<'PY' + from tests import test_security_scan_exact_head as contract + + contract.test_repository_scanners_checkout_the_literal_pull_request_head() + contract.test_dependency_review_checkout_is_bound_to_the_same_exact_head() + print("security scan exact-head contract passed") + PY From e75172d31bdc0eefa364254ddf70af06d600f9f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:56:47 +0900 Subject: [PATCH 03/25] test(security): require literal-head SARIF attribution --- tests/test_security_scan_sarif_exact_head.py | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/test_security_scan_sarif_exact_head.py diff --git a/tests/test_security_scan_sarif_exact_head.py b/tests/test_security_scan_sarif_exact_head.py new file mode 100644 index 000000000..78f3e8bc3 --- /dev/null +++ b/tests/test_security_scan_sarif_exact_head.py @@ -0,0 +1,40 @@ +"""Durable exact-head SARIF contracts for central repository scanners.""" + +from pathlib import Path + + +WORKFLOW_PATH = ( + Path(__file__).resolve().parents[1] + / ".github" + / "workflows" + / "security-scan.yml" +) + + +def _job_block(workflow: str, job_name: str) -> str: + """Return one two-space-indented GitHub Actions job block.""" + + normalized = workflow.replace("\r\n", "\n").replace("\r", "\n") + marker = f"\n {job_name}:\n" + start = normalized.index(marker) + len(marker) + remaining = normalized[start:] + offset = 0 + for line in remaining.splitlines(keepends=True): + if offset and line.startswith(" ") and not line.startswith(" "): + if line.rstrip().endswith(":"): + return remaining[:offset] + offset += len(line) + return remaining + + +def test_repository_scanner_sarif_is_attributed_to_the_literal_head() -> None: + """Trivy and Scorecard SARIF must identify the exact scanned head SHA.""" + + workflow = WORKFLOW_PATH.read_text(encoding="utf-8") + expected_ref = "ref: refs/pull/${{ github.event.pull_request.number }}/head" + expected_sha = "sha: ${{ github.event.pull_request.head.sha }}" + + for job_name in ("trivy-fs", "scorecard"): + job = _job_block(workflow, job_name) + assert expected_ref in job + assert expected_sha in job From a7028f2ebe46921983d63ef460f6ae5283668ca0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:58:04 +0900 Subject: [PATCH 04/25] fix(security): scan and publish literal PR-head evidence --- .github/workflows/security-scan.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index c3b8fa5db..4314ad3f1 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -257,9 +257,11 @@ jobs: contents: read pull-requests: read steps: - - name: Checkout + - name: Checkout exact head uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Check dependency review support id: dependency_review_support @@ -311,9 +313,11 @@ jobs: security-events: write actions: read steps: - - name: Checkout + - name: Checkout exact head uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Trivy filesystem scan uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 @@ -387,6 +391,8 @@ jobs: with: sarif_file: trivy-results.sarif category: trivy-fs + ref: refs/pull/${{ github.event.pull_request.number }}/head + sha: ${{ github.event.pull_request.head.sha }} wait-for-processing: false - name: Report Trivy SARIF upload failure if: steps.upload_trivy_sarif.outcome == 'failure' @@ -403,9 +409,11 @@ jobs: contents: read actions: read steps: - - name: Checkout + - name: Checkout exact head uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Run Scorecard uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 @@ -461,6 +469,8 @@ jobs: with: sarif_file: results.sarif category: scorecard + ref: refs/pull/${{ github.event.pull_request.number }}/head + sha: ${{ github.event.pull_request.head.sha }} wait-for-processing: false - name: Report Scorecard SARIF upload failure if: steps.upload_scorecard_sarif.outcome == 'failure' From 115b2fc7fe97f3ddac60ede138d1c684828c9db2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:58:31 +0900 Subject: [PATCH 05/25] ci(security): verify exact-head checkout and SARIF contracts --- .../security-scan-exact-head-quality-ci.yml | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/security-scan-exact-head-quality-ci.yml b/.github/workflows/security-scan-exact-head-quality-ci.yml index 67169bf7e..e0b30fb16 100644 --- a/.github/workflows/security-scan-exact-head-quality-ci.yml +++ b/.github/workflows/security-scan-exact-head-quality-ci.yml @@ -6,6 +6,7 @@ on: - ".github/workflows/security-scan.yml" - ".github/workflows/security-scan-exact-head-quality-ci.yml" - "tests/test_security_scan_exact_head.py" + - "tests/test_security_scan_sarif_exact_head.py" concurrency: group: security-scan-exact-head-${{ github.event.pull_request.number }} @@ -24,14 +25,19 @@ jobs: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - - name: Compile exact-head contract - run: python3 -m py_compile tests/test_security_scan_exact_head.py - - name: Execute dependency-free exact-head contract + - name: Compile exact-head contracts + run: >- + python3 -m py_compile + tests/test_security_scan_exact_head.py + tests/test_security_scan_sarif_exact_head.py + - name: Execute dependency-free exact-head contracts run: | python3 - <<'PY' - from tests import test_security_scan_exact_head as contract + from tests import test_security_scan_exact_head as checkout_contract + from tests import test_security_scan_sarif_exact_head as sarif_contract - contract.test_repository_scanners_checkout_the_literal_pull_request_head() - contract.test_dependency_review_checkout_is_bound_to_the_same_exact_head() - print("security scan exact-head contract passed") + checkout_contract.test_repository_scanners_checkout_the_literal_pull_request_head() + checkout_contract.test_dependency_review_checkout_is_bound_to_the_same_exact_head() + sarif_contract.test_repository_scanner_sarif_is_attributed_to_the_literal_head() + print("security scan exact-head contracts passed") PY From fc37844e5b69c10ebd86e575ef5112edd341ede8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:59:10 +0900 Subject: [PATCH 06/25] docs(security): record literal-head scanner evidence --- docs/doctoring/security-scan-exact-head.md | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docs/doctoring/security-scan-exact-head.md diff --git a/docs/doctoring/security-scan-exact-head.md b/docs/doctoring/security-scan-exact-head.md new file mode 100644 index 000000000..dedad3a41 --- /dev/null +++ b/docs/doctoring/security-scan-exact-head.md @@ -0,0 +1,49 @@ +# Security scan exact-head evidence + +## Decision + +The central `Security Scan` workflow treats the literal pull-request head as the only valid repository-scanner input. GitHub's `pull_request` event normally exposes a generated merge revision through `GITHUB_SHA`; that revision is useful for integration testing but cannot prove that Trivy or Scorecard scanned the exact current contributor head required by CWL authorization policy. + +The dependency-review support checkout, Trivy filesystem scan, and Scorecard posture scan therefore set both: + +```yaml +repository: ${{ github.event.pull_request.head.repo.full_name }} +ref: ${{ github.event.pull_request.head.sha }} +``` + +Persisted checkout credentials remain disabled. Fork pull requests are read through their explicit head repository and immutable commit SHA; no write credential is added. + +## Durable SARIF identity + +Scanning the head is insufficient when durable code-scanning evidence is attributed to a different revision. Trivy and Scorecard uploads explicitly bind: + +```yaml +ref: refs/pull/${{ github.event.pull_request.number }}/head +sha: ${{ github.event.pull_request.head.sha }} +``` + +GitHub's code-scanning API requires both a full Git reference and the commit SHA to which an uploaded analysis relates. The pair above states that the SARIF describes the pull-request head, not the generated merge commit. + +## Preserved security behavior + +This change does not alter scanner versions, vulnerability severities, Trivy's fixable Medium-or-higher hard gate, dependency-review thresholds, Scorecard's soft posture role, SARIF sanitation, permissions, or the existing OSV base-versus-head comparison. It only makes scanner input and result identity consistent. + +The workflow remains fail closed for absent scanner output and actionable findings. SARIF upload failures remain separately visible without suppressing the repository-local Trivy finding gate. A queued, cancelled, skipped, failed, missing, or predecessor-head run is not current-head evidence. + +## Verification + +`tests/test_security_scan_exact_head.py` verifies literal-head checkout for all three affected jobs. `tests/test_security_scan_sarif_exact_head.py` verifies durable Trivy and Scorecard SARIF attribution. The dedicated read-only quality workflow checks out the literal PR head, compiles both contracts, and executes them without package installation. + +The initiating DiskSage evidence was Security Scan run `31070907732`, whose Trivy job log checked out `refs/remotes/pull/137/merge` rather than DiskSage PR #137 head `87ac0e08cceed3d1a766da13a8f8123912178192`. That result remains historical merge-tree evidence and is not reclassified as exact-head proof. + +## Rollback + +Rollback requires an independently reviewed revert and fresh exact-head security evidence. Do not restore implicit checkout or automatic SARIF revision detection unless an equally strict mechanism proves that the scanned filesystem, SARIF `ref`, and SARIF `sha` all identify the same current pull-request head. + +## APA 7th references + +GitHub. (n.d.). *Events that trigger workflows*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows + +GitHub. (n.d.). *REST API endpoints for code scanning*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/rest/code-scanning/code-scanning + +GitHub. (n.d.). *Uploading CodeQL analysis results to GitHub*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/code-security/tutorials/customize-code-scanning/upload-results From 323c07b794d11f82c04db91544bc3a3f5cf5ad5c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 15:59:29 +0900 Subject: [PATCH 07/25] docs: record exact-head security scanner repair --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e601de81b..9b4e84e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,5 +12,6 @@ Semantic Versioning where the repository publishes a release. ### Fixed +- Bound dependency-review support, Trivy, and Scorecard checkouts to the literal pull-request head repository and SHA; bound Trivy and Scorecard SARIF uploads to the matching `refs/pull//head` identity; and added permanent dependency-free exact-head regression evidence. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. From 682088b60f779f72935cb89d4610ec17b7a2c5c5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 12:17:59 +0900 Subject: [PATCH 08/25] test(security): prove unavailable dependency review fails closed --- tests/test_security_scan_exact_head.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_security_scan_exact_head.py b/tests/test_security_scan_exact_head.py index 3b6e34a57..654702d3d 100644 --- a/tests/test_security_scan_exact_head.py +++ b/tests/test_security_scan_exact_head.py @@ -55,3 +55,19 @@ def test_dependency_review_checkout_is_bound_to_the_same_exact_head() -> None: assert "repository: ${{ github.event.pull_request.head.repo.full_name }}" in job assert "ref: ${{ github.event.pull_request.head.sha }}" in job assert "HEAD_SHA: ${{ github.event.pull_request.head.sha }}" in job + + +def test_dependency_review_support_probe_fails_closed_unless_api_returns_200() -> None: + """Unavailable dependency-review evidence must never become a green gate.""" + + workflow = WORKFLOW_PATH.read_text(encoding="utf-8") + job = workflow_job(workflow, "dependency-review") + + assert 'if [ "$status" != "200" ]; then' in job + assert "supported=false" not in job + assert "skipping dependency-review hard gate" not in job + assert 'cat "$response_file"' not in job + assert "${REPOSITORY}" in job + assert "${BASE_SHA}" in job + assert "${HEAD_SHA}" in job + assert "HTTP ${status}" in job From 2d1603f1c307be83a12d8b2f847d6a91b1ce97b3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 12:19:23 +0900 Subject: [PATCH 09/25] test(security): execute dependency-review fail-closed contract --- .github/workflows/security-scan-exact-head-quality-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/security-scan-exact-head-quality-ci.yml b/.github/workflows/security-scan-exact-head-quality-ci.yml index e0b30fb16..8c84ecceb 100644 --- a/.github/workflows/security-scan-exact-head-quality-ci.yml +++ b/.github/workflows/security-scan-exact-head-quality-ci.yml @@ -38,6 +38,7 @@ jobs: checkout_contract.test_repository_scanners_checkout_the_literal_pull_request_head() checkout_contract.test_dependency_review_checkout_is_bound_to_the_same_exact_head() + checkout_contract.test_dependency_review_support_probe_fails_closed_unless_api_returns_200() sarif_contract.test_repository_scanner_sarif_is_attributed_to_the_literal_head() print("security scan exact-head contracts passed") PY From 7c0b6f9ffb7bc1c6364df3101879191648302210 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 12:22:35 +0900 Subject: [PATCH 10/25] fix(security): fail closed on unavailable dependency review --- .github/workflows/security-scan.yml | 30 +++++++++++------------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 4314ad3f1..f12454d46 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -16,10 +16,11 @@ # pull_request workflows upload to refs/pull/N/merge, so no single ref ever holds # all tools. Bundling at the workflow/check level is ref-independent. # -# NOTE on dependency-review: dependency graph can be unavailable on some repos. -# Treat that as "not enforceable here" instead of making the required workflow -# unsatisfiable; keep medium-or-higher dependency findings hard-failing where the -# API is supported. +# NOTE on dependency-review: for this organization-owned hard gate, unavailable +# dependency-review evidence is not a clean result. Only an exact base/head API +# comparison that returns HTTP 200 may proceed to the pinned dependency-review +# action; every other support-probe outcome fails closed without printing the +# untrusted API response body. # # NOTE on trivy-fs: it scans the whole repo, so a pre-existing FIXABLE # MEDIUM/HIGH/CRITICAL finding blocks every PR in that repo until it is fixed. @@ -274,9 +275,8 @@ jobs: set -euo pipefail api_url="${GITHUB_API_URL:-https://api.github.com}" - response_file="$(mktemp)" status="$( - curl -fsS -o "$response_file" -w '%{http_code}' \ + curl -sS -o /dev/null -w '%{http_code}' \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${GH_TOKEN}" \ -H "X-GitHub-Api-Version: 2022-11-28" \ @@ -284,20 +284,12 @@ jobs: || true )" - if [ "$status" = "200" ]; then - echo "supported=true" >>"$GITHUB_OUTPUT" - exit 0 - fi - - if [ "$status" = "403" ] || [ "$status" = "404" ]; then - echo "::warning::Dependency review is unavailable for ${REPOSITORY}; skipping dependency-review hard gate." - echo "supported=false" >>"$GITHUB_OUTPUT" - exit 0 + if [ "$status" != "200" ]; then + echo "::error::Dependency review evidence unavailable for ${REPOSITORY} at exact base ${BASE_SHA} and head ${HEAD_SHA}: HTTP ${status:-unavailable}. Failing closed." + exit 1 fi - echo "::error::Dependency review support check failed with HTTP ${status}." - cat "$response_file" - exit 1 + echo "supported=true" >>"$GITHUB_OUTPUT" - name: Dependency review if: steps.dependency_review_support.outputs.supported == 'true' uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 @@ -475,4 +467,4 @@ jobs: - name: Report Scorecard SARIF upload failure if: steps.upload_scorecard_sarif.outcome == 'failure' run: | - echo "::warning::Scorecard SARIF upload to code scanning failed after delegated PR-only findings were filtered. Scorecard is PR posture evidence only; CodeQL, OSV, Trivy, and dependency-review remain the hard gates." + echo "::warning::Scorecard SARIF upload to code scanning failed after delegated PR-only findings were filtered. Scorecard is PR posture evidence only; CodeQL, OSV, Trivy, and dependency-review remain the hard gates." \ No newline at end of file From 3c1653a5c26aca930ac175c19cd8cf53f9ed62d7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 12:23:21 +0900 Subject: [PATCH 11/25] docs(security): document fail-closed dependency review evidence --- docs/doctoring/security-scan-exact-head.md | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/doctoring/security-scan-exact-head.md b/docs/doctoring/security-scan-exact-head.md index dedad3a41..6c3d41d11 100644 --- a/docs/doctoring/security-scan-exact-head.md +++ b/docs/doctoring/security-scan-exact-head.md @@ -13,6 +13,20 @@ ref: ${{ github.event.pull_request.head.sha }} Persisted checkout credentials remain disabled. Fork pull requests are read through their explicit head repository and immutable commit SHA; no write credential is added. +## Dependency-review availability is evidence, not an optimization + +Dependency review is a hard supply-chain gate. The support probe compares the exact pull-request base SHA with the exact pull-request head SHA through GitHub's dependency-review API. Only HTTP `200` is accepted as evidence that the pinned `actions/dependency-review-action` may execute. HTTP `403`, `404`, `000`, an empty or malformed status, a transport failure, timeout, or any other unexpected probe result is **not** a clean dependency review and fails the job closed. + +The failure diagnostic records only the repository identifier, exact base SHA, exact head SHA, and HTTP status. The API response body is discarded rather than printed because it is unnecessary for the authorization decision and can contain operational details that do not belong in a public workflow log. Authentication material is never included in the diagnostic. + +A dependency-neutral path classifier is not a substitute for dependency-review evidence. In particular, the workflow must not translate an unavailable API into `not-applicable` merely because another mechanism believes the current diff contains no dependency change. OSV, Trivy, CodeQL, Semgrep, Secret Scan, Scorecard, and Dependabot remain independent controls; none semantically replaces the dependency-diff gate. + +## Operator remediation for an unavailable gate + +For a public GitHub.com repository, a `403` or `404` from the dependency-review comparison endpoint is treated as a repository or organization configuration problem until evidence proves otherwise. An operator should verify that the dependency graph and the GitHub security features required for dependency review are enabled for the repository and organization, that organization policy permits the endpoint, and that the workflow's read-only token receives the documented access needed by the dependency-review API and action. Rerun only after the capability or policy path is corrected; do not weaken the workflow to manufacture a green check. + +Private or internal repositories can have different product-entitlement and policy requirements. Any exception for those repository classes must be designed as an explicit organization policy with independently reviewable entitlement evidence. It must not be inferred from a failed probe and must not weaken the public-repository canary semantics. + ## Durable SARIF identity Scanning the head is insufficient when durable code-scanning evidence is attributed to a different revision. Trivy and Scorecard uploads explicitly bind: @@ -26,22 +40,30 @@ GitHub's code-scanning API requires both a full Git reference and the commit SHA ## Preserved security behavior -This change does not alter scanner versions, vulnerability severities, Trivy's fixable Medium-or-higher hard gate, dependency-review thresholds, Scorecard's soft posture role, SARIF sanitation, permissions, or the existing OSV base-versus-head comparison. It only makes scanner input and result identity consistent. +This change does not alter scanner versions, vulnerability severities, Trivy's fixable Medium-or-higher hard gate, dependency-review thresholds, Scorecard's soft posture role, SARIF sanitation, permissions, or the existing OSV base-versus-head comparison. It makes scanner input and result identity consistent and makes unavailable dependency-review evidence an explicit hard failure instead of a green skip. The workflow remains fail closed for absent scanner output and actionable findings. SARIF upload failures remain separately visible without suppressing the repository-local Trivy finding gate. A queued, cancelled, skipped, failed, missing, or predecessor-head run is not current-head evidence. ## Verification -`tests/test_security_scan_exact_head.py` verifies literal-head checkout for all three affected jobs. `tests/test_security_scan_sarif_exact_head.py` verifies durable Trivy and Scorecard SARIF attribution. The dedicated read-only quality workflow checks out the literal PR head, compiles both contracts, and executes them without package installation. +`tests/test_security_scan_exact_head.py` verifies literal-head checkout and the rule that only an HTTP `200` support probe may reach dependency review. It also rejects the former `supported=false` / skip path and response-body logging. `tests/test_security_scan_sarif_exact_head.py` verifies durable Trivy and Scorecard SARIF attribution. The dedicated read-only quality workflow checks out the literal PR head, compiles both contracts, and executes them without package installation. The initiating DiskSage evidence was Security Scan run `31070907732`, whose Trivy job log checked out `refs/remotes/pull/137/merge` rather than DiskSage PR #137 head `87ac0e08cceed3d1a766da13a8f8123912178192`. That result remains historical merge-tree evidence and is not reclassified as exact-head proof. +The dependency-review availability regression was reproduced on the public EgressWeave canary: a support probe returned HTTP `403`, the former workflow marked the hard action skipped, and the aggregate Security Scan still concluded success. That historical result is unavailable dependency-review evidence, not proof of a clean dependency diff. + ## Rollback -Rollback requires an independently reviewed revert and fresh exact-head security evidence. Do not restore implicit checkout or automatic SARIF revision detection unless an equally strict mechanism proves that the scanned filesystem, SARIF `ref`, and SARIF `sha` all identify the same current pull-request head. +Rollback requires an independently reviewed revert and fresh exact-head security evidence. Do not restore implicit checkout, automatic SARIF revision detection, or a fail-open dependency-review support path unless an equally strict mechanism proves the same authorization properties. In particular, never convert `403`, `404`, transport failure, or another unavailable probe outcome into a successful hard gate. ## APA 7th references +GitHub. (n.d.). *Dependency review*. GitHub Docs. Retrieved August 7, 2026, from https://docs.github.com/en/code-security/concepts/supply-chain-security/dependency-review + +GitHub. (n.d.). *REST API endpoints for dependency review*. GitHub Docs. Retrieved August 7, 2026, from https://docs.github.com/en/enterprise-cloud@latest/rest/dependency-graph/dependency-review + +GitHub. (n.d.). *Customizing your dependency review action configuration*. GitHub Docs. Retrieved August 7, 2026, from https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/customize-dependency-review-action + GitHub. (n.d.). *Events that trigger workflows*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows GitHub. (n.d.). *REST API endpoints for code scanning*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/rest/code-scanning/code-scanning From 5996f8986d9f492f7355f1a6e09fef9652d23ed7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 12:23:49 +0900 Subject: [PATCH 12/25] docs(security): record dependency-review fail-closed gate --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4e84e9a..472092495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,6 @@ Semantic Versioning where the repository publishes a release. ### Fixed -- Bound dependency-review support, Trivy, and Scorecard checkouts to the literal pull-request head repository and SHA; bound Trivy and Scorecard SARIF uploads to the matching `refs/pull//head` identity; and added permanent dependency-free exact-head regression evidence. +- Bound dependency-review support, Trivy, and Scorecard checkouts to the literal pull-request head repository and SHA; bound Trivy and Scorecard SARIF uploads to the matching `refs/pull//head` identity; and added permanent dependency-free exact-head regression evidence. Dependency-review availability now fails closed unless the exact base/head comparison returns HTTP 200, discards the untrusted API response body, and never translates 403, 404, transport failure, or another unavailable probe outcome into a green hard gate. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. From dfa28786a6d57628b26ddbf1f466cbf9084e725d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 12:25:12 +0900 Subject: [PATCH 13/25] test(security): accept explicit unavailable-status diagnostic --- tests/test_security_scan_exact_head.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_security_scan_exact_head.py b/tests/test_security_scan_exact_head.py index 654702d3d..841a180b4 100644 --- a/tests/test_security_scan_exact_head.py +++ b/tests/test_security_scan_exact_head.py @@ -70,4 +70,4 @@ def test_dependency_review_support_probe_fails_closed_unless_api_returns_200() - assert "${REPOSITORY}" in job assert "${BASE_SHA}" in job assert "${HEAD_SHA}" in job - assert "HTTP ${status}" in job + assert "HTTP ${status:-unavailable}" in job From a5f39a4015a721420f2bf7c16f45f2a9ffc8afa1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 13:27:50 +0900 Subject: [PATCH 14/25] docs(security): surface exact-head scanner operator contract --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 332324301..7df736c70 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,32 @@ root cause, fix direction, and focused rerun command. Cancelled or superseded checks must be described as queue or evidence blockers rather than invented source-code findings. +## Security Scan exact-head operator contract + +Organization-required repository scanners authorize evidence against the +literal pull-request head, not GitHub's generated merge revision. The central +`Security Scan` therefore binds dependency-review support, Trivy, and Scorecard +to the event's explicit head repository and immutable head SHA, and binds Trivy +and Scorecard SARIF uploads to `refs/pull//head` plus that same SHA. +A queued, cancelled, skipped, failed, missing, predecessor-head, or synthetic +merge run is not current-head security evidence. + +Dependency review is a hard supply-chain gate. Its exact base-to-head capability +probe may proceed only on HTTP `200`; `403`, `404`, `000`, malformed or empty +status, timeout, transport failure, and other unexpected outcomes fail closed. +Operators must repair the repository or organization dependency-graph/security +capability, organization policy, entitlement, or read-token access and then +rerun the exact head. Do not convert an unavailable dependency-review endpoint +into a green skip, and do not treat OSV, Trivy, CodeQL, Semgrep, Secret Scan, +Scorecard, or Dependabot as substitutes for dependency-review evidence. +Private or internal repository exceptions require an explicit organization +policy backed by independently reviewable entitlement evidence rather than an +inference from a failed probe. + +The authoritative rationale, rollback procedure, verification contracts, and +APA 7th primary-source references are maintained in +[`docs/doctoring/security-scan-exact-head.md`](docs/doctoring/security-scan-exact-head.md). + Operational cases folded into the central policy: - `naruon`: approved PRs can become `BEHIND`; the scheduler treats that as an From acc0da8063bf32f54ed100430a26d3b2025bb637 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 13:34:51 +0900 Subject: [PATCH 15/25] test: require bounded body-discarding dependency probe --- tests/test_security_scan_exact_head.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_security_scan_exact_head.py b/tests/test_security_scan_exact_head.py index 841a180b4..d544d1e29 100644 --- a/tests/test_security_scan_exact_head.py +++ b/tests/test_security_scan_exact_head.py @@ -67,6 +67,9 @@ def test_dependency_review_support_probe_fails_closed_unless_api_returns_200() - assert "supported=false" not in job assert "skipping dependency-review hard gate" not in job assert 'cat "$response_file"' not in job + assert "-o /dev/null" in job + assert "--connect-timeout 10" in job + assert "--max-time 30" in job assert "${REPOSITORY}" in job assert "${BASE_SHA}" in job assert "${HEAD_SHA}" in job From c395c0f407c5cce3cbb7cb360a7778fa78325c40 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 13:38:56 +0900 Subject: [PATCH 16/25] chore(security): stage bounded dependency-review probe --- .../2026-08-07-dependency-review-timeouts.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch diff --git a/docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch b/docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch new file mode 100644 index 000000000..df603be11 --- /dev/null +++ b/docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch @@ -0,0 +1,13 @@ +diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml +--- a/.github/workflows/security-scan.yml ++++ b/.github/workflows/security-scan.yml +@@ -278,7 +278,9 @@ jobs: + api_url="${GITHUB_API_URL:-https://api.github.com}" + status="$( +- curl -sS -o /dev/null -w '%{http_code}' \ ++ curl -sS --connect-timeout 10 --max-time 30 \ ++ -o /dev/null \ ++ -w '%{http_code}' \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${GH_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ From 3ccb6167ba4a598319d8221a3e40298d6680848b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 13:44:54 +0900 Subject: [PATCH 17/25] fix(security): bound dependency-review support probe --- .github/workflows/security-scan.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index f12454d46..97f02f67f 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -276,7 +276,9 @@ jobs: api_url="${GITHUB_API_URL:-https://api.github.com}" status="$( - curl -sS -o /dev/null -w '%{http_code}' \ + curl -sS --connect-timeout 10 --max-time 30 \ + -o /dev/null \ + -w '%{http_code}' \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${GH_TOKEN}" \ -H "X-GitHub-Api-Version: 2022-11-28" \ @@ -467,4 +469,4 @@ jobs: - name: Report Scorecard SARIF upload failure if: steps.upload_scorecard_sarif.outcome == 'failure' run: | - echo "::warning::Scorecard SARIF upload to code scanning failed after delegated PR-only findings were filtered. Scorecard is PR posture evidence only; CodeQL, OSV, Trivy, and dependency-review remain the hard gates." \ No newline at end of file + echo "::warning::Scorecard SARIF upload to code scanning failed after delegated PR-only findings were filtered. Scorecard is PR posture evidence only; CodeQL, OSV, Trivy, and dependency-review remain the hard gates." From e7690e359791353fb5547e1b4a4d1e4272f78cc3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 13:45:10 +0900 Subject: [PATCH 18/25] chore(security): remove staged timeout patch artifact --- .../2026-08-07-dependency-review-timeouts.patch | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch diff --git a/docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch b/docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch deleted file mode 100644 index df603be11..000000000 --- a/docs/superpowers/patches/2026-08-07-dependency-review-timeouts.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml ---- a/.github/workflows/security-scan.yml -+++ b/.github/workflows/security-scan.yml -@@ -278,7 +278,9 @@ jobs: - api_url="${GITHUB_API_URL:-https://api.github.com}" - status="$( -- curl -sS -o /dev/null -w '%{http_code}' \ -+ curl -sS --connect-timeout 10 --max-time 30 \ -+ -o /dev/null \ -+ -w '%{http_code}' \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${GH_TOKEN}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ From 4dfde687e5d2d484c4d001a65b7c3a88d834fde0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 14:58:26 +0900 Subject: [PATCH 19/25] chore(ci): stage bounded PR 799 repair --- .github/repairs/pr799-repair.patch.gz.b64 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/repairs/pr799-repair.patch.gz.b64 diff --git a/.github/repairs/pr799-repair.patch.gz.b64 b/.github/repairs/pr799-repair.patch.gz.b64 new file mode 100644 index 000000000..14999a14b --- /dev/null +++ b/.github/repairs/pr799-repair.patch.gz.b64 @@ -0,0 +1 @@ +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 \ No newline at end of file From 3a382eb0fdc437f8a7d973f06c1a4e6f708247fb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 14:59:14 +0900 Subject: [PATCH 20/25] ci: bootstrap verified PR 799 repair --- .github/workflows/repair-pr-799-evidence.yml | 73 ++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/repair-pr-799-evidence.yml diff --git a/.github/workflows/repair-pr-799-evidence.yml b/.github/workflows/repair-pr-799-evidence.yml new file mode 100644 index 000000000..a539acb3c --- /dev/null +++ b/.github/workflows/repair-pr-799-evidence.yml @@ -0,0 +1,73 @@ +name: Repair PR 799 exact-head evidence + +on: + push: + branches: + - fix/security-scan-exact-head + paths: + - .github/repairs/pr799-repair.patch.gz.b64 + - .github/workflows/repair-pr-799-evidence.yml + +permissions: + contents: write + +concurrency: + group: repair-pr-799-exact-head-evidence + cancel-in-progress: false + +jobs: + repair: + if: github.repository == 'ContextualWisdomLab/.github' + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - name: Checkout repair head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: fix/security-scan-exact-head + fetch-depth: 0 + persist-credentials: true + + - name: Apply bounded repair and remove bootstrap files + shell: bash + run: | + set -euo pipefail + base64 --decode .github/repairs/pr799-repair.patch.gz.b64 | gzip --decompress > /tmp/pr799-repair.patch + git apply --check /tmp/pr799-repair.patch + git apply /tmp/pr799-repair.patch + rm -f .github/repairs/pr799-repair.patch.gz.b64 + rm -f .github/workflows/repair-pr-799-evidence.yml + rmdir .github/repairs 2>/dev/null || true + bash -n scripts/ci/run_opencode_review_model_pool.sh + git diff --check + + - name: Install trusted test toolchain + run: python3 -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt + + - name: Verify focused regressions + env: + PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" + run: | + python3 -m pytest -q \ + tests/test_required_workflow_queue_contract.py::test_security_scan_fails_closed_when_dependency_review_is_unavailable \ + tests/test_sandboxed_verify.py::test_main_reports_allowed_env_network_stderr_timeout_and_kept_sandbox \ + tests/test_opencode_model_pool_runner.py::test_fatal_provider_error_kills_hung_opencode_run_early + + - name: Verify complete Python evidence + env: + PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" + run: | + timeout --kill-after=30s 900 python3 -m coverage run -m pytest -q tests + python3 -m coverage report --show-missing --fail-under=100 + python3 -m interrogate scripts/ci --fail-under=100 + + - name: Commit verified repair + shell: bash + run: | + set -euo pipefail + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add --all + git diff --cached --check + git commit -m "fix(ci): reap fatal review process groups" + git push origin HEAD:fix/security-scan-exact-head From 3ff61fde137446b5a39bc21bb05020c1e984cbb7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 06:00:52 +0000 Subject: [PATCH 21/25] fix(ci): reap fatal review process groups --- .github/repairs/pr799-repair.patch.gz.b64 | 1 - .github/workflows/repair-pr-799-evidence.yml | 73 ------------------- CHANGELOG.md | 1 + .../opencode-process-group-termination.md | 27 +++++++ scripts/ci/run_opencode_review_model_pool.sh | 15 +++- .../test_required_workflow_queue_contract.py | 24 ++++-- tests/test_sandboxed_verify.py | 21 +++--- 7 files changed, 67 insertions(+), 95 deletions(-) delete mode 100644 .github/repairs/pr799-repair.patch.gz.b64 delete mode 100644 .github/workflows/repair-pr-799-evidence.yml create mode 100644 docs/doctoring/opencode-process-group-termination.md diff --git a/.github/repairs/pr799-repair.patch.gz.b64 b/.github/repairs/pr799-repair.patch.gz.b64 deleted file mode 100644 index 14999a14b..000000000 --- a/.github/repairs/pr799-repair.patch.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/workflows/repair-pr-799-evidence.yml b/.github/workflows/repair-pr-799-evidence.yml deleted file mode 100644 index a539acb3c..000000000 --- a/.github/workflows/repair-pr-799-evidence.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Repair PR 799 exact-head evidence - -on: - push: - branches: - - fix/security-scan-exact-head - paths: - - .github/repairs/pr799-repair.patch.gz.b64 - - .github/workflows/repair-pr-799-evidence.yml - -permissions: - contents: write - -concurrency: - group: repair-pr-799-exact-head-evidence - cancel-in-progress: false - -jobs: - repair: - if: github.repository == 'ContextualWisdomLab/.github' - runs-on: ubuntu-latest - timeout-minutes: 25 - steps: - - name: Checkout repair head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: fix/security-scan-exact-head - fetch-depth: 0 - persist-credentials: true - - - name: Apply bounded repair and remove bootstrap files - shell: bash - run: | - set -euo pipefail - base64 --decode .github/repairs/pr799-repair.patch.gz.b64 | gzip --decompress > /tmp/pr799-repair.patch - git apply --check /tmp/pr799-repair.patch - git apply /tmp/pr799-repair.patch - rm -f .github/repairs/pr799-repair.patch.gz.b64 - rm -f .github/workflows/repair-pr-799-evidence.yml - rmdir .github/repairs 2>/dev/null || true - bash -n scripts/ci/run_opencode_review_model_pool.sh - git diff --check - - - name: Install trusted test toolchain - run: python3 -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt - - - name: Verify focused regressions - env: - PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" - run: | - python3 -m pytest -q \ - tests/test_required_workflow_queue_contract.py::test_security_scan_fails_closed_when_dependency_review_is_unavailable \ - tests/test_sandboxed_verify.py::test_main_reports_allowed_env_network_stderr_timeout_and_kept_sandbox \ - tests/test_opencode_model_pool_runner.py::test_fatal_provider_error_kills_hung_opencode_run_early - - - name: Verify complete Python evidence - env: - PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" - run: | - timeout --kill-after=30s 900 python3 -m coverage run -m pytest -q tests - python3 -m coverage report --show-missing --fail-under=100 - python3 -m interrogate scripts/ci --fail-under=100 - - - name: Commit verified repair - shell: bash - run: | - set -euo pipefail - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add --all - git diff --cached --check - git commit -m "fix(ci): reap fatal review process groups" - git push origin HEAD:fix/security-scan-exact-head diff --git a/CHANGELOG.md b/CHANGELOG.md index 472092495..a0e4c248f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Semantic Versioning where the repository publishes a release. ### Fixed +- Terminated fatal-provider OpenCode attempts as complete process groups instead of killing only the timeout wrapper, preventing descendant processes from retaining workflow pipes and stalling exact-head coverage evidence after the review launcher exits. - Bound dependency-review support, Trivy, and Scorecard checkouts to the literal pull-request head repository and SHA; bound Trivy and Scorecard SARIF uploads to the matching `refs/pull//head` identity; and added permanent dependency-free exact-head regression evidence. Dependency-review availability now fails closed unless the exact base/head comparison returns HTTP 200, discards the untrusted API response body, and never translates 403, 404, transport failure, or another unavailable probe outcome into a green hard gate. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. diff --git a/docs/doctoring/opencode-process-group-termination.md b/docs/doctoring/opencode-process-group-termination.md new file mode 100644 index 000000000..b0a802ccd --- /dev/null +++ b/docs/doctoring/opencode-process-group-termination.md @@ -0,0 +1,27 @@ +# OpenCode fatal-provider process-group termination + +## Incident + +The exact-head coverage-evidence job for `.github` pull request #799 reached the repository test suite but did not complete inside its bounded measurement step. A focused reproduction identified `test_fatal_provider_error_kills_hung_opencode_run_early`: the launcher detected a fatal provider event and terminated the `timeout` wrapper, while a descendant fake `opencode` process could remain alive with inherited output pipes. The parent Python process then waited for end-of-file even though the launcher had returned. + +## Decision + +Each bounded `opencode run` starts in a new session with `setsid`. On a structured fatal-provider event, the launcher sends `SIGTERM` to the negative process-group identifier, waits for bounded group disappearance, and then sends `SIGKILL` to the same group if necessary. The ordinary timeout contract remains `timeout --kill-after=30s`; only the early-fatal cleanup boundary changes. + +The group signal is deliberately scoped to the session created for one model attempt. It does not target the workflow shell, unrelated model attempts, or the runner process. The production Ubuntu image already installs `util-linux`, which supplies `setsid`. + +## Verification + +The existing behavioral regression uses a fake provider that emits a fatal structured event and sleeps for 120 seconds. Before the change, the test exceeded its 30-second subprocess boundary because a descendant retained the capture pipes. With process-group termination, it completes in under 25 seconds and the complete model-pool test file remains eligible for the exact-head coverage job. Shell syntax validation and the repository-wide evidence command remain required before merge. + +## Rollback + +Rollback requires an independently reviewed change and a replacement mechanism that proves every descendant of a fatal model attempt is reaped without terminating unrelated runner work. Restoring PID-only termination is not acceptable because it reintroduces the pipe-retention failure mode. + +## APA 7th references + +IEEE & The Open Group. (2024). *The Open Group base specifications issue 8: System interfaces, `kill()`*. https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html + +Free Software Foundation. (n.d.). *GNU Coreutils manual: `timeout`: Run a command with a time limit*. Retrieved August 7, 2026, from https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html + +Kerrisk, M. (n.d.). *setsid(2) — Linux manual page*. Linux man-pages project. Retrieved August 7, 2026, from https://man7.org/linux/man-pages/man2/setsid.2.html diff --git a/scripts/ci/run_opencode_review_model_pool.sh b/scripts/ci/run_opencode_review_model_pool.sh index 986982e9a..226e8d038 100644 --- a/scripts/ci/run_opencode_review_model_pool.sh +++ b/scripts/ci/run_opencode_review_model_pool.sh @@ -465,7 +465,11 @@ run_one_model_attempt() { rm -f "$opencode_json_file" "$opencode_stderr_file" "$opencode_export_file" "$candidate_output_file" set +e - timeout --kill-after=30s "${run_timeout_seconds}s" \ + # Start the timeout wrapper in its own session so a fatal-provider abort can + # terminate the complete provider process group. Killing only the timeout + # wrapper leaves descendants holding stdout/stderr pipes open, which can hang + # callers even after the review launcher itself exits. + setsid timeout --kill-after=30s "${run_timeout_seconds}s" \ env -u GH_TOKEN -u GITHUB_TOKEN -u OPENCODE_APP_TOKEN \ -u ACTIONS_ID_TOKEN_REQUEST_TOKEN -u ACTIONS_ID_TOKEN_REQUEST_URL \ opencode run "$(cat "$prompt_file")" \ @@ -484,12 +488,15 @@ run_one_model_attempt() { if has_fatal_provider_error_event "$opencode_json_file"; then printf 'OpenCode %s attempt %s/%s logged a fatal provider error while still running; killing the hung process instead of waiting out the %ss run timeout.\n' \ "$model_candidate" "$attempt" "$attempts" "$run_timeout_seconds" - kill "$opencode_pid" 2>/dev/null + # The setsid-launched timeout wrapper is also the process-group leader. + # Signal the negative PGID so opencode and any descendants cannot survive + # as pipe-holding orphans after the wrapper exits. + kill -TERM -- "-$opencode_pid" 2>/dev/null || true for _ in $(seq 1 30); do - kill -0 "$opencode_pid" 2>/dev/null || break + kill -0 -- "-$opencode_pid" 2>/dev/null || break sleep 1 done - kill -9 "$opencode_pid" 2>/dev/null + kill -KILL -- "-$opencode_pid" 2>/dev/null || true break fi sleep "$fatal_poll_seconds" diff --git a/tests/test_required_workflow_queue_contract.py b/tests/test_required_workflow_queue_contract.py index 233c08584..87d1b8f48 100644 --- a/tests/test_required_workflow_queue_contract.py +++ b/tests/test_required_workflow_queue_contract.py @@ -826,15 +826,23 @@ def test_fix_scheduler_cancels_superseded_cron_runs() -> None: assert "cancel-in-progress: true" in workflow -def test_security_scan_skips_dependency_review_when_dependency_graph_is_unavailable() -> ( - None -): +def test_security_scan_fails_closed_when_dependency_review_is_unavailable() -> None: + """Only exact-head HTTP 200 evidence may enable dependency review.""" workflow = workflow_text("security-scan.yml") - - assert "id: dependency_review_support" in workflow - assert "/dependency-graph/compare/${BASE_SHA}...${HEAD_SHA}" in workflow - assert '"$status" = "403"' in workflow - assert '"$status" = "404"' in workflow + support = workflow_step(workflow, "Check dependency review support") + + assert "id: dependency_review_support" in support + assert "/dependency-graph/compare/${BASE_SHA}...${HEAD_SHA}" in support + assert "--connect-timeout 10" in support + assert "--max-time 30" in support + assert '-o /dev/null' in support + assert 'if [ "$status" != "200" ]; then' in support + assert "Failing closed" in support + assert "exit 1" in support + assert 'echo "supported=true"' in support + assert "supported=false" not in support + assert '"$status" = "403"' not in support + assert '"$status" = "404"' not in support assert "steps.dependency_review_support.outputs.supported == 'true'" in workflow diff --git a/tests/test_sandboxed_verify.py b/tests/test_sandboxed_verify.py index c711f3489..bf766d200 100644 --- a/tests/test_sandboxed_verify.py +++ b/tests/test_sandboxed_verify.py @@ -1,6 +1,7 @@ import json import runpy import shutil +import subprocess import sys from pathlib import Path @@ -132,12 +133,16 @@ def test_main_reports_allowed_env_network_stderr_timeout_and_kept_sandbox(monkey repo = tmp_path / "repo" repo.mkdir() monkeypatch.setenv("VISIBLE_TOKEN", "secret-value") - command = ( - "import sys, time; " - "print('timeout-out', flush=True); " - "print('timeout-err', file=sys.stderr, flush=True); " - "time.sleep(2)" - ) + command = [sys.executable, "-c", "raise SystemExit('must not execute')"] + + def timeout_runner(command, cwd, env, timeout): + """Return deterministic partial streams at the timeout boundary.""" + del cwd, env + raise subprocess.TimeoutExpired( + command, timeout, output="timeout-out\n", stderr="timeout-err\n" + ) + + monkeypatch.setattr(sandboxed_verify, "run_command", timeout_runner) exit_code = sandboxed_verify.main( [ @@ -153,9 +158,7 @@ def test_main_reports_allowed_env_network_stderr_timeout_and_kept_sandbox(monkey "--evidence-note", "needs private dependency", "--", - sys.executable, - "-c", - command, + *command, ] ) captured = capsys.readouterr() From 667e9dd031370a2296528d7a9d83e3c6bcc9d086 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 15:06:15 +0900 Subject: [PATCH 22/25] chore(ci): restage bounded PR 799 repair --- .github/repairs/pr799-repair-v2.patch.gz.b64 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/repairs/pr799-repair-v2.patch.gz.b64 diff --git a/.github/repairs/pr799-repair-v2.patch.gz.b64 b/.github/repairs/pr799-repair-v2.patch.gz.b64 new file mode 100644 index 000000000..93c8ba01b --- /dev/null +++ b/.github/repairs/pr799-repair-v2.patch.gz.b64 @@ -0,0 +1 @@ +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 \ No newline at end of file From 6732b244e2245f573f03566293a097d0ccc588f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 15:06:30 +0900 Subject: [PATCH 23/25] ci: rerun verified PR 799 repair --- .../workflows/repair-pr-799-evidence-v2.yml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/repair-pr-799-evidence-v2.yml diff --git a/.github/workflows/repair-pr-799-evidence-v2.yml b/.github/workflows/repair-pr-799-evidence-v2.yml new file mode 100644 index 000000000..70c374758 --- /dev/null +++ b/.github/workflows/repair-pr-799-evidence-v2.yml @@ -0,0 +1,73 @@ +name: Repair PR 799 exact-head evidence v2 + +on: + push: + branches: + - fix/security-scan-exact-head + paths: + - .github/repairs/pr799-repair-v2.patch.gz.b64 + - .github/workflows/repair-pr-799-evidence-v2.yml + +permissions: + contents: write + +concurrency: + group: repair-pr-799-exact-head-evidence-v2 + cancel-in-progress: false + +jobs: + repair: + if: github.repository == 'ContextualWisdomLab/.github' + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - name: Checkout repair head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: fix/security-scan-exact-head + fetch-depth: 0 + persist-credentials: true + + - name: Apply bounded repair and remove bootstrap files + shell: bash + run: | + set -euo pipefail + base64 --decode .github/repairs/pr799-repair-v2.patch.gz.b64 | gzip --decompress > /tmp/pr799-repair.patch + git apply --check /tmp/pr799-repair.patch + git apply /tmp/pr799-repair.patch + rm -f .github/repairs/pr799-repair-v2.patch.gz.b64 + rm -f .github/workflows/repair-pr-799-evidence-v2.yml + rmdir .github/repairs 2>/dev/null || true + bash -n scripts/ci/run_opencode_review_model_pool.sh + git diff --check + + - name: Install trusted test toolchain + run: python3 -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt + + - name: Verify focused regressions + env: + PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" + run: | + python3 -m pytest -q \ + tests/test_required_workflow_queue_contract.py::test_security_scan_fails_closed_when_dependency_review_is_unavailable \ + tests/test_sandboxed_verify.py::test_main_reports_allowed_env_network_stderr_timeout_and_kept_sandbox \ + tests/test_opencode_model_pool_runner.py::test_fatal_provider_error_kills_hung_opencode_run_early + + - name: Verify complete Python evidence + env: + PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" + run: | + timeout --kill-after=30s 900 python3 -m coverage run -m pytest -q tests + python3 -m coverage report --show-missing --fail-under=100 + python3 -m interrogate scripts/ci --fail-under=100 + + - name: Commit verified repair + shell: bash + run: | + set -euo pipefail + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add --all + git diff --cached --check + git commit -m "fix(ci): reap fatal review process groups" + git push origin HEAD:fix/security-scan-exact-head From 9ad6b70160b1bfa3f31f619e7f93349e71e85958 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 15:10:47 +0900 Subject: [PATCH 24/25] chore(ci): remove temporary PR 799 repair workflow --- .../workflows/repair-pr-799-evidence-v2.yml | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 .github/workflows/repair-pr-799-evidence-v2.yml diff --git a/.github/workflows/repair-pr-799-evidence-v2.yml b/.github/workflows/repair-pr-799-evidence-v2.yml deleted file mode 100644 index 70c374758..000000000 --- a/.github/workflows/repair-pr-799-evidence-v2.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Repair PR 799 exact-head evidence v2 - -on: - push: - branches: - - fix/security-scan-exact-head - paths: - - .github/repairs/pr799-repair-v2.patch.gz.b64 - - .github/workflows/repair-pr-799-evidence-v2.yml - -permissions: - contents: write - -concurrency: - group: repair-pr-799-exact-head-evidence-v2 - cancel-in-progress: false - -jobs: - repair: - if: github.repository == 'ContextualWisdomLab/.github' - runs-on: ubuntu-latest - timeout-minutes: 25 - steps: - - name: Checkout repair head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: fix/security-scan-exact-head - fetch-depth: 0 - persist-credentials: true - - - name: Apply bounded repair and remove bootstrap files - shell: bash - run: | - set -euo pipefail - base64 --decode .github/repairs/pr799-repair-v2.patch.gz.b64 | gzip --decompress > /tmp/pr799-repair.patch - git apply --check /tmp/pr799-repair.patch - git apply /tmp/pr799-repair.patch - rm -f .github/repairs/pr799-repair-v2.patch.gz.b64 - rm -f .github/workflows/repair-pr-799-evidence-v2.yml - rmdir .github/repairs 2>/dev/null || true - bash -n scripts/ci/run_opencode_review_model_pool.sh - git diff --check - - - name: Install trusted test toolchain - run: python3 -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt - - - name: Verify focused regressions - env: - PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" - run: | - python3 -m pytest -q \ - tests/test_required_workflow_queue_contract.py::test_security_scan_fails_closed_when_dependency_review_is_unavailable \ - tests/test_sandboxed_verify.py::test_main_reports_allowed_env_network_stderr_timeout_and_kept_sandbox \ - tests/test_opencode_model_pool_runner.py::test_fatal_provider_error_kills_hung_opencode_run_early - - - name: Verify complete Python evidence - env: - PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1" - run: | - timeout --kill-after=30s 900 python3 -m coverage run -m pytest -q tests - python3 -m coverage report --show-missing --fail-under=100 - python3 -m interrogate scripts/ci --fail-under=100 - - - name: Commit verified repair - shell: bash - run: | - set -euo pipefail - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add --all - git diff --cached --check - git commit -m "fix(ci): reap fatal review process groups" - git push origin HEAD:fix/security-scan-exact-head From a6cd746e86c07452d03e0f5cef8e8d13c22f28c6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 15:11:38 +0900 Subject: [PATCH 25/25] chore(ci): remove encoded PR 799 repair payload --- .github/repairs/pr799-repair-v2.patch.gz.b64 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/repairs/pr799-repair-v2.patch.gz.b64 diff --git a/.github/repairs/pr799-repair-v2.patch.gz.b64 b/.github/repairs/pr799-repair-v2.patch.gz.b64 deleted file mode 100644 index 93c8ba01b..000000000 --- a/.github/repairs/pr799-repair-v2.patch.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file