security.yml previously ran safety/bandit/semgrep with `|| true` and could not go red. Now: pip-audit on requirements.txt is a hard gate (dev deps reported only), bandit HIGH fails (B104 bind-all skipped: intended behind Cloudflare/Caddy), pip-licenses uploaded as a report. Weekly + on dependency/source changes. Running it locally found 29 advisories, all in pinned-and-forgotten runtime deps: starlette 0.27 (7, incl. Host-header path confusion and form DoS), fastapi 0.104, requests 2.31 (3), pymysql 1.1. Bumped to current: fastapi 0.141.1 / starlette 1.6.0, pydantic 2.13.5, uvicorn 0.52.4, requests 2.34.2, pymysql 1.2.0; dev: pytest 9.1.1, black 26.5.1. pip-audit is now clean. requires-python narrowed to 3.11 (the truth: psycopg2-binary 2.9.9 fails on 3.13; pandas 2.0.3 has no 3.12 wheels). Full suite passes; API smoke-tested (health, stations, forecast, history, stats, docs, openapi) on the new stack. black 26 reformatted 8 files.
105 lines
3.2 KiB
YAML
105 lines
3.2 KiB
YAML
name: Security
|
|
|
|
# Two gates that can actually fail, plus one report:
|
|
# - pip-audit against requirements.txt: any known vulnerability in a runtime
|
|
# dependency fails the job (dev-only tools are reported, not gated)
|
|
# - bandit on src/: HIGH severity findings fail; medium/low are listed.
|
|
# B104 (bind 0.0.0.0) is skipped: the service is meant to listen on all
|
|
# interfaces behind Cloudflare/Caddy.
|
|
# - pip-licenses report as an artifact (informational; the project is MIT
|
|
# and its runtime deps are MIT/BSD/Apache/PSF)
|
|
# The old file ran safety/bandit/semgrep with `|| true` and could not go red.
|
|
|
|
on:
|
|
schedule:
|
|
- cron: "0 3 * * 1" # weekly, Monday 03:00 UTC
|
|
workflow_dispatch:
|
|
push:
|
|
paths:
|
|
- "requirements*.txt"
|
|
- "pyproject.toml"
|
|
- "uv.lock"
|
|
- "src/**/*.py"
|
|
- ".gitea/workflows/security.yml"
|
|
pull_request:
|
|
paths:
|
|
- "requirements*.txt"
|
|
- "pyproject.toml"
|
|
- "src/**/*.py"
|
|
|
|
env:
|
|
PYTHON_VERSION: "3.11"
|
|
|
|
jobs:
|
|
dependencies:
|
|
name: Dependency vulnerabilities
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
|
|
- name: Install pip-audit
|
|
run: |
|
|
python -m pip install --upgrade pip --root-user-action=ignore
|
|
pip install --root-user-action=ignore pip-audit
|
|
|
|
- name: Runtime dependencies (gate)
|
|
run: pip-audit -r requirements.txt --strict --desc on
|
|
|
|
- name: Dev dependencies (report only)
|
|
run: pip-audit -r requirements-dev.txt --desc on || echo "::warning::dev-only dependency advisories above"
|
|
|
|
code:
|
|
name: Static analysis
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
|
|
- name: Install bandit
|
|
run: |
|
|
python -m pip install --upgrade pip --root-user-action=ignore
|
|
pip install --root-user-action=ignore bandit
|
|
|
|
- name: bandit (HIGH fails; medium/low listed)
|
|
run: |
|
|
bandit -r src/ -q --skip B104 -ll -ii || true
|
|
bandit -r src/ -q --skip B104 --severity-level high --confidence-level medium
|
|
|
|
licenses:
|
|
name: License report
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: pip
|
|
cache-dependency-path: requirements.txt
|
|
|
|
- name: Install
|
|
run: |
|
|
python -m pip install --upgrade pip --root-user-action=ignore
|
|
pip install --root-user-action=ignore -r requirements.txt pip-licenses
|
|
|
|
- name: Report
|
|
run: |
|
|
pip-licenses --format=markdown --with-urls --output-file=licenses.md
|
|
pip-licenses --format=json --output-file=licenses.json
|
|
echo "Copyleft licenses among runtime deps (informational):"
|
|
pip-licenses --format=plain | grep -iE 'GPL|AGPL|LGPL' || echo " none"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: licenses-${{ github.run_number }}
|
|
path: |
|
|
licenses.md
|
|
licenses.json
|