Repository navigation
ci: add ruff lint and bandit SAST, and SHA-pin all actions #10
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| # A new push to the same PR supersedes the previous run. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| name: tests (${{ matrix.os }}, py${{ matrix.python-version }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest] | |
| # uv.lock resolves for 3.10+ only, so pytest can stay on a version | |
| # patched for CVE-2025-71176. 3.8 and 3.9 are covered by test-legacy. | |
| python-version: ['3.10', '3.11', '3.12', '3.13'] | |
| include: | |
| # The tool is developed on macOS, which has its own termios/stty paths. | |
| - os: macos-latest | |
| python-version: '3.12' | |
| steps: | |
| # Actions are pinned to a commit SHA, not a tag: a tag is mutable, so | |
| # `@v4` is an unaudited third party with write access to this build. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| # tests/test_nse_integration.py shells out to real nmap against local stub | |
| # servers. Without nmap the whole module skips itself, so installing it is | |
| # what makes the NSE assertions actually run. | |
| - name: Install nmap | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends nmap | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| enable-cache: true | |
| - name: Check uv.lock is up to date | |
| run: uv lock --check | |
| # Coverage reporting and the 95% floor come from [tool.pytest.ini_options] | |
| # in pyproject.toml, so a coverage regression fails this step too. | |
| - name: Run tests | |
| run: uv run --frozen pytest tests/ -v | |
| # spoonmap.py is dependency-free stdlib and README claims Python 3.6+, so the | |
| # oldest versions pyproject declares still need testing. They cannot use | |
| # uv.lock (it resolves for 3.10+), so the test toolchain is resolved fresh | |
| # here, outside the project. That means pytest 8.x on these two jobs — the | |
| # CVE-2025-71176 tmpdir issue is a local-user attack on a throwaway runner, | |
| # and nothing vulnerable enters uv.lock, which is what the repo ships. | |
| test-legacy: | |
| name: tests (ubuntu-latest, py${{ matrix.python-version }}, unlocked) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.8', '3.9'] | |
| steps: | |
| # Actions are pinned to a commit SHA, not a tag: a tag is mutable, so | |
| # `@v4` is an unaudited third party with write access to this build. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Install nmap | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends nmap | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| enable-cache: true | |
| - name: Run tests | |
| run: > | |
| uv run --isolated --no-project --python ${{ matrix.python-version }} | |
| --with pytest --with pytest-cov | |
| pytest tests/ -v | |
| # Separate job, not a step on `test`: a lint failure and a test failure are | |
| # different questions, and neither should hide the other behind a red X. | |
| # Ruleset and pinned ruff version live in pyproject.toml. | |
| lint: | |
| name: ruff | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| python-version: '3.12' | |
| enable-cache: true | |
| - name: Ruff | |
| run: uv run --frozen ruff check spoonmap.py tests/ | |
| # SAST against a committed baseline: spoonmap shells out to masscan/nmap and | |
| # parses their XML, so a bare run reports 32 reviewed findings (subprocess | |
| # list-form calls, xml.etree on tool output we invoked ourselves) every time. | |
| # Baselining them means only a NEW finding fails the build. Regenerate with | |
| # uvx --from 'bandit[toml]==1.9.4' bandit -r spoonmap.py -c pyproject.toml \ | |
| # -f json -o .bandit-baseline.json | |
| # and say in the commit message why each added finding is acceptable. | |
| bandit: | |
| name: bandit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| python-version: '3.12' | |
| enable-cache: true | |
| - name: Bandit | |
| run: > | |
| uvx --from "bandit[toml]==1.9.4" bandit | |
| -r spoonmap.py -c pyproject.toml -b .bandit-baseline.json |