glsec

Rules / GL011

GL011 Download-and-execute pattern in script (curl | bash)

Severity: error
OWASP: CICD-SEC-3 – Dependency Chain Abuse

What glsec checks

glsec flags script lines that download content with curl or wget and pipe it directly into a shell interpreter, including via bash process substitution:

Pattern Example
Pipe to shell curl URL | bash
Pipe to sh wget -qO- URL | sh
Pipe to Python curl URL | python3
Pipe to Ruby/Perl/Node curl URL | ruby
Process substitution python3 <(curl URL)
Multi-stage pipe curl URL | base64 -d | bash
Command substitution bash -c "$(curl URL)"
Substitution piped to shell echo "$(curl URL)" | bash
Inline base64 payload echo "…" | base64 -d | bash
Download-then-execute curl URL -o f.sh && bash f.sh
Redirect-then-execute curl URL > f.sh; sh f.sh

A line that also runs an integrity check (sha256sum, sha512sum, shasum, gpg --verify, cosign verify) is treated as verified and not flagged. Pipes or shell tokens that appear only inside a quoted string literal do not trigger.

Trigger example

setup:
  script:
    - curl -sSL https://install.example.com/setup.sh | bash
    - wget -qO- https://raw.githubusercontent.com/org/repo/main/install.sh | sh
    - python3 <(curl -s https://example.com/bootstrap.py)
    - echo "$(curl -s https://example.com/payload)" | bash
    - echo "ZWNobyBwd25lZAo=" | base64 -d | bash
    - curl -fsSL https://example.com/install.sh -o install.sh && bash install.sh
    - curl -fsSL https://example.com/payload.sh > install.sh; sh install.sh

Safe alternative

Download the script first, verify its checksum, then execute:

setup:
  script:
    - curl -sSLO https://install.example.com/setup.sh
    - echo "a3b4c5d6…  setup.sh" | sha256sum --check
    - bash setup.sh

Or better: commit the script to the repository and reference it locally, removing the runtime network dependency entirely.

Why this matters

HTTPS provides transport-level security but no content integrity guarantee. If the script host is compromised, the pipeline will silently execute malicious code in the CI environment with access to all secrets, tokens, and build artifacts. Unlike a committed dependency, the executed code is unversioned and cannot be audited in pull request reviews or git history.

See also

GL083 covers the other half of this problem: a script line that opens a shell back to a remote host without downloading anything.

Enable the ShellCheck integration to surface additional shell issues in these script blocks.

Documentation for glsec 1.23.0. Source of this page · Rule implementation