From 7ecb0fcdae2d50f5d6ee68bdc56469947a3e73ac Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 26 Oct 2026 15:23:53 -0700 Subject: [PATCH] agents: add GitHub Actions workflow security review rules GitHub Actions workflows and scripts can introduce command, flag, and environment injection vulnerabilities when handling untrusted inputs from pull requests, comments, and git refs. Without explicit review criteria, code review audits may overlook these attack vectors. Add security review rules and audit checks covering expression interpolation in `run:` and `script:` blocks, untrusted git ref names, CLI option-flag injection, pre-mutation actor authorization, single-line `$GITHUB_OUTPUT` and `$GITHUB_ENV` writes, and untrusted stdout suppression. --- .agents/rules/github_actions_workflows.md | 30 +++++++++++++++++++ .agents/skills/review-code/SKILL.md | 4 ++- .../review-code/review-workflows-prompt.md | 16 ++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/.agents/rules/github_actions_workflows.md b/.agents/rules/github_actions_workflows.md index a61f57d4a1..b5c5c77160 100644 --- a/.agents/rules/github_actions_workflows.md +++ b/.agents/rules/github_actions_workflows.md @@ -55,6 +55,36 @@ globs: ".github/workflows/**,.github/actions/**,.github/dependabot.yml" into a single flat directory first (reading paths from the rule's `.txt` manifest) so `/...` subdirectories are not preserved in the artifact. +## Security review +* **No `${{ ... }}` in `run:` or `script:` Blocks**: Never interpolate + `${{ ... }}` expressions (including `${{ secrets.* }}`) directly into `run:` + shell scripts or `actions/github-script` `script:` bodies, even inside + quotes. Bind expressions to `env:` variables (strictly step-scoped for + `secrets.*`) and reference them as quoted shell variables (`"$VAR"`) or + `process.env.VAR`. +* **Untrusted Git Ref Names (`github.head_ref`, `github.ref_name`)**: Treat + branch and tag names as untrusted input. Git ref names allow quotes (`'`, `"`) + and shell metacharacters (`;`, `&`, `|`, `$()`, `` ` ``); reject or guard + against quotes and `#` before writing refs to `.bazelrc`. +* **CLI Argument & Option-Flag Injection**: Validate untrusted inputs (such as + slash-command arguments from issue or PR comments) against strict allowlist + regexes before writing to `$GITHUB_OUTPUT` or passing to tools. Separate + options from positional arguments with `--` when expanding arrays into CLI + commands (e.g. `cmd "${ARGS[@]}" -- "${positional[@]}"`). +* **Authorize Before Mutating State**: In comment- or event-triggered workflows, + verify actor authorization (`author_association` or user allowlist) before + writing to `$GITHUB_OUTPUT` or `$GITHUB_ENV`, posting reactions, or triggering + failure-reporting jobs. +* **Single-Line `$GITHUB_OUTPUT` and `$GITHUB_ENV` Writes**: Reject `\n` and + `\r` in keys and values before writing `key=value` lines to `$GITHUB_OUTPUT` + or `$GITHUB_ENV`. In line-anchored regexes over comment or PR bodies, match + horizontal whitespace (`[ \t]`) and non-linebreak characters (`[^\r\n]`) + rather than `\s` or `.`. +* **Suppress Untrusted Stdout in Privileged Jobs**: Never echo raw PR bodies, + titles, or comment bodies to stdout (pass `--quiet` to `grep`, and use + `printf '%s\n'` instead of `echo`), preventing `::...::` workflow command + injection. + ## Workflow Scripts & Testing * **Separate Script Files (No Inline Scripts)**: Factor non-trivial scripts (such as multi-line shell or Python logic) into standalone files (e.g. `.sh` diff --git a/.agents/skills/review-code/SKILL.md b/.agents/skills/review-code/SKILL.md index 9a35b66904..d5eec4fbd8 100644 --- a/.agents/skills/review-code/SKILL.md +++ b/.agents/skills/review-code/SKILL.md @@ -60,7 +60,9 @@ file from `.agents/skills/review-code/`: - Focus: Audits workflow files (`.github/**`) and workflow tests against `.agents/rules/github_actions_workflows.md` (e.g. `chmod +x` executable permissions, shebangs, action and tool versions, - `./.github/actions/setup-bazel` wrapper usage, fallback jobs). + `./.github/actions/setup-bazel` wrapper usage, fallback jobs, and shell, + script, CLI flag, `$GITHUB_OUTPUT`, or workflow-command injection + vulnerabilities). 8. **`rules_python` Usage Sub-Agent** (`Role: "rules_python Usage Auditor"`): - Prompt file: diff --git a/.agents/skills/review-code/review-workflows-prompt.md b/.agents/skills/review-code/review-workflows-prompt.md index c596ac0a80..0162015458 100644 --- a/.agents/skills/review-code/review-workflows-prompt.md +++ b/.agents/skills/review-code/review-workflows-prompt.md @@ -36,5 +36,21 @@ project's workflow rules and conventions: `uses: ./.github/actions/setup-bazel` (updating `.github/actions/setup-bazel/action.yaml` if new defaults or inputs are needed). +10. Audit for injection and security vulnerabilities: + - Flag any `${{ ... }}` expression interpolated directly inside a `run:` + shell block or `actions/github-script` `script:` body; require binding to + `env:` and referencing via `"$VAR"` or `process.env.VAR`. + - Check how PR author or comment author controlled inputs + (`github.head_ref`, `github.ref_name`, `github.event.pull_request.*`, + `github.event.comment.*`, `github.event.issue.*`, `inputs.*`) flow into + `.bazelrc`, shell commands, and CLI invocations. Require `--` before + positional argument arrays and strict allowlist regex validation on + comment arguments. + - Verify comment and event handlers check actor authorization before writing + to `$GITHUB_OUTPUT` or `$GITHUB_ENV`, reacting to comments, or triggering + failure-reporting jobs. + - Verify helpers writing `key=value` to `$GITHUB_OUTPUT` or `$GITHUB_ENV` + reject `\n` and `\r`, and ensure untrusted PR/comment text is never + echoed to stdout where `::...::` workflow commands could execute. @.agents/skills/review-code/review-report-format.md