## Summary A [recent exploit](https://github.com/advisories/GHSA-7x29-qqmq-v6qc) brought attention to how easy it can be for attackers to use template expansion in GitHub Actions workflows to inject arbitrary code into a repository. That vulnerability [would have been caught by the zizmor linter](https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-injection), which looks for potential security vulnerabilities in GitHub Actions workflows. This PR adds [zizmor](https://github.com/woodruffw/zizmor) as a pre-commit hook and fixes the high- and medium-severity warnings flagged by the tool. All the warnings fixed in this PR are related to this zizmor check: https://woodruffw.github.io/zizmor/audits/#artipacked. The summary of the check is that `actions/checkout` will by default persist git configuration for the duration of the workflow, which can be insecure. It's unnecessary unless you actually need to do things with `git` later on in the workflow. None of our workflows do except for `publish-docs.yml` and `sync-typeshed.yml`, so I set `persist-credentials: true` for those two but `persist-credentials: false` for all other uses of `actions/checkout`. Unfortunately there are several warnings in `release.yml`, including four high-severity warnings. However, this is a generated workflow file, so I have deliberately excluded this file from the check. These are the findings in `release.yml`: <details> <summary>release.yml findings</summary> ``` warning[artipacked]: credential persistence through GitHub Actions artifacts --> /Users/alexw/dev/ruff/.github/workflows/release.yml:62:9 | 62 | - uses: actions/checkout@v4 | _________- 63 | | with: 64 | | submodules: recursive | |_______________________________- does not set persist-credentials: false | = note: audit confidence → Low warning[artipacked]: credential persistence through GitHub Actions artifacts --> /Users/alexw/dev/ruff/.github/workflows/release.yml:124:9 | 124 | - uses: actions/checkout@v4 | _________- 125 | | with: 126 | | submodules: recursive | |_______________________________- does not set persist-credentials: false | = note: audit confidence → Low warning[artipacked]: credential persistence through GitHub Actions artifacts --> /Users/alexw/dev/ruff/.github/workflows/release.yml:174:9 | 174 | - uses: actions/checkout@v4 | _________- 175 | | with: 176 | | submodules: recursive | |_______________________________- does not set persist-credentials: false | = note: audit confidence → Low warning[artipacked]: credential persistence through GitHub Actions artifacts --> /Users/alexw/dev/ruff/.github/workflows/release.yml:249:9 | 249 | - uses: actions/checkout@v4 | _________- 250 | | with: 251 | | submodules: recursive 252 | | # Create a GitHub Release while uploading all files to it | |_______________________________________________________________- does not set persist-credentials: false | = note: audit confidence → Low error[excessive-permissions]: overly broad workflow or job-level permissions --> /Users/alexw/dev/ruff/.github/workflows/release.yml:17:1 | 17 | / permissions: 18 | | "contents": "write" ... | 39 | | # If there's a prerelease-style suffix to the version, then the release(s) 40 | | # will be marked as a prerelease. | |_________________________________^ contents: write is overly broad at the workflow level | = note: audit confidence → High error[template-injection]: code injection via template expansion --> /Users/alexw/dev/ruff/.github/workflows/release.yml:80:9 | 80 | - id: plan | _________^ 81 | | run: | | |_________^ 82 | || dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --out... 83 | || echo "dist ran successfully" 84 | || cat plan-dist-manifest.json 85 | || echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" | ||__________________________________________________________________________________^ this step | ||__________________________________________________________________________________^ inputs.tag may expand into attacker-controllable code | = note: audit confidence → Low error[template-injection]: code injection via template expansion --> /Users/alexw/dev/ruff/.github/workflows/release.yml:80:9 | 80 | - id: plan | _________^ 81 | | run: | | |_________^ 82 | || dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --out... 83 | || echo "dist ran successfully" 84 | || cat plan-dist-manifest.json 85 | || echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" | ||__________________________________________________________________________________^ this step | ||__________________________________________________________________________________^ inputs.tag may expand into attacker-controllable code | = note: audit confidence → Low error[template-injection]: code injection via template expansion --> /Users/alexw/dev/ruff/.github/workflows/release.yml:80:9 | 80 | - id: plan | _________^ 81 | | run: | | |_________^ 82 | || dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --out... 83 | || echo "dist ran successfully" 84 | || cat plan-dist-manifest.json 85 | || echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" | ||__________________________________________________________________________________^ this step | ||__________________________________________________________________________________^ inputs.tag may expand into attacker-controllable code | = note: audit confidence → Low ``` </details> ## Test Plan `uvx pre-commit run -a`
78 lines
2.2 KiB
YAML
78 lines
2.2 KiB
YAML
name: Daily parser fuzz
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
schedule:
|
|
- cron: "0 0 * * *"
|
|
pull_request:
|
|
paths:
|
|
- ".github/workflows/daily_fuzz.yaml"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
CARGO_INCREMENTAL: 0
|
|
CARGO_NET_RETRY: 10
|
|
CARGO_TERM_COLOR: always
|
|
RUSTUP_MAX_RETRIES: 10
|
|
PACKAGE_NAME: ruff
|
|
FORCE_COLOR: 1
|
|
|
|
jobs:
|
|
fuzz:
|
|
name: Fuzz
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
# Don't run the cron job on forks:
|
|
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
- uses: astral-sh/setup-uv@v4
|
|
- name: "Install Rust toolchain"
|
|
run: rustup show
|
|
- name: "Install mold"
|
|
uses: rui314/setup-mold@v1
|
|
- uses: Swatinem/rust-cache@v2
|
|
- name: Build ruff
|
|
# A debug build means the script runs slower once it gets started,
|
|
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
|
|
run: cargo build --locked
|
|
- name: Fuzz
|
|
run: |
|
|
(
|
|
uvx \
|
|
--python=3.12 \
|
|
--from=./python/py-fuzzer \
|
|
fuzz \
|
|
--test-executable=target/debug/ruff \
|
|
--bin=ruff \
|
|
$(shuf -i 0-9999999999999999999 -n 1000)
|
|
)
|
|
|
|
create-issue-on-failure:
|
|
name: Create an issue if the daily fuzz surfaced any bugs
|
|
runs-on: ubuntu-latest
|
|
needs: fuzz
|
|
if: ${{ github.repository == 'astral-sh/ruff' && always() && github.event_name == 'schedule' && needs.fuzz.result == 'failure' }}
|
|
permissions:
|
|
issues: write
|
|
steps:
|
|
- uses: actions/github-script@v7
|
|
with:
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
script: |
|
|
await github.rest.issues.create({
|
|
owner: "astral-sh",
|
|
repo: "ruff",
|
|
title: `Daily parser fuzz failed on ${new Date().toDateString()}`,
|
|
body: "Runs listed here: https://github.com/astral-sh/ruff/actions/workflows/daily_fuzz.yml",
|
|
labels: ["bug", "parser", "fuzzer"],
|
|
})
|