Actions: `uses: $/…` self-repository references are not resolved to local reusable workflows or composite actions (false positives and downgraded severity)
Les mainteneurs répondent en général sous 1 jour
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 62/100
Piste de recherche
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.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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.
- Langage dominant
- CodeQL
- Étoiles
- 10.2k
- Forks
- 2.1k
- Merge moyen
- 2 j 11 h
- PR mergées (30 j)
- 155
Préparer son environnement
Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de github/codeql
-
false-positive javascript
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
github/codeql#22632 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Add AlertSuppression.ql for Rust (inline // codeql[...] suppression)Peut-être pris @cnuss l’a pris il y a 191 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
github/codeql#21637 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
false-positive
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
github/codeql#21076 · 3 commentaires · 3 réactions ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 4/5 3-5 jours Accessibilité débutants 15/100
Les mainteneurs répondent en général sous 1 jour
-
Rust: extraction succeeds with missing proc-macro output when the project's `rust-version` exceeds the forced toolchainPeut-être pris @paldepind l’a pris il y a 1 jour. Ouverte
github/codeql#22793 · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de github/codeql
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 60/100
EchoTools/nevr-runtime#450 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
NuSkooler/enigma-bbs#907 ·
Les mainteneurs répondent en général sous 1 jour
-
enhancement good first issue
Difficulté 2/5 1-3 heures Accessibilité débutants 80/100
-
Update Python support to 3.15Ouvertepython-version
Difficulté 1/5 Moins d'une heure Accessibilité débutants 88/100
-
initramfs: -type f (#18686) skips the libcurl.so.4 symlink, libcurl no longer copied into initramfsOuverte
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 2 jours