Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

Actions: `uses: $/…` self-repository references are not resolved to local reusable workflows or composite actions (false positives and downgraded severity)

Aberta
#22,755 1 comentário 1 reação 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

Ninguém assumiu esta issue ainda.

Avaliação

Dificuldade
3/5
Tempo estimado
1-2 dias
Facilidade para iniciantes
62/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Ativa
Stack de tecnologia
github-actions, yaml
Domínio
ci-cd, security, tooling

Direção de pesquisa

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.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

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:

  1. False positives. A reusable workflow whose only callers run on schedule, workflow_dispatch, push, or release is analyzed with workflow_call as its trigger. That makes it a source for actions/untrusted-checkout/medium, actions/code-injection/medium, and actions/envvar-injection/medium.
  2. Downgraded severity, which masks real findings. A pull_request_target caller that passes github.event.pull_request.title into a $/ reusable workflow or composite action is reported as actions/code-injection/medium rather than actions/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:

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):

Downstream, ReusableWorkflowImpl.getACaller() and CompositeActionImpl.getACallerStep() are empty. As a result:

  • JobImpl.getATriggerEvent() falls back to workflow_call;
  • EventImpl.isPrivileged() takes the not exists(...getACaller()) branch;
  • the checkoutTriggers() sources in UntrustedCheckoutQuery.qll match on workflow_call;
  • ControlChecks cannot see caller-side if: 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.

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 to uses: ./ gives 0 results.

The default branch, which still uses ./, has none of these alerts.

URL to the alert on GitHub code scanning (optional)

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.

Linguagem predominante
CodeQL
Estrelas
10.2k
Forks
2.1k
Merge médio
2d 13h
PRs com merge (30d)
144

Preparar o ambiente

Abrir no Codespaces

Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de github/codeql

Todas as issues de github/codeql

Issues semelhantes

Mais issues de DevOps

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.