fix(ci): prevent script injection in GitHub Actions workflows#1669
Open
aidandaly24 wants to merge 1 commit into
Open
fix(ci): prevent script injection in GitHub Actions workflows#1669aidandaly24 wants to merge 1 commit into
aidandaly24 wants to merge 1 commit into
Conversation
Bind untrusted GitHub context values (github.actor, github.event.*,
inputs.*, step outputs) to env vars and reference them as shell/JS
variables instead of interpolating ${{ }} directly into run: and
github-script bodies.
Inline expansion lets attacker-controlled event data (e.g. a PR head
SHA or a diff-derived test-file list) execute as shell. Routing values
through env: removes the injection surface while preserving behavior.
Files: pr-ai-review.yml, e2e-tests.yml, pr-tarball.yml, release.yml
Contributor
Package TarballHow to installgh release download pr-1669-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.21.1.tgz |
Contributor
|
Claude Security Review: no high-confidence findings. (run) |
agentcore-cli-automation
approved these changes
Jun 30, 2026
agentcore-cli-automation
left a comment
There was a problem hiding this comment.
Looks good to merge.
The mechanical context → env: translation is applied consistently across all four workflows. Verified that no ${{ … }} expressions remain inside any run: or script: body in the modified files (and the same holds for the other pull_request_target workflows in the repo — codeql.yml, pr-security-review.yml, pr-size.yml, pr-title.yml — so scope is appropriate).
A few details I checked and was happy with:
e2e-tests.ymlunquoted$GA_EXTRA/$HARNESS_EXTRA: the inline comment correctly flags this as intentional. The values are derived fromgit diff --name-only -- 'e2e-tests/*.test.ts', then funneled throughtr '\n' ' ', so word-splitting is the desired behavior. The unquoted form will undergo pathname expansion and word splitting but not command/process substitution, and the step is gated byAUTHORIZED_USERS, so the residual risk (a maintainer-authored test file with shell-meaningful characters in its name) is acceptable.pr-ai-review.ymlPR URL → number flow:PR_NUM="${PR_URL##*/}"is pure parameter expansion, and the downstream JS only doesparseInt(process.env.PR_NUMBER), so untrusted content can't escape.release.yml: onlyworkflow_dispatch-triggered, but the env-var pattern is still the right hygiene.
Contributor
Coverage Report
|
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.
Description
Hardens our GitHub Actions workflows against script injection (the AppSec/ACAT "Script Injection in GitHub Actions workflows" finding class).
Several
run:andactions/github-scriptsteps interpolated GitHub context values directly into the script body via${{ … }}. Because the Actions runner performs that substitution before the shell/JS sees the line, any attacker-controlled event data lands as executable code. The most sensitive cases here run underpull_request_target(so they execute with repo secrets + a write token against untrusted PR content).The fix follows GitHub's recommended pattern: bind each expression to an
env:variable and reference the shell/JS variable instead.env:assignments are not an injection vector. No behavior changes.Files & what changed
Determine PR URL/Extract PR numbersteps now readinputs.pr_url,github.event.pull_request.html_url,github.event_namefromenv:; bothgithub-scriptsteps read the PR number viaprocess.env.PR_NUMBER.github.actor,secrets.AUTHORIZED_USERS), CDK clone branch (inputs.cdk_branch), changed-file detection (github.event.pull_request.base.sha), and bothvitestruns (diff-derived*_extratest-file lists) now flow throughenv:. The*_EXTRAvars are intentionally left unquoted so they still word-split into vitest args.github.event.pull_request.head.sha/github.repositorynow flow throughenv:.Determine release metadata(github.ref_name,github.event.inputs.bump_type) andVerify we have the merged code(needs.*.outputs.base_branch) now flow throughenv:.Type of Change
Testing
These are CI workflow files; changes are mechanical (context →
env:var) and preserve existing behavior. Validated by:${{ }}expressions remain inside anyrun:/script:body across the four filesChecklist