mirror of
https://github.com/astral-sh/ruff
synced 2026-01-10 08:04:26 -05:00
## 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`
154 lines
5.2 KiB
YAML
154 lines
5.2 KiB
YAML
# Publish the Ruff documentation.
|
|
#
|
|
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce
|
|
# job within `cargo-dist`.
|
|
name: mkdocs
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
ref:
|
|
description: "The commit SHA, tag, or branch to publish. Uses the default branch if not specified."
|
|
default: ""
|
|
type: string
|
|
workflow_call:
|
|
inputs:
|
|
plan:
|
|
required: true
|
|
type: string
|
|
|
|
jobs:
|
|
mkdocs:
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
persist-credentials: true
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: 3.12
|
|
|
|
- name: "Set docs version"
|
|
run: |
|
|
version="${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || inputs.ref }}"
|
|
# if version is missing, use 'latest'
|
|
if [ -z "$version" ]; then
|
|
echo "Using 'latest' as version"
|
|
version="latest"
|
|
fi
|
|
|
|
# Use version as display name for now
|
|
display_name="$version"
|
|
|
|
echo "version=$version" >> $GITHUB_ENV
|
|
echo "display_name=$display_name" >> $GITHUB_ENV
|
|
|
|
- name: "Set branch name"
|
|
run: |
|
|
version="${{ env.version }}"
|
|
display_name="${{ env.display_name }}"
|
|
timestamp="$(date +%s)"
|
|
|
|
# create branch_display_name from display_name by replacing all
|
|
# characters disallowed in git branch names with hyphens
|
|
branch_display_name="$(echo "$display_name" | tr -c '[:alnum:]._' '-' | tr -s '-')"
|
|
|
|
echo "branch_name=update-docs-$branch_display_name-$timestamp" >> $GITHUB_ENV
|
|
echo "timestamp=$timestamp" >> $GITHUB_ENV
|
|
|
|
- name: "Add SSH key"
|
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
|
uses: webfactory/ssh-agent@v0.9.0
|
|
with:
|
|
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
|
|
|
- name: "Install Rust toolchain"
|
|
run: rustup show
|
|
|
|
- uses: Swatinem/rust-cache@v2
|
|
|
|
- name: "Install Insiders dependencies"
|
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
|
run: pip install -r docs/requirements-insiders.txt
|
|
|
|
- name: "Install dependencies"
|
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
|
run: pip install -r docs/requirements.txt
|
|
|
|
- name: "Copy README File"
|
|
run: |
|
|
python scripts/transform_readme.py --target mkdocs
|
|
python scripts/generate_mkdocs.py
|
|
|
|
- name: "Build Insiders docs"
|
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
|
run: mkdocs build --strict -f mkdocs.insiders.yml
|
|
|
|
- name: "Build docs"
|
|
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
|
run: mkdocs build --strict -f mkdocs.public.yml
|
|
|
|
- name: "Clone docs repo"
|
|
run: |
|
|
version="${{ env.version }}"
|
|
git clone https://${{ secrets.ASTRAL_DOCS_PAT }}@github.com/astral-sh/docs.git astral-docs
|
|
|
|
- name: "Copy docs"
|
|
run: rm -rf astral-docs/site/ruff && mkdir -p astral-docs/site && cp -r site/ruff astral-docs/site/
|
|
|
|
- name: "Commit docs"
|
|
working-directory: astral-docs
|
|
run: |
|
|
branch_name="${{ env.branch_name }}"
|
|
|
|
git config user.name "astral-docs-bot"
|
|
git config user.email "176161322+astral-docs-bot@users.noreply.github.com"
|
|
|
|
git checkout -b $branch_name
|
|
git add site/ruff
|
|
git commit -m "Update ruff documentation for $version"
|
|
|
|
- name: "Create Pull Request"
|
|
working-directory: astral-docs
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }}
|
|
run: |
|
|
version="${{ env.version }}"
|
|
display_name="${{ env.display_name }}"
|
|
branch_name="${{ env.branch_name }}"
|
|
|
|
# set the PR title
|
|
pull_request_title="Update ruff documentation for $display_name"
|
|
|
|
# Delete any existing pull requests that are open for this version
|
|
# by checking against pull_request_title because the new PR will
|
|
# supersede the old one.
|
|
gh pr list --state open --json title --jq '.[] | select(.title == "$pull_request_title") | .number' | \
|
|
xargs -I {} gh pr close {}
|
|
|
|
# push the branch to GitHub
|
|
git push origin $branch_name
|
|
|
|
# create the PR
|
|
gh pr create --base main --head $branch_name \
|
|
--title "$pull_request_title" \
|
|
--body "Automated documentation update for $display_name" \
|
|
--label "documentation"
|
|
|
|
- name: "Merge Pull Request"
|
|
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
|
|
working-directory: astral-docs
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }}
|
|
run: |
|
|
branch_name="${{ env.branch_name }}"
|
|
|
|
# auto-merge the PR if the build was triggered by a release. Manual builds should be reviewed by a human.
|
|
# give the PR a few seconds to be created before trying to auto-merge it
|
|
sleep 10
|
|
gh pr merge --squash $branch_name
|