Actions: `uses: $/…` self-repository references are not resolved to local reusable workflows or composite actions (false positives and downgraded severity)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 62/100
Direzione di ricerca
Start in actions/ql/lib/codeql/actions/ast/internal/Ast.qll: pathUsesParser(), UsesStepImpl.getCallee(), and ExternalJobImpl.getCallee() currently match ./ but not $/. Align $/ with ./ as in the suggested diff, then check DataFlowPrivate.qll viableCallable still joins on getResolvedPath(). Done when the two reproduction trees (uses: $/ vs uses: ./) produce the same CodeQL Actions query results.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Description of the false positive
The Actions extractor and libraries do not resolve GitHub's self-repository uses: $/… syntax to the local reusable workflow or composite action it references. CodeQL resolves the equivalent ./… reference correctly. Because the call edge is lost, CodeQL analyzes every $/-called reusable workflow and composite action as if it had no caller. That causes two problems:
- False positives. A reusable workflow whose only callers run on
schedule,workflow_dispatch,push, orreleaseis analyzed withworkflow_callas its trigger. That makes it a source foractions/untrusted-checkout/medium,actions/code-injection/medium, andactions/envvar-injection/medium. - Downgraded severity, which masks real findings. A
pull_request_targetcaller that passesgithub.event.pull_request.titleinto a$/reusable workflow or composite action is reported asactions/code-injection/mediumrather thanactions/code-injection/critical. The same code with./is reported as critical.
On a real repository (below), switching the uses: prefix between ./ and $/ with no other change moves CodeQL from 0 results to 8. With $/ now recommended for same-repository references, CodeQL's caller-aware Actions analysis is silently lost for those workflows.
Why projects are adopting $/
$/ is not cosmetic. Several GitHub-owned controls depend on it:
- SHA-pinning policy.
$/resolves at the exact running commit and counts as pinned for the "Require actions to be pinned to a full-length commit SHA" policy../local actions cannot satisfy that policy. - Actions dependency locking.
github/gh-actions-lock, the tooling for the dependency locking in GitHub's 2026 Actions security roadmap, treats$/as inherently pinned and rewrites./local actions to$/when onboarding a workflow. - zizmor. zizmor's
self-repositoryaudit flags every./same-repository reference and auto-fixes it to$/.
CodeQL already accepts $/ in one place: #22155 (for #22464) excludes $/ from actions/unpinned-tag. The call-resolution path in the AST was not updated, so every other Actions query still treats $/ as unresolved.
Root cause
At ce33a8a98cb2b35160a2f89bd464cc2fe04e742b (current main):
DataFlowPrivate.qll#L122:viableCallablejoinsUses.getCallee()togetResolvedPath(), which is a file path with no prefix or a./prefix.Ast.qll#L1393-L1395:repoUsesParser()is([^/]+)/([^/]+)/([^@]+)@(.+)andpathUsesParser()is\./(.+).ExternalJobImpl.getCallee()(Ast.qll#L1402-L1410):$/.github/workflows/x.ymldoes not match./%and has no@, sogetCallee()has no result and no call edge is created for job-level reusable workflows.UsesStepImpl.getCallee()(Ast.qll#L1371-L1375): this returns the literal$/.github/actions/x, which never equals.github/actions/xor./.github/actions/x, so step-level composite actions are not linked either.
Downstream, ReusableWorkflowImpl.getACaller() and CompositeActionImpl.getACallerStep() are empty. As a result:
JobImpl.getATriggerEvent()falls back toworkflow_call;EventImpl.isPrivileged()takes thenot exists(...getACaller())branch;- the
checkoutTriggers()sources inUntrustedCheckoutQuery.qllmatch onworkflow_call; ControlCheckscannot see caller-sideif:guards;- inputs lose interprocedural taint from the caller, so privileged, externally triggerable flows are not recognized as critical.
Minimal reproduction
CodeQL CLI 2.27.1 (bundle codeql-bundle-v2.27.1, codeql/actions-queries 0.6.36, codeql/actions-all 0.6.2). Each scenario is two identical trees that differ only in the uses: prefix (./ or $/).
codeql database create db-$V --language=actions --source-root=$V
codeql database analyze db-$V --format=sarif-latest --output=$V.sarif \
codeql/actions-queries:codeql-suites/actions-security-and-quality.qls
Scenario 1: false positives (caller runs only on workflow_dispatch and schedule)
.github/workflows/caller.yml (PREFIX is ./ or $/):
name: Caller
on:
workflow_dispatch:
schedule:
- cron: '0 9 * * 1'
permissions:
contents: read
jobs:
call:
uses: PREFIX.github/workflows/reusable.yml
.github/workflows/reusable.yml:
name: Reusable
on:
workflow_call:
permissions:
contents: read
jobs:
resolve:
runs-on: ubuntu-24.04
outputs:
digest: ${{ steps.resolve.outputs.digest }}
steps:
- id: resolve
run: bash "${RUNNER_TEMP}/resolve.sh"
use:
needs: resolve
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.resolve.outputs.digest }}
persist-credentials: false
- id: parse
run: bash "${RUNNER_TEMP}/parse.sh"
- run: echo '${{ steps.parse.outputs.labels }}'
- env:
TOOL_CACHE: ${{ runner.tool_cache }}
run: echo "RUNNER_TOOL_CACHE=${TOOL_CACHE}" >> "$GITHUB_ENV"
Scenario 2: downgraded severity (caller is pull_request_target with write permissions)
.github/workflows/caller.yml:
name: Caller
on: pull_request_target
permissions:
contents: write
pull-requests: write
jobs:
call:
uses: PREFIX.github/workflows/reusable.yml
with:
title: ${{ github.event.pull_request.title }}
step:
runs-on: ubuntu-24.04
steps:
- uses: PREFIX.github/actions/greet
with:
who: ${{ github.event.pull_request.title }}
.github/workflows/reusable.yml:
name: Reusable
on:
workflow_call:
inputs:
title:
type: string
required: true
jobs:
echo:
runs-on: ubuntu-24.04
steps:
- run: echo "${{ inputs.title }}"
.github/actions/greet/action.yml:
name: Greet
description: Echo a name
inputs:
who:
description: Name
required: true
runs:
using: composite
steps:
- run: echo "Hello ${{ inputs.who }}"
shell: bash
| Scenario | ./ |
$/ |
|---|---|---|
| 1: dispatch- or schedule-only caller | 0 results | actions/untrusted-checkout/medium reusable.yml:18; actions/code-injection/medium reusable.yml:24; actions/envvar-injection/medium reusable.yml:27 |
2: pull_request_target caller |
actions/code-injection/critical reusable.yml:12 and greet/action.yml:10 |
actions/code-injection/medium at the same two locations |
Code samples or links to source code
Real-world case: microsoft/hve-core#3138 migrates 57 job-level reusable-workflow calls and 34 step-level local actions from ./ to $/ to satisfy the SHA-pinning policy, dependency locking, and zizmor's self-repository audit. The tracking issue is microsoft/hve-core#3112.
- Caller (
schedule,workflow_dispatch):backlog-groom-orchestrator.yml#L683 - Callee results:
backlog-groom.lock.yml#L308(untrusted-checkout);#L587(envvar-injection);#L892-L896(code-injection ×3).
- Callers (
release,workflow_dispatch):release-marketplace-stable.yml#L143andrelease-marketplace-prerelease.yml#L145 - Callee results:
extension-marketplace-publish.yml#L126and#L189(untrusted-checkout), plus#L308(envvar-injection)
A/B at 35e26eac1 with the same CLI and the actions-security-extended and actions-security-and-quality suites:
- the tree as committed (
$/) gives 8 results; - the same tree with every
uses: $/changed touses: ./gives 0 results.
The default branch, which still uses ./, has none of these alerts.
URL to the alert on GitHub code scanning (optional)
- https://github.com/microsoft/hve-core/security/code-scanning/1023
- https://github.com/microsoft/hve-core/security/code-scanning/1024
- https://github.com/microsoft/hve-core/security/code-scanning/1025
- https://github.com/microsoft/hve-core/security/code-scanning/1026
- https://github.com/microsoft/hve-core/security/code-scanning/1027
Suggested fix (verified locally)
Treat $/ as a same-repository path, exactly like ./:
--- a/actions/ql/lib/codeql/actions/ast/internal/Ast.qll
+++ b/actions/ql/lib/codeql/actions/ast/internal/Ast.qll
@@ class UsesStepImpl
override string getCallee() {
- if u.getValue().indexOf("@") > 0
- then result = u.getValue().prefix(u.getValue().indexOf("@"))
- else result = u.getValue()
+ if u.getValue().matches("$/%")
+ then result = "./" + u.getValue().suffix(2)
+ else
+ if u.getValue().indexOf("@") > 0
+ then result = u.getValue().prefix(u.getValue().indexOf("@"))
+ else result = u.getValue()
}
@@
-private string pathUsesParser() { result = "\\./(.+)" }
+private string pathUsesParser() { result = "(?:\\.|\\$)/(.+)" }
@@ class ExternalJobImpl
override string getCallee() {
- if u.getValue().matches("./%")
+ if u.getValue().matches(["./%", "$/%"])
then result = u.getValue().regexpCapture(pathUsesParser(), 1)
With this patch applied to codeql/actions-all 0.6.2 (including the copy vendored in codeql/actions-queries 0.6.36), the $/ variants match the ./ variants exactly:
- Scenario 1: 3 results become 0.
- Scenario 2: medium becomes critical.
- microsoft/hve-core at
35e26eac1: 8 results become 0.
UsesStepImpl.getVersion() and ExternalJobImpl.getVersion() need no change: $/ references have no @. actions/unpinned-tag already skips $/ (#22155).
Possibly related: #21834 and #22263, which cover external callee resolution; this issue covers same-repository resolution only.
I'm happy to open a PR with the fix, library tests, and a change note if that would help.
- Lingua principale
- CodeQL
- Stelle
- 10.2k
- Fork
- 2.1k
- Merge medio
- 2g 13h
- PR unite (30g)
- 144
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di github/codeql
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
github/codeql#22766 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Python: trailing comma in a PEP 695 type parameter list causes a parse errorForse già presa @jketema l’ha presa 6 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
github/codeql#22739 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
false-positive javascript
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
github/codeql#22632 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Add AlertSuppression.ql for Rust (inline // codeql[...] suppression)Forse già presa @cnuss l’ha presa 189 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
github/codeql#21637 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
false-positive
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
github/codeql#21076 · 3 commenti · 3 reazioni ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di github/codeql
Issue simili
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 92/100
alunduil/projects-v2-sync#14 ·
-
`helios / deploy`: switch zone wait in `deploy.sh` has almost no headroom over healthy startup timesForse già presa Una pull request collegata a questa issue è aperta o già unita. ApertaTest Flake
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
oxidecomputer/omicron#11453 ·
I maintainer di solito rispondono entro 1 giorno
-
Website: the docs site's own service worker can't install (navigateFallback "/" isn't precached)Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
components-web-app/docs#173 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
-
ci: test on Node 26Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100