Skip to content

ci: switch CodeQL to advanced setup with JS/TS, same-repo and fork PR scanning#99

Merged
ricogu merged 4 commits into
mainfrom
ci/codeql-advanced-fork-pr-scanning
May 23, 2026
Merged

ci: switch CodeQL to advanced setup with JS/TS, same-repo and fork PR scanning#99
ricogu merged 4 commits into
mainfrom
ci/codeql-advanced-fork-pr-scanning

Conversation

@ricogu

@ricogu ricogu commented May 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Switch CodeQL from default setup to advanced setup so that:

  1. TypeScript / JavaScript sources are also analyzed (currently only actions is scanned).
  2. Both same-repo branch PRs and fork PRs produce a CodeQL analysis on their head commit, satisfying the code_scanning rule in ruleset 10774671 and unblocking PRs like fix: use commit-message input #98 that are currently mergeable_state: BLOCKED solely because no analysis exists for the head SHA.

Motivation

/code-scanning/default-setup is configured for ["actions"] only. The repo ruleset on main requires:

  • code_scanning (CodeQL, severity ≥ high, quality alerts ≥ errors)
  • code_quality (severity errors)
  • copilot_code_review

For a PR from a fork (e.g., #98 from Herrtian/pull-request-semver-bumper), default setup never analyzes the PR head, so the rule cannot be satisfied and the PR stays blocked even with green CI and an approving review.

Changes

Added

  • .github/workflows/codeql.yml — CodeQL Advanced workflow.
    • Triggers: push to main, pull_request on main, pull_request_target on main, weekly schedule (26 1 * * 3).
    • Job-level if ensures each PR is analyzed exactly once:
      • Same-repo PRs → pull_request only.
      • Fork PRs → pull_request_target only.
      • Push and schedule → always.
    • Matrix: actions (build-mode: none), javascript-typescript (build-mode: none).
    • Query suite: security-and-quality so quality alerts feed the code_quality rule.
    • actions/checkout uses the PR head ref/repo for any pull_request[_target] event.
    • CodeQL configuration is provided inline via the config: input (see Security section).

Why the dual pull_request + pull_request_target trigger

Source of the PR Event chosen by if: Workflow file used Can upload SARIF?
Branch in SAP/pull-request-semver-bumper pull_request The PR's version (lets us iterate on the workflow inside a PR) Yes (write token available)
Fork (e.g. Herrtian/...) pull_request_target main's version (fork can't tamper) Yes (base-repo token, after disabling default setup)

Without splitting, a single pull_request_target-only setup would force every workflow change to be merged before being testable; a single pull_request-only setup would silently skip fork PRs (no SARIF write permission for fork tokens). The split gives both audiences the right behavior with no duplicate runs.

Security model for fork-PR scanning

pull_request_target runs in the base-repo context with a token that has security-events: write. That power needs careful containment. The defenses in this workflow:

  1. Fork can't tamper with the workflowpull_request_target reads the workflow file from main. The fork's .github/workflows/codeql.yml is ignored.
  2. Fork can't tamper with the CodeQL config — the configuration is provided inline via init's config: input rather than config-file:. A config-file: path resolves against the working directory, which contains the fork's checkout, so a malicious fork could ship disable-default-queries: true + broad paths-ignore and neuter the gate. Inline config is read from the trusted workflow file from main.
  3. Token isn't exposed to checked-out codeactions/checkout uses persist-credentials: false, so the base-repo GITHUB_TOKEN is not written to .git/config. Fork code can't git push back.
  4. No fork code is executedbuild-mode: none for both languages. CodeQL extracts directly from source. No npm install, no actions/setup-node, no run: step that touches fork content.
  5. Permissions are scopedcontents: read, security-events: write, pull-requests: read. No contents: write, no id-token: write.

This follows GitHub's documented pattern for fork-PR CodeQL coverage.

Manual steps required after merge

CodeQL default setup must be disabled before the advanced workflow can upload SARIF, otherwise analyze fails with "Code scanning is configured with default setup".

After this PR merges, an admin must run once:

gh api -X PATCH repos/SAP/pull-request-semver-bumper/code-scanning/default-setup \
  -f state=not-configured

Or via UI: Settings → Code security → CodeQL analysis → switch off default setup.

The next push to main (or any PR) will then run the advanced workflow.

Compatibility & impact

Breaking changes

  • None for consumers of the action.
  • Internal: default CodeQL setup must be disabled (see above).

CI impact

  • Adds two CodeQL jobs per push/PR. Each is build-mode: none and small in scope, so wall-clock impact is minor (~1-2 min).
  • Same-repo and fork PRs both gain JS/TS analysis that wasn't happening before.

Dependencies

  • New workflow uses actions/checkout@v4, github/codeql-action/init@v4, github/codeql-action/analyze@v4 (current major versions).

Verification

After merge + disabling default setup, verify both languages produce analyses:

gh api 'repos/SAP/pull-request-semver-bumper/code-scanning/analyses?per_page=10' \
  --jq '[.[] | {ref,commit_sha,tool:.tool.name,category}]'

Expected: entries for /language:actions and /language:javascript-typescript.

For PRs (same-repo or fork), after the workflow runs:

gh api 'repos/SAP/pull-request-semver-bumper/code-scanning/analyses?ref=refs/pull/<n>/head' \
  --jq '[.[] | {commit_sha,category}]'

Should return the head SHA, and the PR's mergeable_state should leave BLOCKED.

Checklist

  • Workflow handles both same-repo and fork PRs without duplicate runs
  • Workflow follows GitHub's documented pattern for fork-PR CodeQL via pull_request_target
  • persist-credentials: false to avoid leaking base-repo token to checked-out code
  • No build steps that execute untrusted fork code
  • CodeQL config is inline (not file-based) so a fork can't override paths-ignore / disable-default-queries to bypass the gate
  • Path filter excludes generated bundles to avoid noise/duplicate alerts
  • No new external dependencies beyond github/codeql-action
  • Default CodeQL setup disabled (manual, post-merge)

ricogu added 2 commits May 23, 2026 21:58
- Replaces default setup to also analyze javascript-typescript sources
  (currently only `actions` is scanned).
- Uses pull_request_target with explicit PR head checkout and
  persist-credentials: false so fork PRs are scanned safely without
  exposing the base-repo token to fork code.
- build-mode: none for both languages, so no fork code is executed.
- Adds .github/codeql/codeql-config.yml to skip generated bundles
  (dist/), node_modules, lockfiles, and test fixtures to keep alerts
  signal-heavy.

Default CodeQL setup must be disabled before this can upload SARIF;
see PR description for the API call.
- Add `pull_request` trigger so PRs from branches within
  SAP/pull-request-semver-bumper are analyzed using their own
  workflow version (lets us iterate on the workflow inside a PR).
- Keep `pull_request_target` trigger for fork PRs so SARIF can be
  uploaded under the base repo for the fork's head SHA.
- Add a job-level `if` so each PR is analyzed exactly once:
  same-repo via pull_request, forks via pull_request_target.
- Push and schedule triggers are unchanged.
@ricogu ricogu changed the title ci: switch CodeQL to advanced setup with JS/TS and fork PR scanning ci: switch CodeQL to advanced setup with JS/TS, same-repo and fork PR scanning May 23, 2026
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

ricogu added 2 commits May 23, 2026 22:07
Previously the workflow used `config-file: ./.github/codeql/codeql-config.yml`
which, on `pull_request_target` for fork PRs, resolved to the fork's
version of that file because actions/checkout had already overlaid the
fork's source onto the working directory.

A malicious fork could submit a PR carrying a config like:

  disable-default-queries: true
  paths-ignore:
    - "**/*"

This would make CodeQL upload an empty SARIF, satisfy the `code_scanning`
ruleset rule, and let the fork merge unscanned code.

Switch to the `config:` (inline YAML) input on codeql-action/init, which
takes precedence over `config-file` and is read from the workflow file
itself \u2014 always the trusted version on main when triggered by
pull_request_target.
The CodeQL configuration is now provided inline in the workflow's
`config:` input (see prior commit). Keeping the file would create
two sources of truth and \u2014 worse \u2014 leave a fork-controlled file
on disk that future contributors might wire up via `config-file:`
again, reintroducing the gate-bypass vulnerability.
@ricogu ricogu merged commit c7e6a91 into main May 23, 2026
11 checks passed
@ricogu ricogu deleted the ci/codeql-advanced-fork-pr-scanning branch May 23, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants