Skip to content

ci: add ruff lint and bandit SAST, and SHA-pin all actions #10

ci: add ruff lint and bandit SAST, and SHA-pin all actions

ci: add ruff lint and bandit SAST, and SHA-pin all actions #10

Workflow file for this run

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