Two layers of defence-in-depth scanning:
* `.forgejo/workflows/security-scan.yml` — runs pip-audit, bandit, and
gitleaks on every push, every PR, and nightly at 07:00 UTC. Activates
when the forge has a runner; harmless no-op until then. Bandit is
invoked with `--skip B608` because every dynamic SQL build in this
codebase uses bound parameters for data and structural placeholders
only — we still catch real injection through code review.
* `scripts/security-scan.sh` + systemd `service`/`timer` — maple-side
daily scanner that runs the same three tools entirely in containers
(no host pollution). Differences from the forge job:
- pip-audit runs INSIDE the live container against installed
packages, catching new CVEs in transitives requirements.txt
doesn't pin (e.g. starlette breaking changes shipping in 1.0).
- bandit scans the LIVE deploy dir at
~/docker/stacks/truenas-burnin/app/, not a fresh git checkout —
so drift between forge HEAD and prod surfaces here too.
- gitleaks scans a managed clone in ~/scan-checkouts/, kept
fast-forward to origin/main.
Output: ~/security-scans/scan-YYYY-MM-DD/{summary,pip-audit,bandit,
gitleaks}.txt with 30-day retention. ~/security-scans/findings.log
appended on any non-zero exit. SECURITY_SCAN_WEBHOOK env in the
service unit lets you POST findings to Mattermost / Slack / etc. once
you decide where alerts should land.
First-run findings already actioned in this commit:
* pip-audit caught 3 CVEs in `pip` itself (CVE-2025-8869,
CVE-2026-1703, CVE-2026-3219). Dockerfile now upgrades pip to
>=26.0 before installing the rest.
* bandit's B608 SQL-injection heuristic flagged two f-string SQL
constructions in `_upsert_drive` and `_fetch_drives_for_template`.
Both were structural concatenation (column-list selection,
'?,?,?' placeholder count), not data interpolation, but refactored
from f-string to explicit concatenation so a future reviewer
doesn't have to relitigate.
* bandit's B104 (binding to 0.0.0.0) annotated with inline `# nosec
B104` — container deliberately binds all interfaces; nginx-proxy-
manager fronts it.
* gitleaks: 0 secrets across 14 commits. Clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
1.9 KiB
YAML
61 lines
1.9 KiB
YAML
name: Security scan
|
|
|
|
# Runs on every push to main, every PR, and nightly at 07:00 UTC (~03:00 EDT).
|
|
# Three jobs run in parallel — failure of any one fails the workflow,
|
|
# making findings visible in the forge UI.
|
|
#
|
|
# Tools:
|
|
# pip-audit — known CVEs in pinned dependencies (PyPI advisory DB)
|
|
# bandit — Python static security analysis (subprocess, eval, etc.)
|
|
# gitleaks — secrets in git history (full repo scan)
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
schedule:
|
|
- cron: "0 7 * * *"
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
|
|
pip-audit:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install pip-audit
|
|
run: pip install --upgrade pip-audit
|
|
- name: Audit requirements.txt
|
|
run: pip-audit --requirement requirements.txt --strict --format=columns
|
|
|
|
bandit:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install bandit
|
|
run: pip install --upgrade bandit
|
|
- name: Static security analysis
|
|
# B608: SQL string construction. All dynamic SQL in this repo uses
|
|
# bound parameters for data; the dynamic part is structural
|
|
# (column lists / IN-clause '?,?,?' placeholders). Reviewed.
|
|
run: bandit -r app -ll -ii --skip B608 -x app/__pycache__,tests
|
|
|
|
gitleaks:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Install gitleaks
|
|
run: |
|
|
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz \
|
|
| tar -xz gitleaks
|
|
chmod +x gitleaks
|
|
- name: Scan git history for secrets
|
|
run: ./gitleaks detect --source . --no-banner --redact --verbose
|