Skip to content
Merged
53 changes: 38 additions & 15 deletions .github/workflows/codeql-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
# runner, then one coordinator POSTs repository_dispatch to
# codeql-scan-dispatch.yml (native, unrestricted, in
# ContextualWisdomLab/.github) with the remaining language matrix. The
# handler publishes codeql-dispatch/<language> and reruns only that exact
# failed job. On rerun the shard reads the terminal status once. Design:
# handler publishes a base/run/source-bound codeql-dispatch receipt and reruns
# only that exact failed job. On rerun the shard reads the terminal status once. Design:
# docs/adr/0025-codeql-required-workflow-dispatch-architecture.md. The
# merge-preview scan (analyze-merge) is required nowhere (PR #1766) and was
# dropped, not migrated.
Expand Down Expand Up @@ -164,7 +164,7 @@ jobs:
steps:
- name: Read current-head CodeQL dispatch verdict
# Shards never dispatch. They re-check the live head, consume an
# authenticated codeql-dispatch/<language> verdict when one exists,
# authenticated base/run/source-bound CodeQL verdict when one exists,
# and otherwise fail pending so the runner is released. One
# coordinator job POSTs the remaining language matrix after every
# shard has a job id.
Expand All @@ -183,6 +183,7 @@ jobs:
live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')"
live_base="$(printf '%s' "$live_pr" | jq -r '.base.sha // empty')"
live_merge="$(printf '%s' "$live_pr" | jq -r '.merge_commit_sha // empty')"
live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')"
if ! [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ && "$live_head" =~ ^[0-9a-fA-F]{40}$ ]] || [[ "$live_state" != "open" && "$live_state" != "closed" ]]; then
echo "::error::Could not validate live pull request state before CodeQL dispatch."
Expand All @@ -207,8 +208,9 @@ jobs:
echo "verdict=obsolete" >>"$GITHUB_OUTPUT"
exit 0
fi
if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "::error::Could not validate live pull request base SHA before CodeQL verdict read."
if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]] ||
! [[ "$live_merge" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "::error::Could not validate live pull request base/source SHA before CodeQL verdict read."
exit 1
fi
if ! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then
Expand All @@ -217,13 +219,17 @@ jobs:
fi

statuses="$(gh api "repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses")"
verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "codeql-dispatch/${LANGUAGE}" '
expected_context="codeql-dispatch/${LANGUAGE}/${live_base}"
expected_description="cwl1;h=${PR_HEAD_SHA};w=codeql-scan-dispatch;r=${REQUIRED_RUN_ID};s=${live_merge}"
verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "$expected_context" --arg description "$expected_description" '
[
.[]
| select(.context == $ctx)
| select(.description == $description)
| select(
(.creator.login // "" | ascii_downcase) as $creator
| $creator == "opencode-agent" or $creator == "opencode-agent[bot]"
or $creator == "cwl-noema-review" or $creator == "cwl-noema-review[bot]"
)
]
| first // {} | .state // empty
Expand All @@ -236,7 +242,7 @@ jobs:
;;
esac

expected_title="CodeQL Scan Dispatch ${TARGET_REPOSITORY}#${PR_NUMBER}@${PR_HEAD_SHA}/${live_base}/${REQUIRED_RUN_ID}"
expected_title="CodeQL Scan Dispatch ${TARGET_REPOSITORY}#${PR_NUMBER}@${PR_HEAD_SHA}/${live_base}/${REQUIRED_RUN_ID}/${live_merge}"
expected_job="CodeQL dispatch scan (${LANGUAGE})"
# A dispatch bound to this required run cannot predate its creation.
required_created_at="$(gh api "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}" --jq .created_at)"
Expand Down Expand Up @@ -266,11 +272,20 @@ jobs:
gate_conclusion="$(printf '%s' "$dispatch_job" | jq -r '
(.steps[]? | select(.name == "Enforce CodeQL Medium+ SARIF gate") | .conclusion) // empty
')"
ghas_identity_conclusion="$(printf '%s' "$dispatch_job" | jq -r '
(.steps[]? | select(.name == "Verify GHAS base/head CodeQL configuration identity") | .conclusion) // empty
')"
sarif_upload_conclusion="$(printf '%s' "$dispatch_job" | jq -r '
(.steps[]? | select(.name == "Preserve CodeQL SARIF evidence") | .conclusion) // empty
')"
case "$gate_conclusion" in
success)
echo "verdict=success" >>"$GITHUB_OUTPUT"
echo "Found completed CodeQL dispatch scan gate for ${LANGUAGE}: success."
exit 0
if [ "$ghas_identity_conclusion" = "success" ] &&
[ "$sarif_upload_conclusion" = "success" ]; then
echo "verdict=success" >>"$GITHUB_OUTPUT"
echo "Found completed CodeQL dispatch proof for ${LANGUAGE}: gate, GHAS identity, and SARIF evidence succeeded."
exit 0
fi
;;
failure|cancelled|skipped)
echo "verdict=failure" >>"$GITHUB_OUTPUT"
Expand All @@ -290,7 +305,7 @@ jobs:
fi

if [ "$RUN_ATTEMPT" != "1" ]; then
echo "::error::Exact CodeQL job was rerun without an authenticated terminal verdict."
echo "::error::Exact CodeQL job was rerun without an authenticated terminal verdict; GHAS identity and preserved SARIF are required for authenticated terminal proof."
exit 1
fi
echo "verdict=pending" >>"$GITHUB_OUTPUT"
Expand Down Expand Up @@ -363,6 +378,7 @@ jobs:
live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')"
live_base="$(printf '%s' "$live_pr" | jq -r '.base.sha // empty')"
live_merge="$(printf '%s' "$live_pr" | jq -r '.merge_commit_sha // empty')"
live_base_ref="$(printf '%s' "$live_pr" | jq -r '.base.ref // empty')"
live_head_ref="$(printf '%s' "$live_pr" | jq -r '.head.ref // empty')"
live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')"
Expand All @@ -382,8 +398,10 @@ jobs:
echo "::error::CodeQL dispatch requires a canonical current run id."
exit 1
fi
if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]] || [ -z "$live_base_ref" ] || [ -z "$live_head_ref" ]; then
echo "::error::Could not validate live pull request base identity before CodeQL dispatch."
if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]] ||
! [[ "$live_merge" =~ ^[0-9a-fA-F]{40}$ ]] ||
[ -z "$live_base_ref" ] || [ -z "$live_head_ref" ]; then
echo "::error::Could not validate live pull request base/source identity before CodeQL dispatch."
exit 1
fi

Expand Down Expand Up @@ -420,13 +438,17 @@ jobs:
pending_matrix='[]'
while IFS= read -r entry; do
language="$(printf '%s' "$entry" | jq -r '.language // empty')"
verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "codeql-dispatch/${language}" '
expected_context="codeql-dispatch/${language}/${live_base}"
expected_description="cwl1;h=${PR_HEAD_SHA};w=codeql-scan-dispatch;r=${REQUIRED_RUN_ID};s=${live_merge}"
verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "$expected_context" --arg description "$expected_description" '
[
.[]
| select(.context == $ctx)
| select(.description == $description)
| select(
(.creator.login // "" | ascii_downcase) as $creator
| $creator == "opencode-agent" or $creator == "opencode-agent[bot]"
or $creator == "cwl-noema-review" or $creator == "cwl-noema-review[bot]"
)
]
| first // {} | .state // empty
Expand Down Expand Up @@ -484,5 +506,6 @@ jobs:
--argjson matrix "$pending_matrix" \
--arg required_run_id "$REQUIRED_RUN_ID" \
--argjson required_jobs "$required_jobs" \
'{event_type:"codeql-scan",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head_ref:$pr_head_ref,pr_head_sha:$pr_head_sha,matrix:$matrix,required_run_id:$required_run_id,required_jobs:$required_jobs}}' |
--arg producer_source_sha "$live_merge" \
'{event_type:"codeql-scan-v2",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head:{schema:"1",ref:$pr_head_ref,sha:$pr_head_sha},producer_source_sha:$producer_source_sha,matrix:$matrix,required_run_id:$required_run_id,required_jobs:$required_jobs}}' |
GH_TOKEN="$app_token" gh api -X POST repos/ContextualWisdomLab/.github/dispatches --input -
75 changes: 72 additions & 3 deletions .github/workflows/codeql-scan-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ jobs:

- name: Detect optional Noema analysis-read credential
id: noema_analysis_config
if: steps.gate.outcome == 'success'
if: always() && steps.live_metadata.outcome == 'success'
env:
TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }}
NOEMA_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }}
Expand Down Expand Up @@ -701,10 +701,23 @@ jobs:
if-no-files-found: error
retention-days: 7

- name: Mint target-scoped Noema CodeQL status token
id: noema_status_token
if: always() && steps.noema_analysis_config.outputs.available == 'true'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
continue-on-error: true
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }}
owner: ContextualWisdomLab
repositories: ${{ steps.noema_analysis_config.outputs.repository }}
permission-statuses: write

- name: Publish CodeQL dispatch status
id: publish_status
if: always() && steps.live_metadata.outcome == 'success'
env:
NOEMA_STATUS_TOKEN: ${{ steps.noema_status_token.outputs.token || '' }}
TARGET_APP_STATUS_TOKEN: ${{ steps.target_app_token.outputs.token || '' }}
GITHUB_STATUS_READ_TOKEN: ${{ github.token }}
PR_REVIEW_MERGE_STATUS_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || '' }}
Expand Down Expand Up @@ -783,6 +796,11 @@ jobs:
actual_creator="$(jq -r '.creator.login // "" | ascii_downcase' "$status_response" 2>/dev/null || true)"
creator_trusted=false
case "$token_label" in
noema-status-token)
case "$actual_creator" in
cwl-noema-review|cwl-noema-review\[bot\]) creator_trusted=true ;;
esac
;;
target-app-token|pr-review-merge-token|opencode-approve-token)
case "$actual_creator" in
opencode-agent|opencode-agent\[bot\]) creator_trusted=true ;;
Expand Down Expand Up @@ -815,6 +833,9 @@ jobs:
return 1
}

if post_status "noema-status-token" "${NOEMA_STATUS_TOKEN:-}"; then
exit 0
fi
if post_status "target-app-token" "$TARGET_APP_STATUS_TOKEN"; then
exit 0
fi
Expand Down Expand Up @@ -920,8 +941,34 @@ jobs:
echo "token=$app_token"
} >>"$GITHUB_OUTPUT"

- name: Resolve Noema settlement token configuration
id: noema_settlement_config
env:
NOEMA_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }}
NOEMA_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }}
TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }}
run: |
set -euo pipefail
if [ -n "$NOEMA_APP_CLIENT_ID" ] && [ -n "$NOEMA_APP_PRIVATE_KEY" ]; then
printf 'repository=%s\n' "${TARGET_REPOSITORY#*/}" >>"$GITHUB_OUTPUT"
echo "available=true" >>"$GITHUB_OUTPUT"
fi

- name: Mint target-scoped Noema CodeQL settlement token
id: noema_settlement_token
if: steps.noema_settlement_config.outputs.available == 'true'
continue-on-error: true
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }}
owner: ContextualWisdomLab
repositories: ${{ steps.noema_settlement_config.outputs.repository }}
permission-actions: write

- name: Settle exact CodeQL required run
env:
NOEMA_WAKE_TOKEN: ${{ steps.noema_settlement_token.outputs.token || '' }}
TARGET_APP_WAKE_TOKEN: ${{ steps.target_app_token.outputs.token || '' }}
PR_REVIEW_MERGE_WAKE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || '' }}
OPENCODE_APPROVE_WAKE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || '' }}
Expand Down Expand Up @@ -960,7 +1007,8 @@ jobs:
}

github_api() {
run_api "target-app-token" "$TARGET_APP_WAKE_TOKEN" "$@" ||
run_api "noema-settlement-token" "${NOEMA_WAKE_TOKEN:-}" "$@" ||
run_api "target-app-token" "$TARGET_APP_WAKE_TOKEN" "$@" ||
run_api "pr-review-merge-token" "$PR_REVIEW_MERGE_WAKE_TOKEN" "$@" ||
run_api "opencode-approve-token" "$OPENCODE_APPROVE_WAKE_TOKEN" "$@" ||
run_api "github-token" "$GITHUB_WAKE_TOKEN" "$@"
Expand Down Expand Up @@ -1074,6 +1122,26 @@ jobs:
echo "::error::CodeQL settlement rejected incomplete handler gate or SARIF evidence for ${language}."
exit 1
fi
clean_gate_count="$(printf '%s' "$handler_jobs" | jq --arg name "$expected_job_name" --argjson attempt "$GITHUB_RUN_ATTEMPT" '
[.[] | select(
.name == $name
and .status == "completed"
and .run_attempt == $attempt
and ([.steps[]? | select(.name == "Enforce CodeQL Medium+ SARIF gate" and .conclusion == "success")] | length) == 1
)] | length
')"
ghas_identity_count="$(printf '%s' "$handler_jobs" | jq --arg name "$expected_job_name" --argjson attempt "$GITHUB_RUN_ATTEMPT" '
[.[] | select(
.name == $name
and .status == "completed"
and .run_attempt == $attempt
and ([.steps[]? | select(.name == "Verify GHAS base/head CodeQL configuration identity" and .conclusion == "success")] | length) == 1
)] | length
')"
if [ "$clean_gate_count" -eq 1 ] && [ "$ghas_identity_count" -ne 1 ]; then
echo "::error::CodeQL settlement rejected missing GHAS configuration identity proof for ${language}."
exit 1
fi
done < <(printf '%s' "$REQUIRED_JOBS" | jq -c '.[]')

case "$RERUN_MODE" in
Expand All @@ -1099,7 +1167,8 @@ jobs:
return 1
}

if post_wake "target-app-token" "$TARGET_APP_WAKE_TOKEN" ||
if post_wake "noema-settlement-token" "${NOEMA_WAKE_TOKEN:-}" ||
post_wake "target-app-token" "$TARGET_APP_WAKE_TOKEN" ||
post_wake "pr-review-merge-token" "$PR_REVIEW_MERGE_WAKE_TOKEN" ||
post_wake "opencode-approve-token" "$OPENCODE_APPROVE_WAKE_TOKEN" ||
post_wake "github-token" "$GITHUB_WAKE_TOKEN"; then
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.d/20260927-codeql-terminal-proof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Fixed

- Require a successful GHAS base/head configuration-identity proof and preserved
SARIF before a clean central CodeQL gate may settle or satisfy an exact required
run. A failed post-gate identity check can no longer be promoted to GREEN by a
wake-only fallback.
- Bind CodeQL terminal receipts to the live base, required run, head, and merge
source through the v2 dispatch protocol, preventing a trusted but stale commit
status from satisfying a retargeted or later required run.
Loading
Loading