From da8c28d0c51adadd7441ba991d0b521dc8dce614 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 5 Jul 2026 01:01:23 +0900 Subject: [PATCH 1/4] feat(ci): add central CodeQL PR workflow for org code_scanning gate Upload PR-head and merge-preview CodeQL SARIF from ContextualWisdomLab/.github so ruleset code_scanning(CodeQL) is satisfied on develop/main/master PRs. Also widen osv-scanner-pr and scorecard-pr triggers to Git Flow bases. --- .github/workflows/codeql-pr.yml | 100 ++++++++++++++++++++++ .github/workflows/osv-scanner-pr.yml | 2 +- .github/workflows/scorecard-pr.yml | 2 +- docs/org-required-workflow-rollout.md | 18 ++++ tests/test_codeql_pr_workflow_contract.py | 22 +++++ 5 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/codeql-pr.yml create mode 100644 tests/test_codeql_pr_workflow_contract.py diff --git a/.github/workflows/codeql-pr.yml b/.github/workflows/codeql-pr.yml new file mode 100644 index 00000000..ded48385 --- /dev/null +++ b/.github/workflows/codeql-pr.yml @@ -0,0 +1,100 @@ +# Uploads CodeQL code scanning analyses on every PR so the org ruleset +# "CWL Central required workflows" -> code_scanning(CodeQL) can evaluate +# mergeability. Without PR-head and merge-preview SARIF on merge_commit_sha, +# approved PRs stay mergeStateStatus=BLOCKED with +# "Code scanning is waiting for results from CodeQL". +name: CodeQL PR + +on: + pull_request: + branches: [main, master, develop] + +permissions: + contents: read + +jobs: + analyze-head: + name: CodeQL compatibility analysis (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha }} + + - name: Initialize CodeQL + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + category: "/language:${{ matrix.language }}" + upload: always + ref: ${{ format('refs/pull/{0}/head', github.event.pull_request.number) }} + sha: ${{ github.event.pull_request.head.sha }} + + analyze-merge: + name: CodeQL merge preview (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - name: Checkout merge preview + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ format('refs/pull/{0}/merge', github.event.pull_request.number) }} + + - name: Initialize CodeQL + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + category: "/language:${{ matrix.language }}-merge" + upload: always + ref: ${{ format('refs/pull/{0}/merge', github.event.pull_request.number) }} + sha: ${{ github.event.pull_request.merge_commit_sha }} \ No newline at end of file diff --git a/.github/workflows/osv-scanner-pr.yml b/.github/workflows/osv-scanner-pr.yml index 00d2b3d5..b3c8a58b 100644 --- a/.github/workflows/osv-scanner-pr.yml +++ b/.github/workflows/osv-scanner-pr.yml @@ -6,7 +6,7 @@ name: OSV-Scanner PR on: pull_request: - branches: [main] + branches: [main, master, develop] permissions: # Upload SARIF to Security > Code Scanning. See github/codeql-action#2117. diff --git a/.github/workflows/scorecard-pr.yml b/.github/workflows/scorecard-pr.yml index 15771dc4..f5436bfa 100644 --- a/.github/workflows/scorecard-pr.yml +++ b/.github/workflows/scorecard-pr.yml @@ -13,7 +13,7 @@ name: Scorecard PR on: pull_request: - branches: [main] + branches: [main, master, develop] permissions: contents: read diff --git a/docs/org-required-workflow-rollout.md b/docs/org-required-workflow-rollout.md index 1f1a48df..b7c5f156 100644 --- a/docs/org-required-workflow-rollout.md +++ b/docs/org-required-workflow-rollout.md @@ -16,6 +16,9 @@ Use an organization repository ruleset instead of copying workflow files into ea - `.github/workflows/strix.yml` - `.github/workflows/opencode-review.yml` - `.github/workflows/pr-review-merge-scheduler.yml` + - `.github/workflows/osv-scanner-pr.yml` + - `.github/workflows/scorecard-pr.yml` + - `.github/workflows/codeql-pr.yml` - Required workflow ref: `refs/heads/main` - Last verified workflow implementation base commit: `ef9950e6b55bf943c0295e1df3e34c94210d21cc` (`#283`) - Required workflow trigger support: `pull_request_target`, `push`, `workflow_run` @@ -44,6 +47,21 @@ The central `.github/workflows/opencode-review.yml` is now part of the active or Keep the OpenCode required workflow active only while the central workflow keeps proving current-head coverage, CodeGraph initialization, bounded evidence, model review output, and approval-gate publication on the current head. +## Code scanning required workflow posture + +The central `.github/workflows/codeql-pr.yml`, `.github/workflows/scorecard-pr.yml`, +and `.github/workflows/osv-scanner-pr.yml` workflows supply PR-head and merge-preview +code scanning analyses for ruleset `18156473` `code_scanning` (CodeQL, Scorecard, +osv-scanner). They trigger on pull requests to `main`, `master`, and `develop` so +Git Flow repositories on `develop` inherit the same merge gate as GitHub Flow repos. + +CodeQL merge preview checks out `refs/pull//merge` and uploads SARIF with +`sha: pull_request.merge_commit_sha` because the ruleset evaluates that commit, +not the ephemeral merge ref OID. + +Repository-local `codeql.yml` push/default-branch scans may remain for branch +history, but PR merge gates should rely on the central `codeql-pr.yml` workflow. + ## Scheduler required workflow posture The central `.github/workflows/pr-review-merge-scheduler.yml` is now part of the active organization required workflow ruleset. diff --git a/tests/test_codeql_pr_workflow_contract.py b/tests/test_codeql_pr_workflow_contract.py new file mode 100644 index 00000000..53f9bc03 --- /dev/null +++ b/tests/test_codeql_pr_workflow_contract.py @@ -0,0 +1,22 @@ +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def test_codeql_pr_workflow_uploads_head_and_merge_sarif_for_ruleset_gate() -> None: + workflow = (REPO_ROOT / ".github/workflows/codeql-pr.yml").read_text( + encoding="utf-8" + ) + + assert "name: CodeQL PR" in workflow + assert "branches: [main, master, develop]" in workflow + assert "upload: always" in workflow + assert "analyze-head:" in workflow + assert "analyze-merge:" in workflow + assert "CodeQL merge preview" in workflow + assert "github.event.pull_request.head.sha" in workflow + assert "github.event.pull_request.merge_commit_sha" in workflow + assert "refs/pull/{0}/head" in workflow + assert "refs/pull/{0}/merge" in workflow + assert "security-events: write" in workflow \ No newline at end of file From cde9b80780ffb6d1ee38338a3b1924aa48a0e641 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 5 Jul 2026 01:03:29 +0900 Subject: [PATCH 2/4] fix(ci): make central CodeQL PR workflow repo-aware Detect languages from the PR head so repositories without JavaScript or TypeScript do not fail CodeQL, and skip merge-preview uploads until merge_commit_sha is available for ruleset evaluation. --- .github/workflows/codeql-pr.yml | 52 ++++++++++++++++------- tests/test_codeql_pr_workflow_contract.py | 2 + 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/.github/workflows/codeql-pr.yml b/.github/workflows/codeql-pr.yml index ded48385..99e3143c 100644 --- a/.github/workflows/codeql-pr.yml +++ b/.github/workflows/codeql-pr.yml @@ -13,8 +13,40 @@ permissions: contents: read jobs: + detect-languages: + name: Detect CodeQL languages + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.detect.outputs.matrix }} + steps: + - name: Checkout PR head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha }} + + - name: Build language matrix + id: detect + run: | + matrix='[]' + if [ -d .github/workflows ]; then + matrix=$(echo "$matrix" | jq '. + [{"language":"actions","build-mode":"none"}]') + fi + if find . -type f \( -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \) \ + -not -path './.git/*' | head -1 | grep -q .; then + matrix=$(echo "$matrix" | jq '. + [{"language":"javascript-typescript","build-mode":"none"}]') + fi + if find . -type f -name '*.py' -not -path './.git/*' | head -1 | grep -q .; then + matrix=$(echo "$matrix" | jq '. + [{"language":"python","build-mode":"none"}]') + fi + if [ "$(echo "$matrix" | jq 'length')" -eq 0 ]; then + matrix='[{"language":"actions","build-mode":"none"}]' + fi + echo "matrix={\"include\":${matrix}}" >> "$GITHUB_OUTPUT" + analyze-head: name: CodeQL compatibility analysis (${{ matrix.language }}) + needs: detect-languages runs-on: ubuntu-latest permissions: actions: read @@ -22,14 +54,7 @@ jobs: security-events: write strategy: fail-fast: false - matrix: - include: - - language: actions - build-mode: none - - language: javascript-typescript - build-mode: none - - language: python - build-mode: none + matrix: ${{ fromJSON(needs.detect-languages.outputs.matrix) }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 @@ -58,6 +83,8 @@ jobs: analyze-merge: name: CodeQL merge preview (${{ matrix.language }}) + needs: detect-languages + if: github.event.pull_request.merge_commit_sha != '' runs-on: ubuntu-latest permissions: actions: read @@ -65,14 +92,7 @@ jobs: security-events: write strategy: fail-fast: false - matrix: - include: - - language: actions - build-mode: none - - language: javascript-typescript - build-mode: none - - language: python - build-mode: none + matrix: ${{ fromJSON(needs.detect-languages.outputs.matrix) }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 diff --git a/tests/test_codeql_pr_workflow_contract.py b/tests/test_codeql_pr_workflow_contract.py index 53f9bc03..5833691d 100644 --- a/tests/test_codeql_pr_workflow_contract.py +++ b/tests/test_codeql_pr_workflow_contract.py @@ -12,8 +12,10 @@ def test_codeql_pr_workflow_uploads_head_and_merge_sarif_for_ruleset_gate() -> N assert "name: CodeQL PR" in workflow assert "branches: [main, master, develop]" in workflow assert "upload: always" in workflow + assert "detect-languages:" in workflow assert "analyze-head:" in workflow assert "analyze-merge:" in workflow + assert "merge_commit_sha != ''" in workflow assert "CodeQL merge preview" in workflow assert "github.event.pull_request.head.sha" in workflow assert "github.event.pull_request.merge_commit_sha" in workflow From a4b0242287f36688f70d21081f73cf525578ec51 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 5 Jul 2026 01:05:27 +0900 Subject: [PATCH 3/4] fix(ci): emit CodeQL matrix via multiline GITHUB_OUTPUT --- .github/workflows/codeql-pr.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-pr.yml b/.github/workflows/codeql-pr.yml index 99e3143c..85145296 100644 --- a/.github/workflows/codeql-pr.yml +++ b/.github/workflows/codeql-pr.yml @@ -30,19 +30,23 @@ jobs: run: | matrix='[]' if [ -d .github/workflows ]; then - matrix=$(echo "$matrix" | jq '. + [{"language":"actions","build-mode":"none"}]') + matrix=$(echo "$matrix" | jq -c '. + [{"language":"actions","build-mode":"none"}]') fi if find . -type f \( -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \) \ -not -path './.git/*' | head -1 | grep -q .; then - matrix=$(echo "$matrix" | jq '. + [{"language":"javascript-typescript","build-mode":"none"}]') + matrix=$(echo "$matrix" | jq -c '. + [{"language":"javascript-typescript","build-mode":"none"}]') fi if find . -type f -name '*.py' -not -path './.git/*' | head -1 | grep -q .; then - matrix=$(echo "$matrix" | jq '. + [{"language":"python","build-mode":"none"}]') + matrix=$(echo "$matrix" | jq -c '. + [{"language":"python","build-mode":"none"}]') fi if [ "$(echo "$matrix" | jq 'length')" -eq 0 ]; then matrix='[{"language":"actions","build-mode":"none"}]' fi - echo "matrix={\"include\":${matrix}}" >> "$GITHUB_OUTPUT" + { + echo 'matrix<> "$GITHUB_OUTPUT" analyze-head: name: CodeQL compatibility analysis (${{ matrix.language }}) From 82f0384dbdef4c0a6bb4996fcbf0d0f88451ec1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 5 Jul 2026 02:12:17 +0900 Subject: [PATCH 4/4] docs(ci): inventory repo-local CodeQL vs central codeql-pr gate --- docs/org-required-workflow-rollout.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/org-required-workflow-rollout.md b/docs/org-required-workflow-rollout.md index b7c5f156..4d910e26 100644 --- a/docs/org-required-workflow-rollout.md +++ b/docs/org-required-workflow-rollout.md @@ -62,6 +62,29 @@ not the ephemeral merge ref OID. Repository-local `codeql.yml` push/default-branch scans may remain for branch history, but PR merge gates should rely on the central `codeql-pr.yml` workflow. +### Repository-local CodeQL inventory (2026-07-04) + +Org audit of default-branch workflow files. Repos without any local CodeQL +workflow depend entirely on central `codeql-pr.yml` once ruleset `18156473` +includes that path; they are the most exposed to +`Code scanning is waiting for results from CodeQL` until the ruleset update +lands. + +| Repository | Default branch | Local CodeQL workflow | PR trigger | merge_commit_sha SARIF | +| --- | --- | --- | ---: | ---: | +| `aFIPC` | `master` | `codeql.yml` | yes | no | +| `bandscope` | `develop` | `codeql.yml` | yes | no | +| `newsdom-api` | `develop` | `codeql.yml` | yes | no | +| `pg-erd-cloud` | `main` | `codeql.yml`, `codeql-backfill.yml` | yes (`codeql.yml`) | no | +| `xtrmLLMBatchPython` | `develop` | `codeql.yml` | yes | no | +| `naruon` | `develop` | `codeql.yml` | yes (temporary; PR `#916` retires PR trigger) | yes (repo-local interim fix) | +| all other public non-fork org repos | varies | none observed | — | — | + +No repository-local PR CodeQL workflow besides `naruon` uploads merge-preview +SARIF on `merge_commit_sha`. Centralizing through `codeql-pr.yml` fixes every +inherited repository in one ruleset change; per-repo deletion of PR triggers is +optional cleanup to avoid duplicate scans. + ## Scheduler required workflow posture The central `.github/workflows/pr-review-merge-scheduler.yml` is now part of the active organization required workflow ruleset.