Repository navigation
[actions] Alternate actions/checkout paths suppress untrusted-checkout alerts #22213
Description
Description of the issue
actions/untrusted-checkout/critical does not report a privileged workflow that checks out an untrusted PR into a non-default path: and then executes a script or local action from that checkout. The actions/untrusted-checkout/high fallback is also suppressed, so neither query reports the workflow.
Minimal reproducer
on: pull_request_target
permissions:
contents: write
jobs:
execute:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
path: candidate
- run: bash candidate/proof.shactions/checkout places path: candidate under $GITHUB_WORKSPACE, so this executes the same checked-out script relationship as a default checkout followed by bash proof.sh.
Reproduction
The complete reproducer is in public fork PR #1.
- Latest-release fork CI: CodeQL Action 4.37.1, CLI 2.26.1,
codeql/actions-queries0.6.31 - Current-main CI: queries compiled directly from
github/codeql@14450f5bf38ea9a3ce2e0e45dcf51c0bbdd01af5
Both successful runs scanned all 11 workflow files and produced the same result matrix:
| Case | Result |
|---|---|
Default checkout followed by bash proof.sh |
actions/untrusted-checkout/critical |
Default checkout followed by uses: ./.github/actions/proof |
actions/untrusted-checkout/critical |
Default checkout with quoted or $GITHUB_WORKSPACE script paths |
actions/untrusted-checkout/critical |
path: candidate, ./candidate, or candidate/ followed by the corresponding script |
no critical or high alert |
path: candidate followed by uses: ./candidate/.github/actions/proof |
no critical or high alert |
| Immutable checkout control | no critical or high alert |
The CI also executed harmless script and composite-action canaries through the tested paths before analysis.
Expected result
The alternate-path cases should be reported by actions/untrusted-checkout/critical, like their default-path controls. A path-representation mismatch should not suppress both the critical query and its high fallback.
Source-level cause
At current main:
ActionsMutableRefCheckout.getPath()andActionsSHACheckout.getPath()return explicit checkout paths verbatim.LocalScriptExecutionRunStep.getPath()and local-action paths are normalized intoGITHUB_WORKSPACE/...form.- The critical query compares those representations with
isSubpath, while the high query suppresses its fallback because the following step is still aPoisonableStep.
Normalizing explicit actions/checkout paths at the checkout model, with regression tests for bare, dot-relative, trailing-slash, and local-action forms, appears to be the smallest fix.
Activity
oscarsj commentedon Jul 20, 2026
Thanks for the detailed report and reproducer — this is very helpful.
You’re right that the
path:checkout variant underpull_request_targetshould still be considered in scope for untrusted-checkout detection when later steps execute scripts or local actions from that checkout. Based on your analysis, this looks like a path-normalization mismatch between checkout modeling and poisonable-step path modeling, which can suppress bothcriticaland thehighfallback.We’ve triaged this as a likely query-modeling gap and will investigate a fix in the checkout/path normalization logic, along with regression coverage for alternate path forms (bare,
./, trailing slash, and local-action references).To help us validate edge cases and prevent regressions, could you share:
ubuntu-latestvs Windows/macOS) or shell (bash,sh,pwsh).path: ./candidate,path: candidate/, nested paths likepath: a/b, or quoted run commands).workflow_runor other privileged trigger patterns (if you tested those).Thanks again — the minimal reproducer and source-level pointers already provide a strong starting point.
1sgtpepper commentedon Jul 21, 2026
Thanks, Oscar. I added the requested coverage in PR #2.
Bash/sh alternate paths remain undetected across Ubuntu and macOS, including nested paths and
workflow_run. Pwsh reports high rather than critical. Both CI runs include SARIF and JSON artifacts: released queries and current main.