267 lines
8.6 KiB
YAML
267 lines
8.6 KiB
YAML
rules:
|
|
- id: yams.github-actions.cross-workflow-artifact-download
|
|
languages: [yaml]
|
|
severity: WARNING
|
|
message: >-
|
|
Uses a third-party artifact download action across workflows. This may allow artifact
|
|
poisoning from untrusted sources. Prefer the official actions/download-artifact@v4 with
|
|
an explicit run-id, github-token, and validation of the source run (conclusion, branch,
|
|
event). Reject pull_request-triggered runs as artifact sources.
|
|
metadata:
|
|
category: security
|
|
confidence: high
|
|
precision: high
|
|
yams_triage: artifact-integrity
|
|
references:
|
|
- https://github.com/dawidd6/action-download-artifact
|
|
pattern-regex: 'uses:\s*dawidd6/action-download-artifact@'
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.mutable-action-ref
|
|
languages: [yaml]
|
|
severity: WARNING
|
|
message: >-
|
|
GitHub Action referenced at a mutable ref (@main or @master). Pinning to a specific
|
|
version tag (e.g. @v4) prevents supply-chain drift; pinning to a full commit SHA
|
|
prevents tag-move attacks. Use @v4 or @<sha> instead.
|
|
metadata:
|
|
category: security
|
|
confidence: high
|
|
precision: medium
|
|
yams_triage: supply-chain
|
|
pattern-regex: 'uses:\s*[^\n@ ]+@(main|master)\b'
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.curl-pipe-shell
|
|
languages: [yaml]
|
|
severity: WARNING
|
|
message: >-
|
|
Piped curl-to-shell pattern found in a workflow step. This executes untrusted network
|
|
content directly. Prefer verifying a checksum against a known-good manifest or using a
|
|
pinned tool/action instead.
|
|
metadata:
|
|
category: security
|
|
confidence: high
|
|
precision: high
|
|
yams_triage: supply-chain
|
|
cwe: "CWE-494"
|
|
pattern-regex: 'curl\s+.*\s*\|\s*(?:sudo\s+)?(?:ba)?sh\b'
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.privileged-manual-workflow-no-environment
|
|
languages: [yaml]
|
|
severity: WARNING
|
|
message: >-
|
|
Workflow triggered by workflow_dispatch with elevated permissions (contents: write,
|
|
id-token: write) but without an environment protection rule. This allows any committer
|
|
to trigger privileged actions without review. Gate the job behind an environment with
|
|
required reviewers or a deployment protection rule.
|
|
metadata:
|
|
category: security
|
|
confidence: medium
|
|
precision: medium
|
|
yams_triage: access-control
|
|
patterns:
|
|
- pattern-inside: |
|
|
...
|
|
on:
|
|
...
|
|
workflow_dispatch:
|
|
...
|
|
permissions:
|
|
contents: write
|
|
...
|
|
- pattern-inside: |
|
|
jobs:
|
|
...
|
|
- pattern: |
|
|
runs-on: ...
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.gh-release-download-raw
|
|
languages: [yaml]
|
|
severity: INFO
|
|
message: >-
|
|
Downloads release assets via gh release download with --pattern. Ensure the release
|
|
is from a trusted, verified tag. Consider using gh attestation verify or a checksum
|
|
manifest produced during the build job to validate asset integrity before use.
|
|
metadata:
|
|
category: security
|
|
confidence: low
|
|
precision: medium
|
|
yams_triage: artifact-integrity
|
|
pattern-regex: 'gh\s+release\s+download\s+.*--pattern'
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.pull-request-target-checkout
|
|
languages: [yaml]
|
|
severity: ERROR
|
|
message: >-
|
|
pull_request_target event with actions/checkout that does not pin ref to the base
|
|
branch. The default checkout checks out the untrusted PR head, giving script injection
|
|
access to repository secrets. Always pass ref: ${{ github.event.pull_request.base.sha }}
|
|
or ref: ${{ github.base_ref }} when using pull_request_target.
|
|
metadata:
|
|
category: security
|
|
confidence: high
|
|
precision: medium
|
|
yams_triage: access-control
|
|
cwe: "CWE-913"
|
|
patterns:
|
|
- pattern-inside: |
|
|
on:
|
|
...
|
|
pull_request_target:
|
|
...
|
|
...
|
|
- pattern: |
|
|
uses: actions/checkout@...
|
|
- pattern-not: |
|
|
uses: actions/checkout@...
|
|
...
|
|
ref: ${{ github.event.pull_request.base.sha }}
|
|
- pattern-not: |
|
|
uses: actions/checkout@...
|
|
...
|
|
ref: ${{ github.base_ref }}
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.workflow-dispatch-untrusted-input
|
|
languages: [yaml]
|
|
severity: WARNING
|
|
message: >-
|
|
workflow_dispatch input is used raw in a shell run step (e.g. ${{ inputs.X }}).
|
|
Without sanitization, this is an injection vector. Prefer setting the input to an
|
|
intermediate environment variable and validate it against a known set of values before
|
|
use in shell commands. Check for input validation (case/if guard) nearby.
|
|
metadata:
|
|
category: security
|
|
confidence: low
|
|
precision: low
|
|
yams_triage: injection
|
|
cwe: "CWE-20"
|
|
pattern-regex: '\$\{\{\s*inputs\.'
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|
|
|
|
- id: yams.github-actions.artifact-download-without-source-validation
|
|
languages: [yaml]
|
|
severity: WARNING
|
|
message: >-
|
|
Downloads artifacts from a workflow_dispatch-controlled run_id input without visible
|
|
source validation. An attacker with write access can supply an arbitrary run_id pointing
|
|
to poisoned artifacts. Validate the run before downloading (check conclusion=success,
|
|
reject pull_request-triggered runs, verify the workflow name/event).
|
|
metadata:
|
|
category: security
|
|
confidence: high
|
|
precision: medium
|
|
yams_triage: artifact-integrity
|
|
cwe: "CWE-345"
|
|
patterns:
|
|
- pattern-inside: |
|
|
on:
|
|
...
|
|
workflow_dispatch:
|
|
...
|
|
inputs:
|
|
...
|
|
run_id:
|
|
...
|
|
...
|
|
- pattern: |
|
|
uses: actions/download-artifact@...
|
|
...
|
|
run-id: ${{ ...
|
|
- pattern-not: |
|
|
uses: actions/download-artifact@...
|
|
...
|
|
run-id: ${{ steps.resolve...
|
|
paths:
|
|
include:
|
|
- /.github/workflows/**/*.yml
|
|
- /.github/workflows/**/*.yaml
|
|
- /tools/opengrep/tests/**/*.yml
|
|
- /tools/opengrep/tests/**/*.yaml
|
|
- tools/opengrep/tests/**/*.yml
|
|
- tools/opengrep/tests/**/*.yaml
|
|
exclude:
|
|
- /third_party/**
|
|
- /build/**
|
|
- /build*/**
|