Summary
The qodana job checks out the pull-request head and runs Qodana against pull-request-controlled files. The job grants several write permissions and passes the QODANA_TOKEN secret expression to the Qodana action.
A same-repository pull request can change build files, qodana.yaml, workflow-related inputs, and the scripts used by this job. The job then runs those pull-request-controlled inputs in a workflow that has write-capable GitHub credentials.
The current repository does not have a repository secret named QODANA_TOKEN. The workflow still defines a secret-bearing execution boundary because the expression is present and would expose a configured token to the PR-head Qodana execution. The confirmed current exposure is the excessive write permission on PR-controlled analysis. The secret exposure is conditional on a token being configured at repository or another applicable scope.
Evidence
The current default branch contains this execution path:
.github/workflows/ci.yml:669-686 defines the Qodana job for pull-request analysis.
.github/workflows/ci.yml:687-691 grants checks: write, pull-requests: write, and security-events: write, in addition to contents: read.
.github/workflows/ci.yml:693-699 checks out ${{ github.event.pull_request.head.sha || github.sha }}.
.github/workflows/ci.yml:701-711 runs JetBrains/qodana-action with upload-result: true and QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}.
.github/workflows/ci.yml:713-714 runs the repository-local SARIF normalisation script after the scan. That script is also selected from the checked-out revision.
.github/workflows/ci.yml:717-720 uploads the SARIF result with the job's security-events: write permission.
The Qodana job first appears in released workflow configuration at 0.15.2. The affected range is >= 0.15.2 until the workflow is remediated.
JetBrains documents that QODANA_TOKEN identifies a Qodana pipeline and is required for paid Qodana linters. JetBrains also documents that the Qodana action uses the GitHub token by default for annotations and pull-request comments. The current workflow grants those write permissions explicitly.
Impact
A contributor with same-repository pull-request write access can change the files that the Qodana job scans and the local script that normalises its result. The changed inputs run before a code-scanning upload while the job has write-capable GitHub credentials.
If QODANA_TOKEN is configured, the pull-request execution can also receive that secret through the workflow environment. A fork pull request normally receives restricted access, but that does not protect the same-repository case.
The current absence of the repository secret reduces the present impact. It does not make the workflow safe to use with a future Qodana token.
Immediate containment
Remove checks: write, pull-requests: write, and any other permission that Qodana does not strictly require from PR-head analysis. Remove the QODANA_TOKEN reference from the untrusted pull-request job until the trust boundary is redesigned.
Do not add the token to the repository while this job checks out pull-request code.
Required remediation
Choose one of these designs:
- Run a secretless Qodana scan on pull requests. Keep the token and result publication in a trusted workflow that runs default-branch code.
- Run a secret-bearing scan only in a protected environment that requires maintainer approval. Do not permit automatic execution for untrusted pull-request heads.
For either design:
- Never expose
QODANA_TOKEN to code or configuration from a pull-request head.
- Do not check out pull-request code in the trusted uploader.
- Pass only the minimum GitHub token permissions to each job.
- Review Qodana's required permissions for annotations, pull-request comments, and SARIF upload separately.
- Treat the local SARIF normalisation script as untrusted when it comes from the pull-request revision.
- Bind uploaded results to the exact repository, workflow run, pull request, head SHA, and attempt.
- Reject stale, malformed, unexpected, or duplicate result artifacts.
Acceptance criteria
- A same-repository pull request cannot access
QODANA_TOKEN.
- PR-head Qodana analysis runs without write-capable GitHub permissions, or it runs only after explicit protected-environment approval.
- The trusted uploader executes only default-branch implementation.
- Code-scanning results remain available for valid pull requests.
- Qodana annotations and pull-request comments continue to work through the smallest verified permission set.
- Tests or deterministic checks cover a modified Qodana configuration, modified build files, modified local normalisation script, same-repository pull request, fork pull request, rerun, stale head, and missing result cases.
References
No code, release, or workflow change is included in this report.
Summary
The
qodanajob checks out the pull-request head and runs Qodana against pull-request-controlled files. The job grants several write permissions and passes theQODANA_TOKENsecret expression to the Qodana action.A same-repository pull request can change build files,
qodana.yaml, workflow-related inputs, and the scripts used by this job. The job then runs those pull-request-controlled inputs in a workflow that has write-capable GitHub credentials.The current repository does not have a repository secret named
QODANA_TOKEN. The workflow still defines a secret-bearing execution boundary because the expression is present and would expose a configured token to the PR-head Qodana execution. The confirmed current exposure is the excessive write permission on PR-controlled analysis. The secret exposure is conditional on a token being configured at repository or another applicable scope.Evidence
The current default branch contains this execution path:
.github/workflows/ci.yml:669-686defines theQodanajob for pull-request analysis..github/workflows/ci.yml:687-691grantschecks: write,pull-requests: write, andsecurity-events: write, in addition tocontents: read..github/workflows/ci.yml:693-699checks out${{ github.event.pull_request.head.sha || github.sha }}..github/workflows/ci.yml:701-711runsJetBrains/qodana-actionwithupload-result: trueandQODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}..github/workflows/ci.yml:713-714runs the repository-local SARIF normalisation script after the scan. That script is also selected from the checked-out revision..github/workflows/ci.yml:717-720uploads the SARIF result with the job'ssecurity-events: writepermission.The Qodana job first appears in released workflow configuration at
0.15.2. The affected range is>= 0.15.2until the workflow is remediated.JetBrains documents that
QODANA_TOKENidentifies a Qodana pipeline and is required for paid Qodana linters. JetBrains also documents that the Qodana action uses the GitHub token by default for annotations and pull-request comments. The current workflow grants those write permissions explicitly.Impact
A contributor with same-repository pull-request write access can change the files that the Qodana job scans and the local script that normalises its result. The changed inputs run before a code-scanning upload while the job has write-capable GitHub credentials.
If
QODANA_TOKENis configured, the pull-request execution can also receive that secret through the workflow environment. A fork pull request normally receives restricted access, but that does not protect the same-repository case.The current absence of the repository secret reduces the present impact. It does not make the workflow safe to use with a future Qodana token.
Immediate containment
Remove
checks: write,pull-requests: write, and any other permission that Qodana does not strictly require from PR-head analysis. Remove theQODANA_TOKENreference from the untrusted pull-request job until the trust boundary is redesigned.Do not add the token to the repository while this job checks out pull-request code.
Required remediation
Choose one of these designs:
For either design:
QODANA_TOKENto code or configuration from a pull-request head.Acceptance criteria
QODANA_TOKEN.References
No code, release, or workflow change is included in this report.