mirror of
https://github.com/zizmorcore/zizmor.git
synced 2025-12-23 08:47:33 +00:00
Compare commits
30 commits
github-act
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff76e6c96e | ||
|
|
8a138d4d7f | ||
|
|
27041c58c9 | ||
|
|
c3913e7eff | ||
|
|
1a6a008951 | ||
|
|
a5e304f536 | ||
|
|
2942f11dc2 | ||
|
|
9d61a10dc1 | ||
|
|
de6f9d6042 | ||
|
|
cc76e2b93f | ||
|
|
7e1c93b760 | ||
|
|
081f844760 | ||
|
|
abdfe5d9b6 | ||
|
|
32d6831a1f | ||
|
|
eafa9e43fc | ||
|
|
f0525e0f30 | ||
|
|
5b7e8bcd72 | ||
|
|
4a7e4e27bc | ||
|
|
147bfabc0f | ||
|
|
411b74a5a9 | ||
|
|
c9fc966d0a | ||
|
|
b3e8725791 | ||
|
|
5bb11ce251 | ||
|
|
94eea71efd | ||
|
|
b9b65b0e80 | ||
|
|
c9f0ea9aaf | ||
|
|
5987ac7503 | ||
|
|
b714997a0c | ||
|
|
b78376a737 | ||
|
|
1f71a18100 |
85 changed files with 1304 additions and 706 deletions
51
.github/workflows/benchmark.yml
vendored
Normal file
51
.github/workflows/benchmark.yml
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
name: CodSpeed Benchmarks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
- labeled
|
||||
# `workflow_dispatch` allows CodSpeed to trigger backtest
|
||||
# performance analysis in order to generate initial data.
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
benchmarks:
|
||||
name: Run benchmarks
|
||||
# PRs only get benchmarked if they have the `run-benchmarks` label.
|
||||
if: |
|
||||
contains(github.event.pull_request.labels.*.name, 'run-benchmarks')
|
||||
|| github.event_name == 'push'
|
||||
|| github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
with:
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: Build zizmor (release)
|
||||
run: cargo build --release
|
||||
|
||||
- name: Run the benchmarks
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
with:
|
||||
mode: walltime
|
||||
run: make bench
|
||||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
@ -38,13 +38,13 @@ jobs:
|
|||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
|
||||
- uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: Test dependencies
|
||||
run: |
|
||||
|
|
@ -67,11 +67,11 @@ jobs:
|
|||
name: Test site build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: Test site
|
||||
run: make site
|
||||
|
|
|
|||
16
.github/workflows/codegen.yml
vendored
16
.github/workflows/codegen.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
pull-requests: write # for opening PRs
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ jobs:
|
|||
make refresh-schemas
|
||||
|
||||
- name: create PR
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
with:
|
||||
draft: true
|
||||
commit-message: "[BOT] update JSON schemas from SchemaStore"
|
||||
|
|
@ -59,18 +59,18 @@ jobs:
|
|||
pull-requests: write # for opening PRs
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: try to refresh context capabilities
|
||||
run: |
|
||||
make webhooks-to-contexts
|
||||
|
||||
- name: create PR
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
with:
|
||||
draft: true
|
||||
commit-message: "[BOT] update context capabilities"
|
||||
|
|
@ -97,18 +97,18 @@ jobs:
|
|||
pull-requests: write # for opening PRs
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: try to refresh CodeQL injection sinks
|
||||
run: |
|
||||
make codeql-injection-sinks
|
||||
|
||||
- name: create PR
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
with:
|
||||
draft: true
|
||||
commit-message: "[BOT] update CodeQL injection sinks"
|
||||
|
|
|
|||
6
.github/workflows/release-binaries.yml
vendored
6
.github/workflows/release-binaries.yml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
target: x86_64-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: artifacts-${{ matrix.target }}
|
||||
path: ${{ steps.archive-release.outputs.filename }}
|
||||
|
|
@ -78,7 +78,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: distrib/
|
||||
|
|
|
|||
6
.github/workflows/release-docker.yml
vendored
6
.github/workflows/release-docker.yml
vendored
|
|
@ -43,7 +43,7 @@ jobs:
|
|||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: digests-${{ matrix.image.platform-pair }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
|
|
@ -107,7 +107,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-*
|
||||
|
|
|
|||
22
.github/workflows/release-pypi.yml
vendored
22
.github/workflows/release-pypi.yml
vendored
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
# target: ppc64le
|
||||
# manylinux: "2_28"
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build wheels
|
||||
|
|
@ -47,7 +47,7 @@ jobs:
|
|||
args: --release --out dist --manifest-path crates/zizmor/Cargo.toml
|
||||
manylinux: ${{ matrix.platform.manylinux }}
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: wheels-linux-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
|
@ -67,7 +67,7 @@ jobs:
|
|||
- runner: ubuntu-24.04
|
||||
target: armv7
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build wheels
|
||||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
args: --release --out dist --manifest-path crates/zizmor/Cargo.toml
|
||||
manylinux: musllinux_1_2
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: wheels-musllinux-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
- runner: windows-latest
|
||||
target: x86
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build wheels
|
||||
|
|
@ -102,7 +102,7 @@ jobs:
|
|||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist --manifest-path crates/zizmor/Cargo.toml
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: wheels-windows-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
|
@ -118,7 +118,7 @@ jobs:
|
|||
- runner: macos-15
|
||||
target: aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build wheels
|
||||
|
|
@ -127,7 +127,7 @@ jobs:
|
|||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist --manifest-path crates/zizmor/Cargo.toml
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: wheels-macos-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
|
@ -136,7 +136,7 @@ jobs:
|
|||
name: Build source distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build sdist
|
||||
|
|
@ -145,7 +145,7 @@ jobs:
|
|||
command: sdist
|
||||
args: --out dist --manifest-path crates/zizmor/Cargo.toml
|
||||
- name: Upload sdist
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: wheels-sdist
|
||||
path: dist
|
||||
|
|
@ -161,7 +161,7 @@ jobs:
|
|||
permissions:
|
||||
id-token: write # Trusted Publishing + PEP 740 attestations
|
||||
steps:
|
||||
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
- name: Attest
|
||||
uses: astral-sh/attest-action@2c727738cea36d6c97dd85eb133ea0e0e8fe754b # v0.0.4
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/release-support-crate.yml
vendored
2
.github/workflows/release-support-crate.yml
vendored
|
|
@ -53,7 +53,7 @@ jobs:
|
|||
permissions:
|
||||
id-token: write # for trusted publishing to crates.io
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
|
|||
2
.github/workflows/release-zizmor-crate.yml
vendored
2
.github/workflows/release-zizmor-crate.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
permissions:
|
||||
id-token: write # for trusted publishing to crates.io
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
|
|||
4
.github/workflows/site.yml
vendored
4
.github/workflows/site.yml
vendored
|
|
@ -26,12 +26,12 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: build site
|
||||
run: make site
|
||||
|
|
|
|||
30
.github/workflows/test-output.yml
vendored
30
.github/workflows/test-output.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
pull-requests: write # for 'Leave comment' step
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
cargo run -- --format sarif . > results.sarif
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
||||
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor-test-sarif-presentation
|
||||
|
|
@ -52,7 +52,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
@ -67,3 +67,27 @@ jobs:
|
|||
--no-exit-codes \
|
||||
--format github \
|
||||
crates/zizmor/tests/integration/test-data/several-vulnerabilities.yml
|
||||
|
||||
test-plain-presentation:
|
||||
name: Test plain text presentation
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.labels.*.name, 'test-plain-presentation')
|
||||
permissions: {}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
|
||||
- name: Run zizmor
|
||||
run: |
|
||||
# Normally we'd want a workflow to fail if the audit fails,
|
||||
# but we're only testing presentation here.
|
||||
cargo run \
|
||||
-- \
|
||||
--no-exit-codes \
|
||||
--format plain \
|
||||
crates/zizmor/tests/integration/test-data/several-vulnerabilities.yml
|
||||
|
|
|
|||
2
.github/workflows/wolfi-update-check.yml
vendored
2
.github/workflows/wolfi-update-check.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
issues: write # to create an issue if a new version is found
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: support/
|
||||
|
|
|
|||
2
.github/workflows/zizmor.yml
vendored
2
.github/workflows/zizmor.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -10,3 +10,6 @@
|
|||
|
||||
# pending snapshots
|
||||
.*.pending-snap
|
||||
|
||||
# benchmarks
|
||||
.codspeed/
|
||||
|
|
|
|||
184
Cargo.lock
generated
184
Cargo.lock
generated
|
|
@ -58,9 +58,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.12.9"
|
||||
version = "0.12.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44baf24dd94e781f74dfe67ffee75a09a57971ddf0f615a178b4f6d404b48ff"
|
||||
checksum = "15580ece6ea97cbf832d60ba19c021113469480852c6a2a6beb0db28f097bf1f"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width 0.2.2",
|
||||
|
|
@ -228,12 +228,6 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
|
|
@ -313,18 +307,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
|
||||
checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.41"
|
||||
version = "1.2.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
|
|
@ -344,9 +338,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.51"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -364,9 +358,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.51"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -376,9 +370,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.60"
|
||||
version = "4.5.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e602857739c5a4291dfa33b5a298aeac9006185229a700e5810a3ef7272d971"
|
||||
checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
|
@ -564,6 +558,12 @@ dependencies = [
|
|||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.4"
|
||||
|
|
@ -705,9 +705,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
|
|
@ -721,11 +721,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.1.4"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d"
|
||||
checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"borrow-or-share",
|
||||
"ref-cast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -923,7 +924,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "github-actions-models"
|
||||
version = "0.41.0"
|
||||
version = "0.42.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"insta",
|
||||
|
|
@ -954,9 +955,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
|
|
@ -977,12 +978,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
|
|
@ -1291,12 +1291,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
|
@ -1317,9 +1317,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.43.2"
|
||||
version = "1.44.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0"
|
||||
checksum = "b5c943d4415edd8153251b6f197de5eb1640e56d84e8d9159bea190421c73698"
|
||||
dependencies = [
|
||||
"console 0.15.11",
|
||||
"once_cell",
|
||||
|
|
@ -1384,13 +1384,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "0.35.0"
|
||||
version = "0.37.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0303b14f91cbac17c64aaf2ef60ab71fe5f34c3867cedcbca72c9dd15f5040fe"
|
||||
checksum = "73c9ffb2b5c56d58030e1b532d8e8389da94590515f118cf35b5cb68e4764a7e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.22.1",
|
||||
"bytecount",
|
||||
"data-encoding",
|
||||
"email_address",
|
||||
"fancy-regex",
|
||||
"fraction",
|
||||
|
|
@ -1428,7 +1428,7 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
|
@ -1477,16 +1477,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "lsp-types"
|
||||
version = "0.97.0"
|
||||
name = "ls-types"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53353550a17c04ac46c585feb189c2db82154fc84b79c7a66c96c2c644f66071"
|
||||
checksum = "7a7deb98ef9daaa7500324351a5bab7c80c644cfb86b4be0c4433b582af93510"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"fluent-uri 0.1.4",
|
||||
"bitflags",
|
||||
"fluent-uri 0.3.2",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1782,9 +1782,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
|||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4"
|
||||
checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"ucd-trie",
|
||||
|
|
@ -1792,9 +1792,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de"
|
||||
checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
|
|
@ -1802,9 +1802,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843"
|
||||
checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
|
|
@ -1815,9 +1815,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a"
|
||||
checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"sha2",
|
||||
|
|
@ -2048,7 +2048,7 @@ version = "0.5.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2073,14 +2073,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.35.0"
|
||||
version = "0.37.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22d0d0665043906aacf1d83bea9d61e5134f8f437815b84320e7facf8ff4e9c2"
|
||||
checksum = "4283168a506f0dcbdce31c9f9cce3129c924da4c6bca46e46707fcb746d2d70c"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"fluent-uri 0.4.1",
|
||||
"getrandom 0.3.4",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"serde_json",
|
||||
|
|
@ -2129,9 +2129,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
|||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.24"
|
||||
version = "0.12.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
|
||||
checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
|
|
@ -2224,7 +2224,7 @@ version = "1.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
|
@ -2347,7 +2347,7 @@ version = "3.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
|
@ -2490,17 +2490,6 @@ dependencies = [
|
|||
"syn 2.0.108",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.108",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.3"
|
||||
|
|
@ -3004,11 +2993,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
|
@ -3028,17 +3017,16 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
|||
|
||||
[[package]]
|
||||
name = "tower-lsp-server"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f3f8ec0dcfdda4d908bad2882fe0f89cf2b606e78d16491323e918dfa95765"
|
||||
checksum = "2f0e711655c89181a6bc6a2cc348131fcd9680085f5b06b6af13427a393a6e72"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"httparse",
|
||||
"lsp-types",
|
||||
"ls-types",
|
||||
"memchr",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
|
|
@ -3055,9 +3043,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
|||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
|
|
@ -3066,9 +3054,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.30"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -3077,9 +3065,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
|
|
@ -3087,9 +3075,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-indicatif"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04d4e11e0e27acef25a47f27e9435355fecdc488867fa2bc90e75b0700d2823d"
|
||||
checksum = "e1ef6990e0438749f0080573248e96631171a0b5ddfddde119aa5ba8c3a9c47e"
|
||||
dependencies = [
|
||||
"indicatif",
|
||||
"tracing",
|
||||
|
|
@ -3110,9 +3098,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.20"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
|
|
@ -3128,9 +3116,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.25.10"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87"
|
||||
checksum = "974d205cc395652cfa8b37daa053fe56eebd429acf8dc055503fee648dae981e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
|
|
@ -3142,9 +3130,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tree-sitter-bash"
|
||||
version = "0.25.0"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "871b0606e667e98a1237ebdc1b0d7056e0aebfdc3141d12b399865d4cb6ed8a6"
|
||||
checksum = "9e5ec769279cc91b561d3df0d8a5deb26b0ad40d183127f409494d6d8fc53062"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter-language",
|
||||
|
|
@ -3152,7 +3140,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tree-sitter-iter"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"tree-sitter",
|
||||
"tree-sitter-yaml",
|
||||
|
|
@ -3166,9 +3154,9 @@ checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8"
|
|||
|
||||
[[package]]
|
||||
name = "tree-sitter-powershell"
|
||||
version = "0.25.9"
|
||||
version = "0.25.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae0e37101b110badaf99aa40460915a8797ceba15fc0ed22773280377a8dffb6"
|
||||
checksum = "415ec6251d133d26b4f62c60721149fe36c315334f47812450187d6ea59cffdf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter-language",
|
||||
|
|
@ -3816,7 +3804,7 @@ checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
|
|||
|
||||
[[package]]
|
||||
name = "yamlpatch"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"insta",
|
||||
|
|
@ -3832,7 +3820,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yamlpath"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"line-index",
|
||||
"self_cell",
|
||||
|
|
@ -3956,7 +3944,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zizmor"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"anstream",
|
||||
|
|
|
|||
44
Cargo.toml
44
Cargo.toml
|
|
@ -21,38 +21,38 @@ rust-version = "1.88.0"
|
|||
[workspace.dependencies]
|
||||
anyhow = "1.0.100"
|
||||
github-actions-expressions = { path = "crates/github-actions-expressions", version = "0.0.11" }
|
||||
github-actions-models = { path = "crates/github-actions-models", version = "0.41.0" }
|
||||
github-actions-models = { path = "crates/github-actions-models", version = "0.42.0" }
|
||||
itertools = "0.14.0"
|
||||
pest = "2.8.3"
|
||||
pest_derive = "2.8.3"
|
||||
pest = "2.8.4"
|
||||
pest_derive = "2.8.4"
|
||||
pretty_assertions = "1.4.1"
|
||||
annotate-snippets = "0.12.9"
|
||||
annotate-snippets = "0.12.10"
|
||||
anstream = "0.6.21"
|
||||
assert_cmd = "2.1.1"
|
||||
async-trait = "0.1.89"
|
||||
camino = "1.2.1"
|
||||
clap = "4.5.51"
|
||||
camino = "1.2.2"
|
||||
clap = "4.5.53"
|
||||
clap-verbosity-flag = { version = "3.0.4", default-features = false }
|
||||
clap_complete = "4.5.60"
|
||||
clap_complete = "4.5.61"
|
||||
clap_complete_nushell = "4.5.10"
|
||||
csv = "1.3.1"
|
||||
etcetera = "0.11.0"
|
||||
flate2 = "1.1.5"
|
||||
fst = "0.4.7"
|
||||
futures = "0.3"
|
||||
http = "1.3.1"
|
||||
http = "1.4.0"
|
||||
http-cache-reqwest = { version = "1.0.0-alpha.2", features = ["manager-moka"] }
|
||||
human-panic = "2.0.4"
|
||||
ignore = "0.4.25"
|
||||
indexmap = { version = "2.11.4", features = ["serde"] }
|
||||
indexmap = { version = "2.12.1", features = ["serde"] }
|
||||
indicatif = "0.18"
|
||||
insta = "1.43.2"
|
||||
jsonschema = "0.35.0"
|
||||
insta = "1.44.3"
|
||||
jsonschema = "0.37.4"
|
||||
line-index = "0.1.2"
|
||||
memchr = "2.7.6"
|
||||
owo-colors = "4.2.3"
|
||||
regex = "1.12.1"
|
||||
reqwest = { version = "0.12.23", default-features = false }
|
||||
reqwest = { version = "0.12.25", default-features = false }
|
||||
reqwest-middleware = "0.4.2"
|
||||
self_cell = "1"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
|
@ -65,16 +65,18 @@ tar = "0.4.44"
|
|||
terminal-link = "0.1.0"
|
||||
thiserror = "2.0.17"
|
||||
tokio = { version = "1.47.1", features = ["rt-multi-thread", "io-std"] }
|
||||
tower-lsp-server = "0.22"
|
||||
tracing = "0.1.41"
|
||||
tracing-indicatif = "0.3.13"
|
||||
tower-lsp-server = "0.23"
|
||||
tracing = "0.1.43"
|
||||
tracing-indicatif = "0.3.14"
|
||||
tracing-subscriber = "0.3.20"
|
||||
tree-sitter = "0.25.10"
|
||||
tree-sitter-bash = "0.25.0"
|
||||
tree-sitter-iter = { path = "crates/tree-sitter-iter", version = "0.0.2" }
|
||||
tree-sitter-powershell = "0.25.9"
|
||||
yamlpath = { path = "crates/yamlpath", version = "0.29.0" }
|
||||
yamlpatch = { path = "crates/yamlpatch", version = "0.7.0" }
|
||||
tree-sitter = "0.26.3"
|
||||
tree-sitter-bash = "0.25.1"
|
||||
tree-sitter-iter = { path = "crates/tree-sitter-iter", version = "0.0.3" }
|
||||
# Exact version since the upstream performed a breaking change outside of semver.
|
||||
# See: https://github.com/zizmorcore/zizmor/pull/1427
|
||||
tree-sitter-powershell = "=0.25.10"
|
||||
yamlpath = { path = "crates/yamlpath", version = "0.31.0" }
|
||||
yamlpatch = { path = "crates/yamlpatch", version = "0.8.0" }
|
||||
tree-sitter-yaml = "0.7.2"
|
||||
tikv-jemallocator = "0.6"
|
||||
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -53,4 +53,4 @@ pinact:
|
|||
|
||||
.PHONY: bench
|
||||
bench:
|
||||
uv run bench/benchmark.py --offline
|
||||
uv run --only-group=bench pytest bench/ --codspeed
|
||||
|
|
|
|||
1
bench/.gitignore
vendored
1
bench/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
results/
|
||||
0
bench/__init__.py
Normal file
0
bench/__init__.py
Normal file
|
|
@ -1,242 +0,0 @@
|
|||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# ///
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Iterator, NoReturn, TypedDict
|
||||
|
||||
_DEPS = ["hyperfine", "curl", "unzip"]
|
||||
|
||||
_HERE = Path(__file__).parent
|
||||
_PROJECT_ROOT = _HERE.parent
|
||||
_ZIZMOR = _PROJECT_ROOT / "target" / "release" / "zizmor"
|
||||
|
||||
assert (_PROJECT_ROOT / "Cargo.toml").is_file(), "Missing project root?"
|
||||
|
||||
_BENCHMARKS = _HERE / "benchmarks.json"
|
||||
_RESULTS = _HERE / "results"
|
||||
|
||||
assert _BENCHMARKS.is_file(), f"Benchmarks file not found: {_BENCHMARKS}"
|
||||
_RESULTS.mkdir(exist_ok=True)
|
||||
|
||||
_CACHE_DIR = Path(tempfile.gettempdir()) / "zizmor-benchmark-cache"
|
||||
_CACHE_DIR.mkdir(exist_ok=True)
|
||||
|
||||
_GH_TOKEN = os.getenv("GH_TOKEN")
|
||||
|
||||
|
||||
class Log:
|
||||
def __init__(self, scope: str | None) -> None:
|
||||
self.scopes = [scope] if scope else []
|
||||
|
||||
def info(self, message: str) -> None:
|
||||
scopes = " ".join(f"[{s}]" for s in self.scopes)
|
||||
print(f"[+] {scopes} {message}", file=sys.stderr)
|
||||
|
||||
def warn(self, message: str) -> None:
|
||||
scopes = " ".join(f"[{s}]" for s in self.scopes)
|
||||
print(f"[!] {scopes} {message}", file=sys.stderr)
|
||||
|
||||
def error(self, message: str) -> NoReturn:
|
||||
self.warn(message)
|
||||
sys.exit(1)
|
||||
|
||||
@contextmanager
|
||||
def scope(self, new_scope: str) -> Iterator[None]:
|
||||
"""Create a new logging scope."""
|
||||
self.scopes.append(new_scope)
|
||||
try:
|
||||
yield None
|
||||
finally:
|
||||
self.scopes.pop()
|
||||
|
||||
|
||||
LOG = Log("benchmarks")
|
||||
|
||||
|
||||
def _curl(url: str, expected_sha256: str) -> Path:
|
||||
"""Download a URL and cache it using content addressing with SHA256."""
|
||||
cached_file = _CACHE_DIR / expected_sha256
|
||||
if cached_file.exists():
|
||||
LOG.info("Using cached file")
|
||||
return cached_file
|
||||
|
||||
result = subprocess.run(
|
||||
["curl", "-fsSL", url],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
content = result.stdout
|
||||
content_hash = hashlib.sha256(content).hexdigest()
|
||||
|
||||
if content_hash != expected_sha256:
|
||||
LOG.error(f"Hash mismatch: {expected_sha256} != {content_hash}")
|
||||
|
||||
cached_file.write_bytes(content)
|
||||
|
||||
return cached_file
|
||||
|
||||
|
||||
def _unzip(archive_path: Path, extract_name: str) -> Path:
|
||||
"""Extract an archive to a directory in the cache."""
|
||||
extract_dir = _CACHE_DIR / extract_name
|
||||
|
||||
if extract_dir.exists():
|
||||
LOG.info("Using cached extraction")
|
||||
return extract_dir
|
||||
|
||||
extract_dir.mkdir(exist_ok=True)
|
||||
|
||||
subprocess.run(
|
||||
["unzip", "-q", str(archive_path), "-d", str(extract_dir)],
|
||||
check=True,
|
||||
)
|
||||
|
||||
LOG.info(f"Extracted {archive_path.name} to {extract_dir}")
|
||||
return extract_dir
|
||||
|
||||
|
||||
class Benchmark(TypedDict):
|
||||
name: str
|
||||
source_type: str
|
||||
source: str
|
||||
source_sha256: str
|
||||
stencil: str
|
||||
online: bool | None
|
||||
|
||||
|
||||
Plan = list[str]
|
||||
|
||||
|
||||
class Bench:
|
||||
def __init__(self, benchmark: Benchmark) -> None:
|
||||
self.benchmark = benchmark
|
||||
|
||||
def plan(self) -> Plan:
|
||||
match self.benchmark["source_type"]:
|
||||
case "archive-url":
|
||||
url = self.benchmark["source"]
|
||||
sha256 = self.benchmark["source_sha256"]
|
||||
archive = _curl(url, sha256)
|
||||
inputs = [str(_unzip(archive, self.benchmark["name"]))]
|
||||
case _:
|
||||
LOG.error(f"Unknown source type: {self.benchmark['source_type']}")
|
||||
|
||||
if self.benchmark.get("online", False):
|
||||
if not _GH_TOKEN:
|
||||
LOG.error("Benchmark requires online access but GH_TOKEN is not set")
|
||||
|
||||
stencil = self.benchmark["stencil"]
|
||||
command = stencil.replace("$ZIZMOR", str(_ZIZMOR)).replace(
|
||||
"$INPUTS", " ".join(inputs)
|
||||
)
|
||||
return shlex.split(command)
|
||||
|
||||
def run(self, plan: Plan, *, dry_run: bool) -> None:
|
||||
command = shlex.join(plan)
|
||||
|
||||
result_file = _RESULTS / f"{self.benchmark['name']}.json"
|
||||
if result_file.exists() and not dry_run:
|
||||
LOG.warn("clobbering existing result file")
|
||||
|
||||
hyperfine_command = [
|
||||
"hyperfine",
|
||||
"--warmup",
|
||||
"3",
|
||||
# NOTE: not needed because we use --no-exit-codes in the stencil
|
||||
# "--ignore-failure",
|
||||
"--export-json",
|
||||
str(result_file),
|
||||
command,
|
||||
]
|
||||
|
||||
if dry_run:
|
||||
LOG.warn(f"would have run: {shlex.join(hyperfine_command)}")
|
||||
return
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
hyperfine_command,
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
LOG.error("run failed, see above for details")
|
||||
|
||||
# Stupid hack: fixup each result file's results[0].command
|
||||
# to be a more useful benchmark identifier, since bencher
|
||||
# apparently keys on these.
|
||||
result_json = json.loads(result_file.read_bytes())
|
||||
result_json["results"][0]["command"] = f"zizmor::{self.benchmark['name']}"
|
||||
result_file.write_text(json.dumps(result_json))
|
||||
|
||||
LOG.info(f"run written to {result_file}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--dry-run", action="store_true", help="Show plans without running them"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--offline", action="store_true", help="Run only offline benchmarks"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
missing = []
|
||||
for dep in _DEPS:
|
||||
if not shutil.which(dep):
|
||||
missing.append(dep)
|
||||
|
||||
if missing:
|
||||
LOG.error(
|
||||
f"Missing dependencies: {', '.join(missing)}. "
|
||||
"Please install them before running benchmarks."
|
||||
)
|
||||
|
||||
LOG.info("ensuring we have a benchable zizmor build")
|
||||
subprocess.run(
|
||||
["cargo", "build", "--release", "-p", "zizmor"],
|
||||
check=True,
|
||||
cwd=_PROJECT_ROOT,
|
||||
)
|
||||
|
||||
if not _ZIZMOR.is_file():
|
||||
LOG.error("zizmor build presumably failed, see above for details")
|
||||
|
||||
LOG.info(f"using cache dir: {_CACHE_DIR}")
|
||||
|
||||
benchmarks: list[Benchmark] = json.loads(_BENCHMARKS.read_text(encoding="utf-8"))
|
||||
LOG.info(f"found {len(benchmarks)} benchmarks in {_BENCHMARKS.name}")
|
||||
|
||||
if args.offline:
|
||||
benchmarks = [b for b in benchmarks if not b.get("online", False)]
|
||||
LOG.info(f"filtered to {len(benchmarks)} offline benchmarks")
|
||||
|
||||
benches = [Bench(benchmark) for benchmark in benchmarks]
|
||||
plans = []
|
||||
with LOG.scope("plan"):
|
||||
for bench in benches:
|
||||
with LOG.scope(bench.benchmark["name"]):
|
||||
LOG.info("beginning plan")
|
||||
plans.append(bench.plan())
|
||||
|
||||
with LOG.scope("run"):
|
||||
for bench, plan in zip(benches, plans):
|
||||
with LOG.scope(bench.benchmark["name"]):
|
||||
bench.run(plan, dry_run=args.dry_run)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "grafana-9f212d11d0ac",
|
||||
"source_type": "archive-url",
|
||||
"source": "https://github.com/grafana/grafana/archive/9f212d11d0ac9c38ada62a7db830844bb9b02905.zip",
|
||||
"source_sha256": "c6d42b52c8d912db2698d8b06f227de46f0c2d04cc757841792ed6567f0c56c7",
|
||||
"stencil": "$ZIZMOR --offline --format=plain --no-exit-codes --no-config $INPUTS"
|
||||
},
|
||||
{
|
||||
"name": "cpython-48f88310044c",
|
||||
"source_type": "archive-url",
|
||||
"source": "https://github.com/python/cpython/archive/48f88310044c6ef877f3b0761cf7afece2f8fb3a.zip",
|
||||
"source_sha256": "a52a67f1dd9cfa67c7d1305d5b9639629abe247b2c32f01b77f790ddf8b49503",
|
||||
"stencil": "$ZIZMOR --offline --format=plain --no-exit-codes --no-config $INPUTS"
|
||||
},
|
||||
{
|
||||
"name": "gha-hazmat-da3c3cd-online",
|
||||
"source_type": "archive-url",
|
||||
"source": "https://github.com/woodruffw/gha-hazmat/archive/da3c3cd.zip",
|
||||
"source_sha256": "f0aa224c5203218ad26e9f104d8dc3eaf8b322c97056add04d79f4a0d53c8f1f",
|
||||
"stencil": "$ZIZMOR --format=plain --no-exit-codes --no-config $INPUTS",
|
||||
"online": true
|
||||
}
|
||||
]
|
||||
12
bench/common.py
Normal file
12
bench/common.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
_HERE = Path(__file__).parent
|
||||
_ZIZMOR = _HERE.parent / "target" / "release" / "zizmor"
|
||||
|
||||
|
||||
def zizmor(args: list[str], *, check: bool = False) -> None:
|
||||
assert _ZIZMOR.is_file(), (
|
||||
f"zizmor binary not found at {_ZIZMOR}, run prepare() first"
|
||||
)
|
||||
subprocess.run([str(_ZIZMOR), *args], check=check)
|
||||
0
bench/conftest.py
Normal file
0
bench/conftest.py
Normal file
13
bench/test_bench_basic.py
Normal file
13
bench/test_bench_basic.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import pytest
|
||||
|
||||
from .common import zizmor
|
||||
|
||||
|
||||
@pytest.mark.benchmark
|
||||
def test_zizmor_startup():
|
||||
zizmor(["--version"])
|
||||
|
||||
|
||||
@pytest.mark.benchmark
|
||||
def test_zizmor_help():
|
||||
zizmor(["--help"])
|
||||
68
bench/test_bench_offline.py
Normal file
68
bench/test_bench_offline.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import io
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import urllib3
|
||||
|
||||
from bench.common import zizmor
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def grafana(tmp_path_factory) -> Path:
|
||||
archive = "https://github.com/grafana/grafana/archive/9f212d11d0ac9c38ada62a7db830844bb9b02905.zip"
|
||||
raw_zip = urllib3.PoolManager().request("GET", archive).data
|
||||
|
||||
path = tmp_path_factory.mktemp("grafana")
|
||||
|
||||
zipfile.ZipFile(io.BytesIO(raw_zip)).extractall(path)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def cpython(tmp_path_factory) -> Path:
|
||||
archive = "https://github.com/python/cpython/archive/48f88310044c6ef877f3b0761cf7afece2f8fb3a.zip"
|
||||
raw_zip = urllib3.PoolManager().request("GET", archive).data
|
||||
|
||||
path = tmp_path_factory.mktemp("cpython")
|
||||
|
||||
zipfile.ZipFile(io.BytesIO(raw_zip)).extractall(path)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
@pytest.mark.benchmark
|
||||
def test_zizmor_offline_grafana_9f212d11d0(grafana: Path):
|
||||
"""
|
||||
Runs `zizmor --offline --format=plain --no-exit-codes --no-config <path-to-grafana-source>`
|
||||
"""
|
||||
|
||||
zizmor(
|
||||
[
|
||||
"--offline",
|
||||
"--format=plain",
|
||||
"--no-exit-codes",
|
||||
"--no-config",
|
||||
str(grafana),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.benchmark
|
||||
def test_zizmor_offline_cpython_48f88310044c(cpython: Path):
|
||||
"""
|
||||
Runs `zizmor --offline --format=plain --no-exit-codes --no-config <path-to-cpython-source>`
|
||||
"""
|
||||
|
||||
zizmor(
|
||||
[
|
||||
"--offline",
|
||||
"--format=plain",
|
||||
"--no-exit-codes",
|
||||
"--no-config",
|
||||
str(cpython),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
47
bench/test_bench_online.py
Normal file
47
bench/test_bench_online.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from bench.common import zizmor
|
||||
|
||||
|
||||
@pytest.mark.skipif("GH_TOKEN" not in os.environ, reason="GH_TOKEN not set")
|
||||
def test_zizmor_online_gha_hazmat_da3c3cd(benchmark):
|
||||
"""
|
||||
Runs `zizmor --format=plain --no-exit-codes --no-config woodruffw/gha-hazmat@da3c3cd`
|
||||
"""
|
||||
|
||||
benchmark.pedantic(
|
||||
zizmor,
|
||||
args=(
|
||||
[
|
||||
"--format=plain",
|
||||
"--no-exit-codes",
|
||||
"--no-config",
|
||||
"woodruffw/gha-hazmat@da3c3cd",
|
||||
],
|
||||
),
|
||||
warmup_rounds=2,
|
||||
iterations=10,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif("GH_TOKEN" not in os.environ, reason="GH_TOKEN not set")
|
||||
def test_zizmor_online_cpython_48f88310044c(benchmark):
|
||||
"""
|
||||
Runs `zizmor --format=plain --no-exit-codes --no-config python/cpython@48f88310044c`
|
||||
"""
|
||||
|
||||
benchmark.pedantic(
|
||||
zizmor,
|
||||
args=(
|
||||
[
|
||||
"--format=plain",
|
||||
"--no-exit-codes",
|
||||
"--no-config",
|
||||
"python/cpython@48f88310044c",
|
||||
],
|
||||
),
|
||||
warmup_rounds=2,
|
||||
iterations=10,
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "github-actions-models"
|
||||
version = "0.41.0"
|
||||
version = "0.42.0"
|
||||
description = "Unofficial, high-quality data models for GitHub Actions workflows, actions, and related components"
|
||||
repository = "https://github.com/zizmorcore/zizmor/tree/main/crates/github-actions-models"
|
||||
keywords = ["github", "ci"]
|
||||
|
|
|
|||
|
|
@ -297,6 +297,12 @@ self_cell!(
|
|||
impl {Debug, PartialEq}
|
||||
);
|
||||
|
||||
impl Display for RepositoryUses {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.raw())
|
||||
}
|
||||
}
|
||||
|
||||
impl RepositoryUses {
|
||||
/// Parse a `uses: some/repo` clause.
|
||||
pub fn parse(uses: impl Into<String>) -> Result<Self, UsesError> {
|
||||
|
|
|
|||
|
|
@ -349,6 +349,8 @@ pub enum AllowDeny {
|
|||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum PackageEcosystem {
|
||||
/// `bazel`
|
||||
Bazel,
|
||||
/// `bun`
|
||||
Bun,
|
||||
/// `bundler`
|
||||
|
|
@ -369,6 +371,8 @@ pub enum PackageEcosystem {
|
|||
DotnetSdk,
|
||||
/// `helm`
|
||||
Helm,
|
||||
/// `julia`
|
||||
Julia,
|
||||
/// `elm`
|
||||
Elm,
|
||||
/// `gitsubmodule`
|
||||
|
|
@ -387,6 +391,8 @@ pub enum PackageEcosystem {
|
|||
Npm,
|
||||
/// `nuget`
|
||||
Nuget,
|
||||
/// `opentofu`
|
||||
Opentofu,
|
||||
/// `pip`
|
||||
Pip,
|
||||
/// `pub`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# https://github.com/zizmorcore/zizmor/issues/1451
|
||||
|
||||
version: 2
|
||||
enable-beta-ecosystems: true
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "opentofu"
|
||||
directories:
|
||||
- "/stack"
|
||||
- "/modules/default-branch-protection"
|
||||
schedule:
|
||||
interval: "cron"
|
||||
cronjob: "30 7 * * *"
|
||||
timezone: "Europe/London"
|
||||
target-branch: "main"
|
||||
groups:
|
||||
terraform:
|
||||
applies-to: "version-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "patch"
|
||||
- "minor"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "tree-sitter-iter"
|
||||
description = "A very simple pre-order iterator for tree-sitter CSTs"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
edition.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yamlpatch"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
description = "Comment and format-preserving YAML patch operations"
|
||||
repository = "https://github.com/zizmorcore/zizmor/tree/main/crates/yamlpatch"
|
||||
keywords = ["yaml", "patch"]
|
||||
|
|
|
|||
|
|
@ -903,21 +903,21 @@ normal:
|
|||
let end = find_content_end(&feature, &doc);
|
||||
|
||||
insta::assert_snapshot!(doc.source()[feature.location.byte_span.0..end], @r"
|
||||
bar: baz
|
||||
abc: def # comment
|
||||
");
|
||||
bar: baz
|
||||
abc: def # comment
|
||||
");
|
||||
|
||||
let feature = route_to_feature_exact(&route!("interior-spaces"), &doc)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let end = find_content_end(&feature, &doc);
|
||||
insta::assert_snapshot!(doc.source()[feature.location.byte_span.0..end], @r"
|
||||
- foo
|
||||
- foo
|
||||
|
||||
- bar
|
||||
# hello
|
||||
- baz # hello
|
||||
");
|
||||
- bar
|
||||
# hello
|
||||
- baz # hello
|
||||
");
|
||||
|
||||
let feature = route_to_feature_exact(&route!("normal"), &doc)
|
||||
.unwrap()
|
||||
|
|
@ -2775,11 +2775,12 @@ items:
|
|||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
items:
|
||||
- first
|
||||
- second
|
||||
- third
|
||||
");
|
||||
|
||||
items:
|
||||
- first
|
||||
- second
|
||||
- third
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2822,19 +2823,20 @@ databases:
|
|||
let result =
|
||||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r#"
|
||||
databases:
|
||||
- name: primary
|
||||
host: db1.example.com
|
||||
port: 5432
|
||||
max_connections: 100
|
||||
ssl: true
|
||||
readonly: false
|
||||
- name: analytics
|
||||
host: db2.example.com
|
||||
port: 5433
|
||||
readonly: true
|
||||
"#);
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
|
||||
databases:
|
||||
- name: primary
|
||||
host: db1.example.com
|
||||
port: 5432
|
||||
max_connections: 100
|
||||
ssl: true
|
||||
readonly: false
|
||||
- name: analytics
|
||||
host: db2.example.com
|
||||
port: 5433
|
||||
readonly: true
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2870,16 +2872,17 @@ jobs:
|
|||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r#"
|
||||
jobs:
|
||||
test:
|
||||
steps:
|
||||
- name: First step
|
||||
run: echo "first"
|
||||
- name: Second step
|
||||
run: echo "second"
|
||||
- name: Third step
|
||||
run: echo "third"
|
||||
"#);
|
||||
|
||||
jobs:
|
||||
test:
|
||||
steps:
|
||||
- name: First step
|
||||
run: echo "first"
|
||||
- name: Second step
|
||||
run: echo "second"
|
||||
- name: Third step
|
||||
run: echo "third"
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2924,20 +2927,21 @@ servers:
|
|||
assert!(result.source().contains("# Staging server"));
|
||||
assert!(result.source().contains("# internal only"));
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r#"
|
||||
servers:
|
||||
# Production server
|
||||
- name: prod
|
||||
host: prod.example.com
|
||||
port: 443
|
||||
# Staging server
|
||||
- name: staging
|
||||
host: staging.example.com # internal only
|
||||
port: 8443
|
||||
- name: dev
|
||||
host: localhost
|
||||
port: 8080
|
||||
"#);
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
|
||||
servers:
|
||||
# Production server
|
||||
- name: prod
|
||||
host: prod.example.com
|
||||
port: 443
|
||||
# Staging server
|
||||
- name: staging
|
||||
host: staging.example.com # internal only
|
||||
port: 8443
|
||||
- name: dev
|
||||
host: localhost
|
||||
port: 8080
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2959,11 +2963,12 @@ ports:
|
|||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
ports:
|
||||
- 8080
|
||||
- 8081
|
||||
- 8082
|
||||
");
|
||||
|
||||
ports:
|
||||
- 8080
|
||||
- 8081
|
||||
- 8082
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2985,11 +2990,12 @@ configs:
|
|||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
configs:
|
||||
- name: config1
|
||||
value: 123
|
||||
- {}
|
||||
");
|
||||
|
||||
configs:
|
||||
- name: config1
|
||||
value: 123
|
||||
- {}
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -3031,15 +3037,16 @@ services:
|
|||
let result =
|
||||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r#"
|
||||
services:
|
||||
- name: api
|
||||
port: 8080
|
||||
- name: worker
|
||||
port: 9090
|
||||
config:
|
||||
replicas: 3
|
||||
"#);
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
|
||||
services:
|
||||
- name: api
|
||||
port: 8080
|
||||
- name: worker
|
||||
port: 9090
|
||||
config:
|
||||
replicas: 3
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -3096,11 +3103,12 @@ tasks:
|
|||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
tasks:
|
||||
- task1
|
||||
- task2
|
||||
- task3
|
||||
");
|
||||
|
||||
tasks:
|
||||
- task1
|
||||
- task2
|
||||
- task3
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -3138,20 +3146,21 @@ jobs:
|
|||
let result =
|
||||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r#"
|
||||
name: CI
|
||||
on: push
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Run tests
|
||||
run: npm test
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
"#);
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
|
||||
name: CI
|
||||
on: push
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Run tests
|
||||
run: npm test
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -3176,9 +3185,10 @@ foo:
|
|||
apply_yaml_patches(&yamlpath::Document::new(original).unwrap(), &operations).unwrap();
|
||||
|
||||
insta::assert_snapshot!(result.source(), @r"
|
||||
foo:
|
||||
- abc
|
||||
- - def
|
||||
- ghi
|
||||
");
|
||||
|
||||
foo:
|
||||
- abc
|
||||
- - def
|
||||
- ghi
|
||||
");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yamlpath"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
description = "Format-preserving YAML feature extraction"
|
||||
repository = "https://github.com/zizmorcore/zizmor/tree/main/crates/yamlpath"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "zizmor"
|
||||
description = "Static analysis for GitHub Actions"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
repository = "https://github.com/zizmorcore/zizmor"
|
||||
documentation = "https://docs.zizmor.sh"
|
||||
keywords = ["cli", "github-actions", "static-analysis", "security"]
|
||||
|
|
@ -25,8 +25,6 @@ gh-token-tests = []
|
|||
online-tests = ["gh-token-tests"]
|
||||
# Test-only: enable tests that require `unbuffer` for TTY behavior.
|
||||
tty-tests = []
|
||||
# Test-only: enable tests that are typically very slow.
|
||||
slow-tests = []
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets.workspace = true
|
||||
|
|
|
|||
|
|
@ -524,6 +524,7 @@ jobs:
|
|||
|workflow: &Workflow, findings| {
|
||||
let fixed = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed.source(), @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -571,6 +572,7 @@ jobs:
|
|||
|workflow: &Workflow, findings| {
|
||||
let fixed = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed.source(), @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: push
|
||||
jobs:
|
||||
|
|
|
|||
|
|
@ -538,6 +538,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Workflow
|
||||
on:
|
||||
pull_request_target:
|
||||
|
|
@ -588,6 +589,7 @@ jobs:
|
|||
}
|
||||
}
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Workflow
|
||||
on:
|
||||
pull_request_target:
|
||||
|
|
@ -641,6 +643,7 @@ jobs:
|
|||
|
||||
// Verify it suggests comment.user.login for issue_comment events
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Issue Comment
|
||||
on: issue_comment
|
||||
|
||||
|
|
@ -690,6 +693,7 @@ jobs:
|
|||
|
||||
// Verify it suggests review.user.login for pull_request_review events
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test PR Review
|
||||
on: pull_request_review
|
||||
|
||||
|
|
@ -739,6 +743,7 @@ jobs:
|
|||
|
||||
// Verify it suggests issue.user.login for issues events
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Issues
|
||||
on: issues
|
||||
|
||||
|
|
@ -788,6 +793,7 @@ jobs:
|
|||
|
||||
// Verify it suggests release.author.login for release events
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Release
|
||||
on: release
|
||||
|
||||
|
|
@ -836,6 +842,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Create
|
||||
on: create
|
||||
|
||||
|
|
@ -885,6 +892,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test Workflow
|
||||
on:
|
||||
pull_request_target:
|
||||
|
|
|
|||
|
|
@ -555,6 +555,7 @@ jobs:
|
|||
|findings: Vec<Finding>| {
|
||||
let fixed_content = apply_fix_for_snapshot(workflow_content, findings);
|
||||
insta::assert_snapshot!(fixed_content, @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: release
|
||||
|
||||
|
|
@ -599,6 +600,7 @@ jobs:
|
|||
|findings: Vec<Finding>| {
|
||||
let fixed_content = apply_fix_for_snapshot(workflow_content, findings);
|
||||
insta::assert_snapshot!(fixed_content, @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: release
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ updates:
|
|||
let fix = &finding.fixes[0];
|
||||
let fixed_document = fix.apply(dependabot.as_document()).unwrap();
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -243,6 +244,7 @@ updates:
|
|||
let fix = &finding.fixes[0];
|
||||
let fixed_document = fix.apply(dependabot.as_document()).unwrap();
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -284,6 +286,7 @@ updates:
|
|||
let fix = &finding.fixes[0];
|
||||
let fixed_document = fix.apply(dependabot.as_document()).unwrap();
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -335,6 +338,7 @@ updates:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(document.source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -379,6 +383,7 @@ updates:
|
|||
|
||||
// Verify the document remains unchanged
|
||||
insta::assert_snapshot!(dependabot.as_document().source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ updates:
|
|||
let fix = &finding.fixes[0];
|
||||
let fixed_document = fix.apply(dependabot.as_document()).unwrap();
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -158,6 +159,7 @@ updates:
|
|||
|
||||
// Verify the document remains unchanged
|
||||
insta::assert_snapshot!(dependabot.as_document().source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -192,6 +194,7 @@ updates:
|
|||
|
||||
// Verify the document remains unchanged
|
||||
insta::assert_snapshot!(dependabot.as_document().source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
@ -240,6 +243,7 @@ updates:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(document.source(), @r"
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
static KNOWN_PERMISSIONS: LazyLock<HashMap<&str, Severity>> = LazyLock::new(|| {
|
||||
[
|
||||
("actions", Severity::High),
|
||||
("artifact-metadata", Severity::Medium),
|
||||
("attestations", Severity::High),
|
||||
("checks", Severity::Medium),
|
||||
("contents", Severity::High),
|
||||
|
|
@ -21,6 +22,8 @@ static KNOWN_PERMISSIONS: LazyLock<HashMap<&str, Severity>> = LazyLock::new(|| {
|
|||
("discussions", Severity::Medium),
|
||||
("id-token", Severity::High),
|
||||
("issues", Severity::High),
|
||||
// What does the write permission even do here?
|
||||
("models", Severity::Low),
|
||||
("packages", Severity::High),
|
||||
("pages", Severity::High),
|
||||
("pull-requests", Severity::High),
|
||||
|
|
|
|||
|
|
@ -88,20 +88,22 @@ const PWSH_REDIRECT_QUERY: &str = r#"
|
|||
|
||||
const PWSH_PIPELINE_QUERY: &str = r#"
|
||||
(pipeline
|
||||
(command
|
||||
command_name: (command_name) @cmd
|
||||
command_elements: (command_elements
|
||||
(_)*
|
||||
(array_literal_expression
|
||||
(unary_expression [
|
||||
(string_literal
|
||||
(expandable_string_literal (variable) @destination))
|
||||
(variable) @destination
|
||||
])
|
||||
)
|
||||
(_)*))
|
||||
(#match? @cmd "(?i)out-file|add-content|set-content|tee-object")
|
||||
(#match? @destination "(?i)ENV:GITHUB_ENV|ENV:GITHUB_PATH")
|
||||
(pipeline_chain
|
||||
(command
|
||||
command_name: (command_name) @cmd
|
||||
command_elements: (command_elements
|
||||
(_)*
|
||||
(array_literal_expression
|
||||
(unary_expression [
|
||||
(string_literal
|
||||
(expandable_string_literal (variable) @destination))
|
||||
(variable) @destination
|
||||
])
|
||||
)
|
||||
(_)*))
|
||||
(#match? @cmd "(?i)out-file|add-content|set-content|tee-object")
|
||||
(#match? @destination "(?i)ENV:GITHUB_ENV|ENV:GITHUB_PATH")
|
||||
)
|
||||
) @span
|
||||
"#;
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl ImpostorCommit {
|
|||
return Ok(false);
|
||||
};
|
||||
|
||||
// Fast path: almost all commit refs will be at the tip of
|
||||
// Fastest path: almost all commit refs will be at the tip of
|
||||
// the branch or tag's history, so check those first.
|
||||
// Check tags before branches, since in practice version tags
|
||||
// are more commonly pinned.
|
||||
|
|
@ -105,6 +105,21 @@ impl ImpostorCommit {
|
|||
}
|
||||
}
|
||||
|
||||
// Fast path: attempt to use GitHub's undocumented `branch_commits`
|
||||
// API to see if the commit is present in any branch/tag.
|
||||
// There are no stabilitiy guarantees for this API, so we fall back
|
||||
// to the slow(er) paths if it fails.
|
||||
match self
|
||||
.client
|
||||
.branch_commits(uses.owner(), uses.repo(), head_ref)
|
||||
.await
|
||||
{
|
||||
Ok(branch_commits) => return Ok(branch_commits.is_empty()),
|
||||
Err(e) => tracing::warn!("fast path impostor check failed for {uses}: {e}"),
|
||||
}
|
||||
|
||||
// Slow path: use GitHub's comparison API to check each branch and tag's
|
||||
// history for presence of the commit.
|
||||
for branch in &branches {
|
||||
if self
|
||||
.named_ref_contains_commit(uses, &format!("refs/heads/{}", &branch.name), head_ref)
|
||||
|
|
@ -400,6 +415,7 @@ jobs:
|
|||
// Apply the fix and snapshot test the result
|
||||
let new_doc = findings[0].fixes[0].apply(input.as_document()).unwrap();
|
||||
assert_snapshot!(new_doc.source(), @r"
|
||||
|
||||
name: Test Impostor Commit Fix
|
||||
on: push
|
||||
jobs:
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ jobs:
|
|||
assert!(fixed_document.source().contains("ANOTHER_VAR: also-keep"));
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
|
|
@ -349,6 +350,7 @@ jobs:
|
|||
assert!(fixed_document.source().contains("GLOBAL_VAR: keep-me"));
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
on: push
|
||||
|
||||
env:
|
||||
|
|
@ -403,6 +405,7 @@ jobs:
|
|||
assert!(fixed_document.source().contains("STEP_VAR: keep-me"));
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
|
|
@ -446,6 +449,7 @@ jobs:
|
|||
let fixed_document = fix.apply(workflow.as_document()).unwrap();
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ jobs:
|
|||
let fixed_document = fix.apply(workflow.as_document()).unwrap();
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
name: Test Vulnerable Actions
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -421,7 +422,8 @@ jobs:
|
|||
.unwrap();
|
||||
let fixed_document = fix.apply(workflow.as_document()).unwrap();
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
name: Test Node Setup
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -434,7 +436,7 @@ jobs:
|
|||
node-version: '18'
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
"#);
|
||||
");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -474,6 +476,7 @@ jobs:
|
|||
let fixed_document = fix.apply(workflow.as_document()).unwrap();
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
name: Test Third Party Action
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -549,7 +552,8 @@ jobs:
|
|||
.unwrap();
|
||||
current_document = fix_cache.apply(¤t_document).unwrap();
|
||||
|
||||
insta::assert_snapshot!(current_document.source(), @r#"
|
||||
insta::assert_snapshot!(current_document.source(), @r"
|
||||
|
||||
name: Test Multiple Vulnerable Actions
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -569,7 +573,7 @@ jobs:
|
|||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
"#);
|
||||
");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -607,6 +611,7 @@ jobs:
|
|||
let fixed_document = fix.apply(workflow.as_document()).unwrap();
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
name: Test Action with Subpath
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -655,7 +660,8 @@ jobs:
|
|||
.apply(workflow.as_document())
|
||||
.unwrap();
|
||||
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
name: Test First Patched Version Priority
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -664,7 +670,7 @@ jobs:
|
|||
steps:
|
||||
- name: Vulnerable action
|
||||
uses: actions/checkout@v3.1.0
|
||||
"#);
|
||||
");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -700,6 +706,7 @@ jobs:
|
|||
let new_doc = fix.apply(workflow.as_document()).unwrap();
|
||||
|
||||
assert_snapshot!(new_doc.source(), @r"
|
||||
|
||||
name: Test Non-Commit Ref
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -763,6 +770,7 @@ jobs:
|
|||
|
||||
let new_doc = findings[0].fixes[0].apply(input.as_document()).unwrap();
|
||||
assert_snapshot!(new_doc.source(), @r"
|
||||
|
||||
name: Test Commit Hash Pinning Real API
|
||||
on: push
|
||||
permissions: {}
|
||||
|
|
@ -819,6 +827,7 @@ jobs:
|
|||
|
||||
let new_doc = findings[0].fixes[0].apply(input.as_document()).unwrap();
|
||||
assert_snapshot!(new_doc.source(), @r"
|
||||
|
||||
name: Test Commit Hash Pinning Real API
|
||||
on: push
|
||||
permissions: {}
|
||||
|
|
|
|||
|
|
@ -407,7 +407,8 @@ jobs:
|
|||
"#;
|
||||
|
||||
let result = apply_fix_for_snapshot(workflow_content, "obfuscation").await;
|
||||
insta::assert_snapshot!(result, @r#"
|
||||
insta::assert_snapshot!(result, @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: push
|
||||
|
||||
|
|
@ -416,7 +417,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
"#);
|
||||
");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -433,7 +434,8 @@ jobs:
|
|||
"#;
|
||||
|
||||
let result = apply_fix_for_snapshot(workflow_content, "obfuscation").await;
|
||||
insta::assert_snapshot!(result, @r#"
|
||||
insta::assert_snapshot!(result, @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: push
|
||||
|
||||
|
|
@ -442,7 +444,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/codeql-action/init@v2
|
||||
"#);
|
||||
");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -459,7 +461,8 @@ jobs:
|
|||
"#;
|
||||
|
||||
let result = apply_fix_for_snapshot(workflow_content, "obfuscation").await;
|
||||
insta::assert_snapshot!(result, @r#"
|
||||
insta::assert_snapshot!(result, @r"
|
||||
|
||||
name: Test Workflow
|
||||
on: push
|
||||
|
||||
|
|
@ -468,6 +471,6 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/cache/save@v4
|
||||
"#);
|
||||
");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -707,6 +707,7 @@ jobs:
|
|||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -758,6 +759,7 @@ jobs:
|
|||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -811,6 +813,7 @@ jobs:
|
|||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -920,6 +923,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(current_document.source(), @r#"
|
||||
|
||||
name: Test Multiple Template Injections
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -986,6 +990,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(current_document.source(), @r#"
|
||||
|
||||
name: Test Duplicate Template Injections
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1046,6 +1051,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(current_document.source(), @r#"
|
||||
|
||||
name: Test Duplicate Template Injections
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1109,6 +1115,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(current_document.source(), @r#"
|
||||
|
||||
name: Test Duplicate Template Injections
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1225,6 +1232,7 @@ jobs:
|
|||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection - Bash
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1271,6 +1279,7 @@ jobs:
|
|||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection - Bash
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1316,7 +1325,8 @@ jobs:
|
|||
finding,
|
||||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
insta::assert_snapshot!(fixed_content.source(), @r"
|
||||
|
||||
name: Test Template Injection - CMD
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1326,7 +1336,7 @@ jobs:
|
|||
- name: Vulnerable step with cmd shell
|
||||
shell: cmd
|
||||
run: echo User is %GITHUB_ACTOR%
|
||||
"#);
|
||||
");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -1363,6 +1373,7 @@ jobs:
|
|||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection - PowerShell
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1409,6 +1420,7 @@ jobs:
|
|||
);
|
||||
// Ubuntu default shell is bash, so should use ${VAR} syntax
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection - Default Shell Ubuntu
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1454,6 +1466,7 @@ jobs:
|
|||
);
|
||||
// Windows default shell is pwsh, so should use $env:VAR syntax
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
|
||||
name: Test Template Injection - Default Shell Windows
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1498,7 +1511,8 @@ jobs:
|
|||
finding,
|
||||
"replace expression with environment variable",
|
||||
);
|
||||
insta::assert_snapshot!(fixed_content.source(), @r#"
|
||||
insta::assert_snapshot!(fixed_content.source(), @r"
|
||||
|
||||
name: Test Template Injection - CMD with Custom Env
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -1510,7 +1524,7 @@ jobs:
|
|||
run: echo PR title is %GITHUB_EVENT_PULL_REQUEST_TITLE%
|
||||
env:
|
||||
GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }}
|
||||
"#);
|
||||
");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -244,6 +244,7 @@ jobs:
|
|||
|
||||
let fixed_document = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
name: Test
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -283,6 +284,7 @@ jobs:
|
|||
|
||||
let fixed_document = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
name: Test
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -323,6 +325,7 @@ jobs:
|
|||
|
||||
let fixed_document = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
name: Test
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -367,6 +370,7 @@ jobs:
|
|||
|
||||
let fixed_document = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
|
||||
name: Test
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -406,7 +410,8 @@ jobs:
|
|||
assert_eq!(findings.len(), 1);
|
||||
|
||||
let fixed_document = apply_fix_for_snapshot(workflow.as_document(), findings);
|
||||
insta::assert_snapshot!(fixed_document.source(), @r#"
|
||||
insta::assert_snapshot!(fixed_document.source(), @r"
|
||||
|
||||
name: Test
|
||||
on: push
|
||||
jobs:
|
||||
|
|
@ -414,7 +419,7 @@ jobs:
|
|||
if: |-
|
||||
${{ github.event_name == 'pull_request' }}
|
||||
uses: ./.github/workflows/reusable.yml
|
||||
"#);
|
||||
");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -463,6 +468,7 @@ jobs:
|
|||
}
|
||||
|
||||
insta::assert_snapshot!(document.source(), @r#"
|
||||
|
||||
name: Test
|
||||
on: push
|
||||
jobs:
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ use crate::{
|
|||
registry::input::RepoSlug,
|
||||
};
|
||||
|
||||
const CONFIG_CANDIDATES: &[&str] = &[".github/zizmor.yml", "zizmor.yml"];
|
||||
const CONFIG_CANDIDATES: &[&str] = &[
|
||||
".github/zizmor.yml",
|
||||
".github/zizmor.yaml",
|
||||
"zizmor.yml",
|
||||
"zizmor.yaml",
|
||||
];
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("configuration error in {path}")]
|
||||
|
|
|
|||
|
|
@ -647,24 +647,28 @@
|
|||
},
|
||||
"package-ecosystem-values": {
|
||||
"enum": [
|
||||
"bazel",
|
||||
"bun",
|
||||
"bundler",
|
||||
"cargo",
|
||||
"composer",
|
||||
"conda",
|
||||
"devcontainers",
|
||||
"docker",
|
||||
"docker-compose",
|
||||
"dotnet-sdk",
|
||||
"elm",
|
||||
"gitsubmodule",
|
||||
"github-actions",
|
||||
"gitsubmodule",
|
||||
"gomod",
|
||||
"gradle",
|
||||
"helm",
|
||||
"julia",
|
||||
"maven",
|
||||
"mix",
|
||||
"npm",
|
||||
"nuget",
|
||||
"opentofu",
|
||||
"pip",
|
||||
"pub",
|
||||
"rust-toolchain",
|
||||
|
|
|
|||
|
|
@ -575,6 +575,29 @@ impl Client {
|
|||
.max_by_key(|t| t.name.len()))
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub(crate) async fn branch_commits(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
commit: &str,
|
||||
) -> Result<BranchCommits, ClientError> {
|
||||
// NOTE(ww): This API is undocumented.
|
||||
// See: https://github.com/orgs/community/discussions/78161
|
||||
let url = format!("https://github.com/{owner}/{repo}/branch_commits/{commit}");
|
||||
|
||||
// We ask GitHub for JSON, because it sends HTML by default for this endpoint.
|
||||
self.base_client
|
||||
.get(&url)
|
||||
.header(ACCEPT, "application/json")
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub(crate) async fn compare_commits(
|
||||
&self,
|
||||
|
|
@ -859,6 +882,23 @@ pub(crate) struct Commit {
|
|||
pub(crate) sha: String,
|
||||
}
|
||||
|
||||
/// The response structure from GitHub's undocumented `branch_commits` API.
|
||||
///
|
||||
/// This model is intentionally incomplete.
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[non_exhaustive]
|
||||
pub(crate) struct BranchCommits {
|
||||
branches: Vec<serde_json::Value>,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
impl BranchCommits {
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.branches.is_empty() && self.tags.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub(crate) enum ComparisonStatus {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::str::FromStr;
|
|||
|
||||
use camino::Utf8Path;
|
||||
use thiserror::Error;
|
||||
use tower_lsp_server::lsp_types::{self, TextDocumentSyncKind};
|
||||
use tower_lsp_server::ls_types::{self, TextDocumentSyncKind};
|
||||
use tower_lsp_server::{Client, LanguageServer, LspService, Server};
|
||||
|
||||
use crate::audit::AuditInput;
|
||||
|
|
@ -25,7 +25,7 @@ pub(crate) struct Error {
|
|||
}
|
||||
|
||||
struct LspDocumentCommon {
|
||||
uri: lsp_types::Uri,
|
||||
uri: ls_types::Uri,
|
||||
text: String,
|
||||
version: Option<i32>,
|
||||
}
|
||||
|
|
@ -39,35 +39,35 @@ struct Backend {
|
|||
impl LanguageServer for Backend {
|
||||
async fn initialize(
|
||||
&self,
|
||||
_: lsp_types::InitializeParams,
|
||||
) -> tower_lsp_server::jsonrpc::Result<lsp_types::InitializeResult> {
|
||||
Ok(lsp_types::InitializeResult {
|
||||
server_info: Some(lsp_types::ServerInfo {
|
||||
_: ls_types::InitializeParams,
|
||||
) -> tower_lsp_server::jsonrpc::Result<ls_types::InitializeResult> {
|
||||
Ok(ls_types::InitializeResult {
|
||||
server_info: Some(ls_types::ServerInfo {
|
||||
name: "zizmor (LSP)".into(),
|
||||
version: Some(env!("CARGO_PKG_VERSION").into()),
|
||||
}),
|
||||
capabilities: lsp_types::ServerCapabilities {
|
||||
text_document_sync: Some(lsp_types::TextDocumentSyncCapability::Kind(
|
||||
lsp_types::TextDocumentSyncKind::FULL,
|
||||
capabilities: ls_types::ServerCapabilities {
|
||||
text_document_sync: Some(ls_types::TextDocumentSyncCapability::Kind(
|
||||
ls_types::TextDocumentSyncKind::FULL,
|
||||
)),
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn initialized(&self, _: lsp_types::InitializedParams) {
|
||||
async fn initialized(&self, _: ls_types::InitializedParams) {
|
||||
let selectors = vec![
|
||||
lsp_types::DocumentFilter {
|
||||
ls_types::DocumentFilter {
|
||||
language: Some("yaml".into()),
|
||||
scheme: None,
|
||||
pattern: Some("**/.github/workflows/*.{yml,yaml}".into()),
|
||||
},
|
||||
lsp_types::DocumentFilter {
|
||||
ls_types::DocumentFilter {
|
||||
language: Some("yaml".into()),
|
||||
scheme: None,
|
||||
pattern: Some("**/action.{yml,yaml}".into()),
|
||||
},
|
||||
lsp_types::DocumentFilter {
|
||||
ls_types::DocumentFilter {
|
||||
language: Some("yaml".into()),
|
||||
scheme: None,
|
||||
pattern: Some("**/.github/dependabot.{yml,yaml}".into()),
|
||||
|
|
@ -80,46 +80,46 @@ impl LanguageServer for Backend {
|
|||
// neglects to.
|
||||
self.client
|
||||
.register_capability(vec![
|
||||
lsp_types::Registration {
|
||||
ls_types::Registration {
|
||||
id: "zizmor-didopen".into(),
|
||||
method: "textDocument/didOpen".into(),
|
||||
register_options: Some(
|
||||
serde_json::to_value(lsp_types::TextDocumentRegistrationOptions {
|
||||
serde_json::to_value(ls_types::TextDocumentRegistrationOptions {
|
||||
document_selector: Some(selectors.clone()),
|
||||
})
|
||||
.expect("failed to serialize LSP document registration options"),
|
||||
),
|
||||
},
|
||||
lsp_types::Registration {
|
||||
ls_types::Registration {
|
||||
id: "zizmor-didchange".into(),
|
||||
method: "textDocument/didChange".into(),
|
||||
register_options: Some(
|
||||
serde_json::to_value(lsp_types::TextDocumentChangeRegistrationOptions {
|
||||
serde_json::to_value(ls_types::TextDocumentChangeRegistrationOptions {
|
||||
document_selector: Some(selectors.clone()),
|
||||
sync_kind: TextDocumentSyncKind::FULL,
|
||||
})
|
||||
.expect("failed to serialize LSP document registration options"),
|
||||
),
|
||||
},
|
||||
lsp_types::Registration {
|
||||
ls_types::Registration {
|
||||
id: "zizmor-didsave".into(),
|
||||
method: "textDocument/didSave".into(),
|
||||
register_options: Some(
|
||||
serde_json::to_value(lsp_types::TextDocumentSaveRegistrationOptions {
|
||||
serde_json::to_value(ls_types::TextDocumentSaveRegistrationOptions {
|
||||
include_text: Some(true),
|
||||
text_document_registration_options:
|
||||
lsp_types::TextDocumentRegistrationOptions {
|
||||
ls_types::TextDocumentRegistrationOptions {
|
||||
document_selector: Some(selectors.clone()),
|
||||
},
|
||||
})
|
||||
.expect("failed to serialize LSP document registration options"),
|
||||
),
|
||||
},
|
||||
lsp_types::Registration {
|
||||
ls_types::Registration {
|
||||
id: "zizmor-didclose".into(),
|
||||
method: "textDocument/didClose".into(),
|
||||
register_options: Some(
|
||||
serde_json::to_value(lsp_types::TextDocumentRegistrationOptions {
|
||||
serde_json::to_value(ls_types::TextDocumentRegistrationOptions {
|
||||
document_selector: Some(selectors),
|
||||
})
|
||||
.expect("failed to serialize LSP document registration options"),
|
||||
|
|
@ -130,7 +130,7 @@ impl LanguageServer for Backend {
|
|||
.expect("failed to register text document capabilities with the LSP client");
|
||||
|
||||
self.client
|
||||
.log_message(lsp_types::MessageType::INFO, "server initialized!")
|
||||
.log_message(ls_types::MessageType::INFO, "server initialized!")
|
||||
.await;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ impl LanguageServer for Backend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn did_open(&self, params: lsp_types::DidOpenTextDocumentParams) {
|
||||
async fn did_open(&self, params: ls_types::DidOpenTextDocumentParams) {
|
||||
tracing::debug!("did_open: {:?}", params);
|
||||
self.audit(LspDocumentCommon {
|
||||
uri: params.text_document.uri,
|
||||
|
|
@ -149,7 +149,7 @@ impl LanguageServer for Backend {
|
|||
.await;
|
||||
}
|
||||
|
||||
async fn did_change(&self, params: lsp_types::DidChangeTextDocumentParams) {
|
||||
async fn did_change(&self, params: ls_types::DidChangeTextDocumentParams) {
|
||||
tracing::debug!("did_change: {:?}", params);
|
||||
let mut params = params;
|
||||
let Some(change) = params.content_changes.pop() else {
|
||||
|
|
@ -164,7 +164,7 @@ impl LanguageServer for Backend {
|
|||
.await;
|
||||
}
|
||||
|
||||
async fn did_save(&self, params: lsp_types::DidSaveTextDocumentParams) {
|
||||
async fn did_save(&self, params: ls_types::DidSaveTextDocumentParams) {
|
||||
tracing::debug!("did_save: {:?}", params);
|
||||
if let Some(text) = params.text {
|
||||
self.audit(LspDocumentCommon {
|
||||
|
|
@ -217,15 +217,15 @@ impl Backend {
|
|||
.iter()
|
||||
.map(|finding| {
|
||||
let primary = finding.primary_location();
|
||||
lsp_types::Diagnostic {
|
||||
range: lsp_types::Range {
|
||||
ls_types::Diagnostic {
|
||||
range: ls_types::Range {
|
||||
start: primary.concrete.location.start_point.into(),
|
||||
end: primary.concrete.location.end_point.into(),
|
||||
},
|
||||
severity: Some(finding.determinations.severity.into()),
|
||||
code: Some(lsp_types::NumberOrString::String(finding.ident.into())),
|
||||
code_description: Some(lsp_types::CodeDescription {
|
||||
href: lsp_types::Uri::from_str(finding.url)
|
||||
code: Some(ls_types::NumberOrString::String(finding.ident.into())),
|
||||
code_description: Some(ls_types::CodeDescription {
|
||||
href: ls_types::Uri::from_str(finding.url)
|
||||
.expect("finding contains an invalid URL somehow"),
|
||||
}),
|
||||
source: Some("zizmor".into()),
|
||||
|
|
@ -248,25 +248,25 @@ impl Backend {
|
|||
async fn audit(&self, params: LspDocumentCommon) {
|
||||
if let Err(e) = self.audit_inner(params).await {
|
||||
self.client
|
||||
.log_message(lsp_types::MessageType::ERROR, format!("audit failed: {e}"))
|
||||
.log_message(ls_types::MessageType::ERROR, format!("audit failed: {e}"))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Severity> for lsp_types::DiagnosticSeverity {
|
||||
impl From<Severity> for ls_types::DiagnosticSeverity {
|
||||
fn from(value: Severity) -> Self {
|
||||
// TODO: Does this mapping make sense?
|
||||
match value {
|
||||
Severity::Informational => lsp_types::DiagnosticSeverity::INFORMATION,
|
||||
Severity::Low => lsp_types::DiagnosticSeverity::WARNING,
|
||||
Severity::Medium => lsp_types::DiagnosticSeverity::WARNING,
|
||||
Severity::High => lsp_types::DiagnosticSeverity::ERROR,
|
||||
Severity::Informational => ls_types::DiagnosticSeverity::INFORMATION,
|
||||
Severity::Low => ls_types::DiagnosticSeverity::WARNING,
|
||||
Severity::Medium => ls_types::DiagnosticSeverity::WARNING,
|
||||
Severity::High => ls_types::DiagnosticSeverity::ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point> for lsp_types::Position {
|
||||
impl From<Point> for ls_types::Position {
|
||||
fn from(value: Point) -> Self {
|
||||
Self {
|
||||
line: value.row as u32,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,15 @@ struct App {
|
|||
#[arg(long, value_enum, default_value_t)]
|
||||
format: OutputFormat,
|
||||
|
||||
/// Whether to render OSC 8 links in the output.
|
||||
///
|
||||
/// This affects links under audit IDs, as well as any links
|
||||
/// produced by audit rules.
|
||||
///
|
||||
/// Only affects `--format=plain` (the default).
|
||||
#[arg(long, value_enum, default_value_t, env = "ZIZMOR_RENDER_LINKS")]
|
||||
render_links: CliRenderLinks,
|
||||
|
||||
/// Whether to render audit URLs in the output, separately from any URLs
|
||||
/// embedded in OSC 8 links.
|
||||
///
|
||||
|
|
@ -325,6 +334,44 @@ pub(crate) enum OutputFormat {
|
|||
Github,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, ValueEnum)]
|
||||
pub(crate) enum CliRenderLinks {
|
||||
/// Render OSC 8 links in output if support is detected.
|
||||
#[default]
|
||||
Auto,
|
||||
/// Always render OSC 8 links in output.
|
||||
Always,
|
||||
/// Never render OSC 8 links in output.
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum RenderLinks {
|
||||
Always,
|
||||
Never,
|
||||
}
|
||||
|
||||
impl From<CliRenderLinks> for RenderLinks {
|
||||
fn from(value: CliRenderLinks) -> Self {
|
||||
match value {
|
||||
CliRenderLinks::Auto => {
|
||||
// We render links if stdout is a terminal. This is assumed
|
||||
// to preclude CI environments and log files.
|
||||
//
|
||||
// TODO: Switch this to the support-hyperlinks crate?
|
||||
// See: https://github.com/zkat/supports-hyperlinks/pull/8
|
||||
if stdout().is_terminal() {
|
||||
RenderLinks::Always
|
||||
} else {
|
||||
RenderLinks::Never
|
||||
}
|
||||
}
|
||||
CliRenderLinks::Always => RenderLinks::Always,
|
||||
CliRenderLinks::Never => RenderLinks::Never,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, ValueEnum)]
|
||||
pub(crate) enum CliShowAuditUrls {
|
||||
/// Render audit URLs in output automatically based on output format and runtime context.
|
||||
|
|
@ -641,6 +688,7 @@ async fn run(app: &mut App) -> Result<ExitCode, Error> {
|
|||
ColorMode::Never
|
||||
} else if std::env::var("FORCE_COLOR").is_ok()
|
||||
|| std::env::var("CLICOLOR_FORCE").is_ok()
|
||||
|| utils::is_ci()
|
||||
{
|
||||
ColorMode::Always
|
||||
} else {
|
||||
|
|
@ -816,6 +864,7 @@ async fn run(app: &mut App) -> Result<ExitCode, Error> {
|
|||
®istry,
|
||||
&results,
|
||||
&app.show_audit_urls.into(),
|
||||
&app.render_links.into(),
|
||||
app.naches,
|
||||
),
|
||||
OutputFormat::Json | OutputFormat::JsonV1 => {
|
||||
|
|
@ -920,6 +969,16 @@ async fn main() -> ExitCode {
|
|||
Some(report)
|
||||
}
|
||||
Error::Collection(err) => match err.inner() {
|
||||
CollectionError::NoInputs => {
|
||||
let group = Group::with_title(Level::ERROR.primary_title(err.to_string()))
|
||||
.element(Level::HELP.message("collection yielded no auditable inputs"))
|
||||
.element(Level::HELP.message("inputs must contain at least one valid workflow, action, or Dependabot config"));
|
||||
|
||||
let renderer = Renderer::styled();
|
||||
let report = renderer.render(&[group]);
|
||||
|
||||
Some(report)
|
||||
}
|
||||
CollectionError::DuplicateInput(..) => {
|
||||
let group = Group::with_title(Level::ERROR.primary_title(err.to_string()))
|
||||
.element(Level::HELP.message(format!(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use anstream::{eprintln, print, println};
|
|||
use owo_colors::OwoColorize;
|
||||
|
||||
use crate::{
|
||||
ShowAuditUrls,
|
||||
RenderLinks, ShowAuditUrls,
|
||||
finding::{
|
||||
Finding, Severity,
|
||||
location::{Location, LocationKind},
|
||||
|
|
@ -44,6 +44,7 @@ impl From<&Severity> for Level<'_> {
|
|||
pub(crate) fn finding_snippets<'doc>(
|
||||
registry: &'doc InputRegistry,
|
||||
finding: &'doc Finding<'doc>,
|
||||
render_links_mode: &RenderLinks,
|
||||
) -> Vec<Snippet<'doc, Annotation<'doc>>> {
|
||||
// Our finding might span multiple workflows, so we need to group locations
|
||||
// by their enclosing workflow to generate each snippet correctly.
|
||||
|
|
@ -68,15 +69,20 @@ pub(crate) fn finding_snippets<'doc>(
|
|||
for (input_key, locations) in locations_by_workflow {
|
||||
let input = registry.get_input(input_key);
|
||||
|
||||
let path = match render_links_mode {
|
||||
RenderLinks::Always => input.link().unwrap_or(input_key.presentation_path()),
|
||||
RenderLinks::Never => input_key.presentation_path(),
|
||||
};
|
||||
|
||||
snippets.push(
|
||||
Snippet::source(input.as_document().source())
|
||||
.fold(true)
|
||||
.line_start(1)
|
||||
.path(input.link().unwrap_or(input_key.presentation_path()))
|
||||
.path(path)
|
||||
.annotations(locations.iter().map(|loc| {
|
||||
let annotation = match loc.symbolic.link {
|
||||
Some(ref link) => link,
|
||||
None => &loc.symbolic.annotation,
|
||||
let annotation = match (loc.symbolic.link.as_deref(), render_links_mode) {
|
||||
(Some(link), RenderLinks::Always) => link,
|
||||
_ => &loc.symbolic.annotation,
|
||||
};
|
||||
|
||||
AnnotationKind::from(loc.symbolic.kind)
|
||||
|
|
@ -96,10 +102,11 @@ pub(crate) fn render_findings(
|
|||
registry: &InputRegistry,
|
||||
findings: &FindingRegistry,
|
||||
show_urls_mode: &ShowAuditUrls,
|
||||
render_links_mode: &RenderLinks,
|
||||
naches_mode: bool,
|
||||
) {
|
||||
for finding in findings.findings() {
|
||||
render_finding(registry, finding, show_urls_mode);
|
||||
render_finding(registry, finding, show_urls_mode, render_links_mode);
|
||||
println!();
|
||||
}
|
||||
|
||||
|
|
@ -192,11 +199,19 @@ pub(crate) fn render_findings(
|
|||
}
|
||||
}
|
||||
|
||||
fn render_finding(registry: &InputRegistry, finding: &Finding, show_urls_mode: &ShowAuditUrls) {
|
||||
let title = Level::from(&finding.determinations.severity)
|
||||
fn render_finding(
|
||||
registry: &InputRegistry,
|
||||
finding: &Finding,
|
||||
show_urls_mode: &ShowAuditUrls,
|
||||
render_links_mode: &RenderLinks,
|
||||
) {
|
||||
let mut title = Level::from(&finding.determinations.severity)
|
||||
.primary_title(finding.desc)
|
||||
.id(finding.ident)
|
||||
.id_url(finding.url);
|
||||
.id(finding.ident);
|
||||
|
||||
if matches!(render_links_mode, RenderLinks::Always) {
|
||||
title = title.id_url(finding.url);
|
||||
}
|
||||
|
||||
let confidence = format!(
|
||||
"audit confidence → {:?}",
|
||||
|
|
@ -204,7 +219,7 @@ fn render_finding(registry: &InputRegistry, finding: &Finding, show_urls_mode: &
|
|||
);
|
||||
|
||||
let mut group = Group::with_title(title)
|
||||
.elements(finding_snippets(registry, finding))
|
||||
.elements(finding_snippets(registry, finding, render_links_mode))
|
||||
.element(Level::NOTE.message(confidence));
|
||||
|
||||
if let Some(tip) = &finding.tip {
|
||||
|
|
|
|||
|
|
@ -4,12 +4,8 @@ use anyhow::{Error, anyhow};
|
|||
use camino::Utf8Path;
|
||||
use github_actions_expressions::context::{Context, ContextPattern};
|
||||
use github_actions_models::common::{Env, expr::LoE};
|
||||
use jsonschema::{
|
||||
BasicOutput::{Invalid, Valid},
|
||||
Validator,
|
||||
output::{ErrorDescription, OutputUnit},
|
||||
validator_for,
|
||||
};
|
||||
use jsonschema::ErrorEntry;
|
||||
use jsonschema::{Validator, validator_for};
|
||||
use std::ops::{Deref, Range};
|
||||
use std::{fmt::Write, sync::LazyLock};
|
||||
|
||||
|
|
@ -307,11 +303,11 @@ pub(crate) static DEFAULT_ENVIRONMENT_VARIABLES: &[(
|
|||
),
|
||||
];
|
||||
|
||||
fn parse_validation_errors(errors: Vec<OutputUnit<ErrorDescription>>) -> Error {
|
||||
fn parse_validation_errors(errors: Vec<ErrorEntry<'_>>) -> Error {
|
||||
let mut message = String::new();
|
||||
|
||||
for error in errors {
|
||||
let description = error.error_description().to_string();
|
||||
let description = error.error.to_string();
|
||||
// HACK: error descriptions are sometimes a long rats' nest
|
||||
// of JSON objects. We should render this in a palatable way
|
||||
// but doing so is nontrivial, so we just skip them for now.
|
||||
|
|
@ -319,7 +315,7 @@ fn parse_validation_errors(errors: Vec<OutputUnit<ErrorDescription>>) -> Error {
|
|||
// the error for an unmatched "oneOf", so these errors are
|
||||
// typically less useful anyways.
|
||||
if !description.starts_with("{") {
|
||||
let location = error.instance_location().as_str();
|
||||
let location = error.instance_location.as_str();
|
||||
if location.is_empty() {
|
||||
writeln!(message, "{description}").expect("I/O on a String failed");
|
||||
} else {
|
||||
|
|
@ -379,18 +375,19 @@ where
|
|||
// 2. The input is semantically invalid, and the user
|
||||
// needs to fix it.
|
||||
// We the JSON schema `validator` to separate these.
|
||||
Ok(raw_value) => match validator
|
||||
.apply(
|
||||
Ok(raw_value) => {
|
||||
let evaluation = validator.evaluate(
|
||||
&serde_json::to_value(&raw_value)
|
||||
.map_err(|e| CollectionError::Syntax(e.into()))?,
|
||||
)
|
||||
.basic()
|
||||
{
|
||||
Valid(_) => Err(e.into()),
|
||||
Invalid(errors) => {
|
||||
);
|
||||
|
||||
if evaluation.flag().valid {
|
||||
Err(e.into())
|
||||
} else {
|
||||
let errors = evaluation.iter_errors().collect::<Vec<_>>();
|
||||
Err(CollectionError::Schema(parse_validation_errors(errors)))
|
||||
}
|
||||
},
|
||||
}
|
||||
// Syntax error.
|
||||
Err(e) => Err(CollectionError::Syntax(e.into())),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ fn test_regular_persona() -> anyhow::Result<()> {
|
|||
zizmor()
|
||||
.input(input_under_test("anonymous-definition.yml"))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (2 suppressed)"
|
||||
@"No findings to report. Good job! (2 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -80,28 +80,28 @@ fn test_jobs_missing_no_cancel() -> anyhow::Result<()> {
|
|||
.args(["--persona=pedantic"])
|
||||
.run()?,
|
||||
@r"
|
||||
help[concurrency-limits]: insufficient job-level concurrency limits
|
||||
--> @@INPUT@@:9:5
|
||||
|
|
||||
9 | concurrency: group
|
||||
| ^^^^^^^^^^^^^^^^^^ job concurrency is missing cancel-in-progress
|
||||
|
|
||||
= note: audit confidence → High
|
||||
help[concurrency-limits]: insufficient job-level concurrency limits
|
||||
--> @@INPUT@@:9:5
|
||||
|
|
||||
9 | concurrency: group
|
||||
| ^^^^^^^^^^^^^^^^^^ job concurrency is missing cancel-in-progress
|
||||
|
|
||||
= note: audit confidence → High
|
||||
|
||||
help[concurrency-limits]: insufficient job-level concurrency limits
|
||||
--> @@INPUT@@:1:1
|
||||
|
|
||||
1 | / name: Workflow with job 1 missing cancel-in-progress and job 2 missing concurrency
|
||||
2 | | on: push
|
||||
3 | | permissions: {}
|
||||
... |
|
||||
17 | | - name: 2-ok
|
||||
18 | | run: echo ok
|
||||
| |___________________^ missing concurrency setting
|
||||
|
|
||||
= note: audit confidence → High
|
||||
help[concurrency-limits]: insufficient job-level concurrency limits
|
||||
--> @@INPUT@@:1:1
|
||||
|
|
||||
1 | / name: Workflow with job 1 missing cancel-in-progress and job 2 missing concurrency
|
||||
2 | | on: push
|
||||
3 | | permissions: {}
|
||||
... |
|
||||
17 | | - name: 2-ok
|
||||
18 | | run: echo ok
|
||||
| |___________________^ missing concurrency setting
|
||||
|
|
||||
= note: audit confidence → High
|
||||
|
||||
2 findings: 0 informational, 2 low, 0 medium, 0 high
|
||||
2 findings: 0 informational, 2 low, 0 medium, 0 high
|
||||
"
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ fn test_config_short_cooldown_permitted() -> anyhow::Result<()> {
|
|||
.input(input_under_test("dependabot-cooldown/default-days-too-short/dependabot.yml"))
|
||||
.config(input_under_test("dependabot-cooldown/configs/cooldown-one-day.yml"))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job!"
|
||||
@"No findings to report. Good job!"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
use crate::common::{input_under_test, zizmor};
|
||||
|
||||
#[cfg_attr(
|
||||
any(not(feature = "gh-token-tests"), not(feature = "slow-tests")),
|
||||
ignore
|
||||
)]
|
||||
#[cfg_attr(not(feature = "gh-token-tests"), ignore)]
|
||||
#[test]
|
||||
fn test_regular_persona() -> anyhow::Result<()> {
|
||||
insta::assert_snapshot!(
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ fn test_issue_518_repro() -> Result<()> {
|
|||
.input(input_under_test("ref-confusion/issue-518-repro.yml"))
|
||||
.offline(false)
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (1 ignored, 1 suppressed)"
|
||||
@"No findings to report. Good job! (1 ignored, 1 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ fn test_nested_annotated_tags() -> Result<()> {
|
|||
"ref-version-mismatch/nested-annotated-tags.yml"
|
||||
))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (1 suppressed)"
|
||||
@"No findings to report. Good job! (1 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ fn test_self_hosted_default() -> Result<()> {
|
|||
zizmor()
|
||||
.input(input_under_test("self-hosted.yml"))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (1 suppressed)"
|
||||
@"No findings to report. Good job! (1 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ fn test_issue_418_repro() -> Result<()> {
|
|||
zizmor()
|
||||
.input(input_under_test("template-injection/issue-418-repro.yml"))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (3 suppressed)"
|
||||
@"No findings to report. Good job! (3 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ fn test_undocumented_permissions_default() -> Result<()> {
|
|||
zizmor()
|
||||
.input(input_under_test("undocumented-permissions.yml"))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (5 suppressed)"
|
||||
@"No findings to report. Good job! (5 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -78,7 +78,7 @@ fn test_documented_permissions_pedantic() -> Result<()> {
|
|||
.input(input_under_test("undocumented-permissions/documented.yml"))
|
||||
.args(["--persona=pedantic"])
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (1 ignored)"
|
||||
@"No findings to report. Good job! (1 ignored)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -94,7 +94,7 @@ fn test_contents_read_only_pedantic() -> Result<()> {
|
|||
))
|
||||
.args(["--persona=pedantic"])
|
||||
.run()?,
|
||||
@r"No findings to report. Good job!"
|
||||
@"No findings to report. Good job!"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -110,7 +110,7 @@ fn test_empty_permissions_pedantic() -> Result<()> {
|
|||
))
|
||||
.args(["--persona=pedantic"])
|
||||
.run()?,
|
||||
@r"No findings to report. Good job!"
|
||||
@"No findings to report. Good job!"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ fn test_issue_659_repro() -> Result<()> {
|
|||
.input(input_under_test("unpinned-uses/issue-659-repro.yml"))
|
||||
.args(["--pedantic"])
|
||||
.run()?,
|
||||
@r"No findings to report. Good job!"
|
||||
@"No findings to report. Good job!"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ fn test_issue_1191_repro() -> Result<()> {
|
|||
"use-trusted-publishing/issue-1191-repro.yml"
|
||||
))
|
||||
.run()?,
|
||||
@r"No findings to report. Good job! (3 suppressed)"
|
||||
@"No findings to report. Good job! (3 suppressed)"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ pub struct Zizmor {
|
|||
stdin: Option<String>,
|
||||
unbuffer: bool,
|
||||
offline: bool,
|
||||
gh_token: bool,
|
||||
inputs: Vec<String>,
|
||||
config: Option<String>,
|
||||
no_config: bool,
|
||||
|
|
@ -53,13 +54,19 @@ pub struct Zizmor {
|
|||
impl Zizmor {
|
||||
/// Create a new zizmor runner.
|
||||
pub fn new() -> Self {
|
||||
let cmd = Command::new(cargo::cargo_bin!());
|
||||
let mut cmd = Command::new(cargo::cargo_bin!());
|
||||
|
||||
// Our child `zizmor` process starts with a clean environment, to
|
||||
// ensure we explicitly test interactions with things like `CI`
|
||||
// and `GH_TOKEN`.
|
||||
cmd.env_clear();
|
||||
|
||||
Self {
|
||||
cmd,
|
||||
stdin: None,
|
||||
unbuffer: false,
|
||||
offline: true,
|
||||
gh_token: true,
|
||||
inputs: vec![],
|
||||
config: None,
|
||||
no_config: false,
|
||||
|
|
@ -84,11 +91,6 @@ impl Zizmor {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn unsetenv(mut self, key: &str) -> Self {
|
||||
self.cmd.env_remove(key);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn input(mut self, input: impl Into<String>) -> Self {
|
||||
self.inputs.push(input.into());
|
||||
self
|
||||
|
|
@ -114,6 +116,11 @@ impl Zizmor {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn gh_token(mut self, flag: bool) -> Self {
|
||||
self.gh_token = flag;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn output(mut self, output: OutputMode) -> Self {
|
||||
self.output = output;
|
||||
self
|
||||
|
|
@ -147,7 +154,12 @@ impl Zizmor {
|
|||
} else {
|
||||
// If we're running in online mode, we pre-assert the
|
||||
// presence of GH_TOKEN to make configuration failures more obvious.
|
||||
std::env::var("GH_TOKEN").context("online tests require GH_TOKEN to be set")?;
|
||||
let token =
|
||||
std::env::var("GH_TOKEN").context("online tests require GH_TOKEN to be set")?;
|
||||
|
||||
if self.gh_token {
|
||||
self.cmd.env("GH_TOKEN", token);
|
||||
}
|
||||
}
|
||||
|
||||
if self.no_config && self.config.is_some() {
|
||||
|
|
|
|||
|
|
@ -164,6 +164,31 @@ fn test_discovers_config_in_dotgithub() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures we correctly discover a `zizmor.yaml` configuration file in a `.github`
|
||||
/// subdirectory of a given input directory, i.e.
|
||||
/// `config-in-dotgithub/.github/zizmor.yaml` in this case.
|
||||
///
|
||||
/// This tests that both `.yml` and `.yaml` extensions are supported.
|
||||
#[test]
|
||||
fn test_discovers_dotyaml_config_in_dotgithub() -> anyhow::Result<()> {
|
||||
insta::assert_snapshot!(
|
||||
zizmor()
|
||||
.input(input_under_test("config-scenarios/dotyaml-config-in-dotgithub"))
|
||||
.setenv("RUST_LOG", "zizmor::config=debug")
|
||||
.output(OutputMode::Both)
|
||||
.run()?,
|
||||
@r"
|
||||
🌈 zizmor v@@VERSION@@
|
||||
DEBUG zizmor::config: discovering config for local input `@@INPUT@@`
|
||||
DEBUG zizmor::config: attempting config discovery in `@@INPUT@@`
|
||||
DEBUG zizmor::config: found config candidate at `@@INPUT@@/.github/zizmor.yaml`
|
||||
No findings to report. Good job! (1 ignored, 2 suppressed)
|
||||
",
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures we correctly discover a configuration file in a `.github`
|
||||
/// subdirectory from an input filename, i.e. going from
|
||||
/// `config-in-dotgithub/.github/workflows/hackme.yml`
|
||||
|
|
|
|||
|
|
@ -83,13 +83,21 @@ fn menagerie() -> Result<()> {
|
|||
|
||||
#[test]
|
||||
fn color_control_basic() -> Result<()> {
|
||||
// No terminal, so no color by default.
|
||||
// No terminal and not CI, so no color by default.
|
||||
let no_color_default_output = zizmor()
|
||||
.output(OutputMode::Both)
|
||||
.input(input_under_test("e2e-menagerie"))
|
||||
.run()?;
|
||||
assert!(!no_color_default_output.contains("\x1b["));
|
||||
|
||||
// No terminal but CI, so color by default.
|
||||
let color_default_ci_output = zizmor()
|
||||
.setenv("CI", "true")
|
||||
.output(OutputMode::Both)
|
||||
.input(input_under_test("e2e-menagerie"))
|
||||
.run()?;
|
||||
assert!(color_default_ci_output.contains("\x1b["));
|
||||
|
||||
// Force color via --color=always.
|
||||
let forced_color_via_arg_output = zizmor()
|
||||
.output(OutputMode::Both)
|
||||
|
|
@ -251,6 +259,25 @@ fn invalid_inputs() -> Result<()> {
|
|||
);
|
||||
}
|
||||
|
||||
insta::assert_snapshot!(
|
||||
zizmor()
|
||||
.expects_failure(true)
|
||||
.input(input_under_test("invalid/empty/"))
|
||||
.args(["--strict-collection"])
|
||||
.run()?,
|
||||
@r"
|
||||
🌈 zizmor v@@VERSION@@
|
||||
fatal: no audit was performed
|
||||
error: no inputs collected
|
||||
|
|
||||
= help: collection yielded no auditable inputs
|
||||
= help: inputs must contain at least one valid workflow, action, or Dependabot config
|
||||
|
||||
Caused by:
|
||||
no inputs collected
|
||||
"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +318,13 @@ fn test_issue_1394() -> Result<()> {
|
|||
🌈 zizmor v@@VERSION@@
|
||||
WARN collect_inputs: zizmor::registry::input: failed to parse input: jobs.demo.steps[0]: duplicate entry with key "env" at line 10 column 9
|
||||
fatal: no audit was performed
|
||||
no inputs collected
|
||||
error: no inputs collected
|
||||
|
|
||||
= help: collection yielded no auditable inputs
|
||||
= help: inputs must contain at least one valid workflow, action, or Dependabot config
|
||||
|
||||
Caused by:
|
||||
no inputs collected
|
||||
"#
|
||||
);
|
||||
|
||||
|
|
@ -575,7 +608,6 @@ fn test_cant_retrieve_offline() -> Result<()> {
|
|||
zizmor()
|
||||
.expects_failure(true)
|
||||
.offline(true)
|
||||
.unsetenv("GH_TOKEN")
|
||||
.args(["pypa/sampleproject"])
|
||||
.run()?,
|
||||
@r"
|
||||
|
|
@ -601,7 +633,7 @@ fn test_cant_retrieve_no_gh_token() -> Result<()> {
|
|||
zizmor()
|
||||
.expects_failure(true)
|
||||
.offline(false)
|
||||
.unsetenv("GH_TOKEN")
|
||||
.gh_token(false)
|
||||
.args(["pypa/sampleproject"])
|
||||
.run()?,
|
||||
@r"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,10 @@ expression: "zizmor().expects_failure(true).input(input_under_test(&format!(\"in
|
|||
🌈 zizmor v@@VERSION@@
|
||||
WARN collect_inputs: zizmor::registry::input: failed to validate input as action: input does not match expected validation schema
|
||||
fatal: no audit was performed
|
||||
no inputs collected
|
||||
error: no inputs collected
|
||||
|
|
||||
= help: collection yielded no auditable inputs
|
||||
= help: inputs must contain at least one valid workflow, action, or Dependabot config
|
||||
|
||||
Caused by:
|
||||
no inputs collected
|
||||
|
|
|
|||
|
|
@ -5,4 +5,10 @@ expression: "zizmor().expects_failure(true).input(input_under_test(&format!(\"in
|
|||
🌈 zizmor v@@VERSION@@
|
||||
WARN collect_inputs: zizmor::registry::input: failed to validate input as workflow: input does not match expected validation schema
|
||||
fatal: no audit was performed
|
||||
no inputs collected
|
||||
error: no inputs collected
|
||||
|
|
||||
= help: collection yielded no auditable inputs
|
||||
= help: inputs must contain at least one valid workflow, action, or Dependabot config
|
||||
|
||||
Caused by:
|
||||
no inputs collected
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ failed to load file://@@INPUT@@ as workflow
|
|||
|
||||
Caused by:
|
||||
0: input does not match expected validation schema
|
||||
1: on.workflow_call.inputs.input: "type" is a required property
|
||||
Additional properties are not allowed ('boom' was unexpected)
|
||||
1: Additional properties are not allowed ('boom' was unexpected)
|
||||
on.workflow_call.inputs.input: "type" is a required property
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
name: hackme
|
||||
on:
|
||||
issues:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
inject-me:
|
||||
name: inject-me
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # tag=v7.0.1
|
||||
with:
|
||||
script: |
|
||||
return "doing a thing: ${{ github.event.issue.title }}"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
rules:
|
||||
template-injection:
|
||||
ignore:
|
||||
- hackme.yml
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# empty
|
||||
|
||||
This is an empty directory, except for this README file.
|
||||
|
|
@ -519,7 +519,7 @@ In general, you should enable `cooldown` for all updaters.
|
|||
Detects usages of `insecure-external-code-execution` in Dependabot configuration
|
||||
files.
|
||||
|
||||
By default, Dependabot does not execution code from dependency manifests
|
||||
By default, Dependabot does not execute code from dependency manifests
|
||||
during updates. However, users can opt in to this behavior by setting
|
||||
`#!yaml insecure-external-code-execution: allow` in their Dependabot
|
||||
configuration.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ description: zizmor's configuration file and configurable behaviors.
|
|||
Configuration support was added in `v0.2.0`.
|
||||
|
||||
`zizmor` supports a small amount of configuration via [YAML] config files,
|
||||
typically named `zizmor.yml`.
|
||||
typically named `zizmor.yml` or `zizmor.yaml`.
|
||||
|
||||
[YAML]: https://learnxinyminutes.com/docs/yaml/
|
||||
|
||||
|
|
@ -41,9 +41,25 @@ typically named `zizmor.yml`.
|
|||
* File inputs (e.g. `zizmor path/to/workflow.yml`): `zizmor` performs
|
||||
directory discovery starting in the directory containing the given file.
|
||||
|
||||
* Directory inputs (e.g. `zizmor .`): `zizmor` looks for a `zizmor.yml` or
|
||||
`.github/zizmor.yml` in the given directory or any parent, up to the
|
||||
filesystem root or the first `.git` directory.
|
||||
* Directory inputs (e.g. `zizmor .`): `zizmor` looks for a `zizmor.yml`
|
||||
or `zizmor.yaml` file in the given directory, the `.github` child directory,
|
||||
or any parent, up to the filesystem root or the first `.git` directory.
|
||||
|
||||
!!! example
|
||||
|
||||
Given an invocation like `zizmor ./repo/`, `zizmor` will attempt
|
||||
to discover configuration files in the following order:
|
||||
|
||||
1. `./repo/.github/zizmor.yml`
|
||||
2. `./repo/.github/zizmor.yaml`
|
||||
3. `./repo/zizmor.yml`
|
||||
4. `./repo/zizmor.yaml`
|
||||
5. `./repo/../.github/zizmor.yml`
|
||||
6. `./repo/../.github/zizmor.yaml`
|
||||
7. ...and so on, until the filesystem root or a `.git/` directory is found.
|
||||
|
||||
|
||||
|
||||
|
||||
!!! note
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ See [insta's documentation] for more details.
|
|||
|
||||
## Benchmarking
|
||||
|
||||
`zizmor` currently uses [hyperfine](https://github.org/sharkdp/hyperfine)
|
||||
`zizmor` currently uses [pytest-codspeed](https://github.com/CodSpeedHQ/pytest-codspeed)
|
||||
for command-line benchmarking.
|
||||
|
||||
Benchmarks are stored in the top-level `bench/` directory, and can be
|
||||
|
|
@ -184,27 +184,22 @@ make bench
|
|||
```
|
||||
|
||||
We currently run offline benchmarks in the CI and report their results
|
||||
to [Bencher](https://bencher.dev/). See
|
||||
[our project page](https://bencher.dev/console/projects/zizmor/plots)
|
||||
on Bencher for results and trends.
|
||||
to [CodSpeed](https://codspeed.io). See
|
||||
[our project page](https://codspeed.io/zizmorcore/zizmor)
|
||||
on CodSpeed for results and trends.
|
||||
|
||||
There are also online benchmarks, but these don't get run automatically.
|
||||
To run them, you can pass `GH_TOKEN` to the `bench/benchmark.py` script
|
||||
directly:
|
||||
To run them, you can set `GH_TOKEN`:
|
||||
|
||||
```bash
|
||||
GH_TOKEN=$(gh auth token) uv run bench/benchmark.py
|
||||
GH_TOKEN=$(gh auth token) make bench
|
||||
```
|
||||
|
||||
### Adding new benchmarks
|
||||
|
||||
`zizmor` currently orchestrates benchmarks with `bench/benchmark.py`,
|
||||
which wraps `hyperfine` to add a planning phase.
|
||||
Benchmarks are currently written as pytest functions.
|
||||
|
||||
Take a look at `bench/benchmarks.json` for the current benchmarks.
|
||||
Observe that each benchmark tells `benchmark.py` how to retrieve its
|
||||
input as well as provides a `stencil` that the benchmark runner will
|
||||
expand to run the benchmark.
|
||||
Take a look at `bench/test_*.py` for existing benchmarks.
|
||||
|
||||
## Building the website
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ jobs:
|
|||
actions: read # Only needed for private repos. Needed for upload-sarif to read workflow run info.
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0
|
||||
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
|
||||
```
|
||||
|
||||
See the action's [`inputs` documentation][inputs-documentation] for
|
||||
|
|
@ -94,12 +94,12 @@ GitHub Actions setup:
|
|||
actions: read # Only needed for private repos. Needed for upload-sarif to read workflow run info.
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v7.1.5
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format=sarif . > results.sarif # (2)!
|
||||
|
|
@ -107,7 +107,7 @@ GitHub Actions setup:
|
|||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # (1)!
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
|
@ -164,10 +164,10 @@ GitHub Actions setup:
|
|||
contents: read # Only needed for private repos. Needed to clone the repo.
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v7.1.5
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format=github . # (2)!
|
||||
|
|
@ -255,7 +255,7 @@ To do so, add the following to your `.pre-commit-config.yaml` `#!yaml repos:` se
|
|||
|
||||
```yaml
|
||||
- repo: https://github.com/zizmorcore/zizmor-pre-commit
|
||||
rev: v1.18.0 # (1)!
|
||||
rev: v1.19.0 # (1)!
|
||||
hooks:
|
||||
- id: zizmor
|
||||
```
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@ of `zizmor`.
|
|||
|
||||
## Next (UNRELEASED)
|
||||
|
||||
### Enhancements 🌱
|
||||
|
||||
* The [excessive-permissions] audit is now aware of the `artifact-metadata`
|
||||
and `models` permissions (#1461)
|
||||
|
||||
## 1.19.0
|
||||
|
||||
### New Features 🌈
|
||||
|
||||
* **New audit**: [archived-uses] detects usages of archived repositories in
|
||||
|
|
@ -30,12 +37,37 @@ of `zizmor`.
|
|||
|
||||
* zizmor now produces more useful and less ambiguous spans for many findings,
|
||||
particularly those from the [anonymous-definition] audit (#1416)
|
||||
|
||||
* zizmor now discovers configuration files named `zizmor.yaml`, in addition
|
||||
to `zizmor.yml` (#1431)
|
||||
|
||||
* zizmor now produces a more useful error message when input collection
|
||||
yields no inputs (#1439)
|
||||
|
||||
* The `--render-links` flag now allows users to control `zizmor`'s OSC 8 terminal
|
||||
link rendering behavior. This is particularly useful in environments that
|
||||
advertise themselves as terminals but fail to correctly render or ignore
|
||||
OSC 8 links (#1454)
|
||||
|
||||
### Performance Improvements 🚄
|
||||
|
||||
* The [impostor-commit] audit is now significantly faster on true positives,
|
||||
making true positive detection virtually as fast as true negative detection.
|
||||
In practice, true positive runs are over 100 times faster than before
|
||||
(#1429)
|
||||
|
||||
### Bug Fixes 🐛
|
||||
|
||||
* Fixed a bug where the [obfuscation] audit would crash if it encountered
|
||||
a CMD shell that was defined outside of the current step block (i.e.
|
||||
as a job or workflow default) (#1418)
|
||||
|
||||
* Fixed a bug where the `opentofu` ecosystem was not recognized in
|
||||
Dependabot configuration files (#1452)
|
||||
|
||||
* `--color=always` no longer implies `--render-links=always`, as some
|
||||
environments (like GitHub Actions) support ANSI color codes but fail
|
||||
to handle OSC escapes gracefully (#1454)
|
||||
|
||||
## 1.18.0
|
||||
|
||||
|
|
@ -1173,7 +1205,7 @@ This is one of `zizmor`'s bigger recent releases! Key enhancements include:
|
|||
|
||||
### What's Changed
|
||||
* fix(cli): remove '0 ignored' from another place by @woodruffw in #157
|
||||
* perf: speed up impostor-commit's fast path by @woodruffw in #158
|
||||
* perf: speed up [impostor-commit]'s fast path by @woodruffw in #158
|
||||
* fix(cli): fixup error printing by @woodruffw in #159
|
||||
|
||||
## v0.3.1
|
||||
|
|
@ -1326,5 +1358,6 @@ This is one of `zizmor`'s bigger recent releases! Key enhancements include:
|
|||
[dependabot-cooldown]: ./audits.md#dependabot-cooldown
|
||||
[concurrency-limits]: ./audits.md#concurrency-limits
|
||||
[archived-uses]: ./audits.md#archived-uses
|
||||
[impostor-commit]: ./audits.md#impostor-commit
|
||||
|
||||
[exit code]: ./usage.md#exit-codes
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ Options:
|
|||
Don't show progress bars, even if the terminal supports them
|
||||
--format <FORMAT>
|
||||
The output format to emit. By default, cargo-style diagnostics will be emitted [default: plain] [possible values: plain, json, json-v1, sarif, github]
|
||||
--render-links <RENDER_LINKS>
|
||||
Whether to render OSC 8 links in the output [env: ZIZMOR_RENDER_LINKS=] [default: auto] [possible values: auto, always, never]
|
||||
--show-audit-urls <SHOW_AUDIT_URLS>
|
||||
Whether to render audit URLs in the output, separately from any URLs embedded in OSC 8 links [env: ZIZMOR_SHOW_AUDIT_URLS=] [default: auto] [possible values: auto, always, never]
|
||||
--color <MODE>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@
|
|||
- aio-libs/aiobotocore#1355
|
||||
|
||||
|
||||
- { width="40" loading=lazy align=left } altair-graphql
|
||||
|
||||
---
|
||||
|
||||
??? example "Examples"
|
||||
- altair-graphql/altair@4aa5679f89528c183321a07e387567c13be29f26
|
||||
|
||||
|
||||
- { width="40" loading=lazy align=left } anchore
|
||||
|
||||
---
|
||||
|
|
@ -357,6 +365,15 @@
|
|||
- docker/compose#12737
|
||||
|
||||
|
||||
- { width="40" loading=lazy align=left } dubzzz
|
||||
|
||||
---
|
||||
|
||||
??? example "Examples"
|
||||
- dubzzz/fast-check#6369
|
||||
- dubzzz/fast-check#6370
|
||||
|
||||
|
||||
- { width="40" loading=lazy align=left } earthobservations
|
||||
|
||||
---
|
||||
|
|
@ -1106,6 +1123,10 @@
|
|||
---
|
||||
|
||||
??? example "Examples"
|
||||
- psf/black#4901
|
||||
- psf/black#4905
|
||||
- psf/black#4906
|
||||
- psf/black#4911
|
||||
- psf/cachecontrol#345
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ adafruit/circuitpython#9785
|
|||
ag2ai/faststream#2398
|
||||
aio-libs/aiobotocore#1344
|
||||
aio-libs/aiobotocore#1355
|
||||
altair-graphql/altair@4aa5679f89528c183321a07e387567c13be29f26
|
||||
anchore/vunnel#832
|
||||
ansible/ansible-documentation#3188
|
||||
apache/airflow#45408
|
||||
|
|
@ -75,6 +76,8 @@ django/django@86b8058b40145fb5ba4fd859676225f533eca986
|
|||
django-commons/django-tasks-scheduler#272
|
||||
Diaoul/subliminal#1190
|
||||
docker/compose#12737
|
||||
dubzzz/fast-check#6369
|
||||
dubzzz/fast-check#6370
|
||||
earthobservations/wetterdienst#1440
|
||||
edgelesssys/contrast#1604
|
||||
EFForg/rayhunter#711
|
||||
|
|
@ -231,6 +234,10 @@ praetorian-inc/noseyparker#228
|
|||
prettytable/prettytable#339
|
||||
privacyidea/privacyidea#4854
|
||||
prometheus/prometheus#16530
|
||||
psf/black#4901
|
||||
psf/black#4905
|
||||
psf/black#4906
|
||||
psf/black#4911
|
||||
psf/cachecontrol#345
|
||||
pubgrub-rs/pubgrub#389
|
||||
pyca/service-identity#75
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ If you run into this issue, you have two options:
|
|||
in your repository secrets, you could do:
|
||||
|
||||
```yaml title="example/repoA/.github/workflows/ci.yml" hl_lines="3"
|
||||
- uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0
|
||||
- uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
|
||||
with:
|
||||
token: ${{ secrets.ZIZMOR_GH_TOKEN }}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -16,4 +16,9 @@ manifest-path = "crates/zizmor/Cargo.toml"
|
|||
include = [{ path = "README.md", format = "sdist" }, { path = "LICENSE", format = "sdist" }]
|
||||
|
||||
[dependency-groups]
|
||||
bench = [
|
||||
"pytest>=9.0.2",
|
||||
"pytest-codspeed>=4.2.0",
|
||||
"urllib3>=2.6.2",
|
||||
]
|
||||
docs = ["zensical"]
|
||||
|
|
|
|||
236
uv.lock
generated
236
uv.lock
generated
|
|
@ -2,6 +2,88 @@ version = 1
|
|||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.3.1"
|
||||
|
|
@ -32,6 +114,27 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/2d/82/e5d2c1c67d19841e9edc74954c827444ae826978499bde3dfc1d007c8c11/deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00", size = 13475, upload-time = "2024-08-30T05:31:48.659Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.10"
|
||||
|
|
@ -41,6 +144,54 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "4.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mdurl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.23"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
|
|
@ -63,6 +214,50 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/93/78/b93cb80bd673bdc9f6ede63d8eb5b4646366953df15667eb3603be57a2b1/pymdown_extensions-10.17.2-py3-none-any.whl", hash = "sha256:bffae79a2e8b9e44aef0d813583a8fea63457b7a23643a43988055b7b79b4992", size = 266556, upload-time = "2025-11-26T15:43:55.162Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-codspeed"
|
||||
version = "4.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi" },
|
||||
{ name = "pytest" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e2/e8/27fcbe6516a1c956614a4b61a7fccbf3791ea0b992e07416e8948184327d/pytest_codspeed-4.2.0.tar.gz", hash = "sha256:04b5d0bc5a1851ba1504d46bf9d7dbb355222a69f2cd440d54295db721b331f7", size = 113263, upload-time = "2025-10-24T09:02:55.704Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/b8/d599a466c50af3f04001877ae8b17c12b803f3b358235736b91a0769de0d/pytest_codspeed-4.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:609828b03972966b75b9b7416fa2570c4a0f6124f67e02d35cd3658e64312a7b", size = 261943, upload-time = "2025-10-24T09:02:37.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/19/ccc1a2fcd28357a8db08ba6b60f381832088a3850abc262c8e0b3406491a/pytest_codspeed-4.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23a0c0fbf8bb4de93a3454fd9e5efcdca164c778aaef0a9da4f233d85cb7f5b8", size = 250782, upload-time = "2025-10-24T09:02:39.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/2d/f0083a2f14ecf008d961d40439a71da0ae0d568e5f8dc2fccd3e8a2ab3e4/pytest_codspeed-4.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2de87bde9fbc6fd53f0fd21dcf2599c89e0b8948d49f9bad224edce51c47e26b", size = 261960, upload-time = "2025-10-24T09:02:40.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/0c/1f514c553db4ea5a69dfbe2706734129acd0eca8d5101ec16f1dd00dbc0f/pytest_codspeed-4.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95aeb2479ca383f6b18e2cc9ebcd3b03ab184980a59a232aea6f370bbf59a1e3", size = 250808, upload-time = "2025-10-24T09:02:42.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/04/479905bd6653bc981c0554fcce6df52d7ae1594e1eefd53e6cf31810ec7f/pytest_codspeed-4.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d4fefbd4ae401e2c60f6be920a0be50eef0c3e4a1f0a1c83962efd45be38b39", size = 262084, upload-time = "2025-10-24T09:02:43.155Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/46/d6f345d7907bac6cbb6224bd697ecbc11cf7427acc9e843c3618f19e3476/pytest_codspeed-4.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:309b4227f57fcbb9df21e889ea1ae191d0d1cd8b903b698fdb9ea0461dbf1dfe", size = 251100, upload-time = "2025-10-24T09:02:44.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/dc/e864f45e994a50390ff49792256f1bdcbf42f170e3bc0470ee1a7d2403f3/pytest_codspeed-4.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72aab8278452a6d020798b9e4f82780966adb00f80d27a25d1274272c54630d5", size = 262057, upload-time = "2025-10-24T09:02:45.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/1c/f1d2599784486879cf6579d8d94a3e22108f0e1f130033dab8feefd29249/pytest_codspeed-4.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:684fcd9491d810ded653a8d38de4835daa2d001645f4a23942862950664273f8", size = 251013, upload-time = "2025-10-24T09:02:46.937Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/fd/eafd24db5652a94b4d00fe9b309b607de81add0f55f073afb68a378a24b6/pytest_codspeed-4.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50794dabea6ec90d4288904452051e2febace93e7edf4ca9f2bce8019dd8cd37", size = 262065, upload-time = "2025-10-24T09:02:48.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/14/8d9340d7dc0ae647991b28a396e16b3403e10def883cde90d6b663d3f7ec/pytest_codspeed-4.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0ebd87f2a99467a1cfd8e83492c4712976e43d353ee0b5f71cbb057f1393aca", size = 251057, upload-time = "2025-10-24T09:02:49.102Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/39/48cf6afbca55bc7c8c93c3d4ae926a1068bcce3f0241709db19b078d5418/pytest_codspeed-4.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbbb2d61b85bef8fc7e2193f723f9ac2db388a48259d981bbce96319043e9830", size = 267983, upload-time = "2025-10-24T09:02:50.558Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/86/4407341efb5dceb3e389635749ce1d670542d6ca148bd34f9d5334295faf/pytest_codspeed-4.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:748411c832147bfc85f805af78a1ab1684f52d08e14aabe22932bbe46c079a5f", size = 256732, upload-time = "2025-10-24T09:02:51.603Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/0e/8cb71fd3ed4ed08c07aec1245aea7bc1b661ba55fd9c392db76f1978d453/pytest_codspeed-4.2.0-py3-none-any.whl", hash = "sha256:e81bbb45c130874ef99aca97929d72682733527a49f84239ba575b5cb843bab0", size = 113726, upload-time = "2025-10-24T09:02:54.785Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
|
|
@ -127,6 +322,19 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "14.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.3.0"
|
||||
|
|
@ -176,6 +384,24 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.6.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zensical"
|
||||
version = "0.0.10"
|
||||
|
|
@ -210,6 +436,11 @@ name = "zizmor"
|
|||
source = { editable = "." }
|
||||
|
||||
[package.dev-dependencies]
|
||||
bench = [
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
docs = [
|
||||
{ name = "zensical" },
|
||||
]
|
||||
|
|
@ -217,4 +448,9 @@ docs = [
|
|||
[package.metadata]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
bench = [
|
||||
{ name = "pytest", specifier = ">=9.0.2" },
|
||||
{ name = "pytest-codspeed", specifier = ">=4.2.0" },
|
||||
{ name = "urllib3", specifier = ">=2.6.2" },
|
||||
]
|
||||
docs = [{ name = "zensical" }]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue