Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1a25b12
test(security): require literal PR-head scanner checkout
seonghobae Aug 6, 2026
7d20fb7
ci(security): execute exact-head scanner contract
seonghobae Aug 6, 2026
e75172d
test(security): require literal-head SARIF attribution
seonghobae Aug 6, 2026
a7028f2
fix(security): scan and publish literal PR-head evidence
seonghobae Aug 6, 2026
115b2fc
ci(security): verify exact-head checkout and SARIF contracts
seonghobae Aug 6, 2026
fc37844
docs(security): record literal-head scanner evidence
seonghobae Aug 6, 2026
323c07b
docs: record exact-head security scanner repair
seonghobae Aug 6, 2026
682088b
test(security): prove unavailable dependency review fails closed
seonghobae Aug 7, 2026
2d1603f
test(security): execute dependency-review fail-closed contract
seonghobae Aug 7, 2026
7c0b6f9
fix(security): fail closed on unavailable dependency review
seonghobae Aug 7, 2026
3c1653a
docs(security): document fail-closed dependency review evidence
seonghobae Aug 7, 2026
5996f89
docs(security): record dependency-review fail-closed gate
seonghobae Aug 7, 2026
dfa2878
test(security): accept explicit unavailable-status diagnostic
seonghobae Aug 7, 2026
a5f39a4
docs(security): surface exact-head scanner operator contract
seonghobae Aug 7, 2026
acc0da8
test: require bounded body-discarding dependency probe
seonghobae Aug 7, 2026
c395c0f
chore(security): stage bounded dependency-review probe
seonghobae Aug 7, 2026
3ccb616
fix(security): bound dependency-review support probe
seonghobae Aug 7, 2026
e7690e3
chore(security): remove staged timeout patch artifact
seonghobae Aug 7, 2026
4dfde68
chore(ci): stage bounded PR 799 repair
seonghobae Aug 7, 2026
3a382eb
ci: bootstrap verified PR 799 repair
seonghobae Aug 7, 2026
3ff61fd
fix(ci): reap fatal review process groups
github-actions[bot] Aug 7, 2026
e4b7f0c
chore(stack): isolate exact-head scanner repair on current main
seonghobae Aug 7, 2026
667e9dd
chore(ci): restage bounded PR 799 repair
seonghobae Aug 7, 2026
6732b24
ci: rerun verified PR 799 repair
seonghobae Aug 7, 2026
9ad6b70
chore(ci): remove temporary PR 799 repair workflow
seonghobae Aug 7, 2026
a6cd746
chore(ci): remove encoded PR 799 repair payload
seonghobae Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/security-scan-exact-head-quality-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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"
- "tests/test_security_scan_sarif_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 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 checkout_contract
from tests import test_security_scan_sarif_exact_head as sarif_contract

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
46 changes: 25 additions & 21 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -257,9 +258,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
Expand All @@ -272,30 +275,23 @@ 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 --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" \
"${api_url}/repos/${REPOSITORY}/dependency-graph/compare/${BASE_SHA}...${HEAD_SHA}" \
|| true
)"
Comment thread
seonghobae marked this conversation as resolved.

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
Expand All @@ -311,9 +307,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
Expand Down Expand Up @@ -387,6 +385,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'
Expand All @@ -403,9 +403,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
Expand Down Expand Up @@ -461,6 +463,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'
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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/<number>/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 each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped.
- 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.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<number>/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
Expand Down
71 changes: 71 additions & 0 deletions docs/doctoring/security-scan-exact-head.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 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.

## 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:

```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 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 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.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
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, 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

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
Loading
Loading