From 881bce07e830ffb4766f2c98394781c6f89abfa8 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 21 Oct 2026 10:47:39 +0200 Subject: [PATCH] Added security scanning --- .gitea/workflows/pip-audit.yaml | 54 +++++++++++++++++++++++++++++++++ scripts/filter_requirements.py | 37 ++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 .gitea/workflows/pip-audit.yaml create mode 100644 scripts/filter_requirements.py diff --git a/.gitea/workflows/pip-audit.yaml b/.gitea/workflows/pip-audit.yaml new file mode 100644 index 0000000..ae665f8 --- /dev/null +++ b/.gitea/workflows/pip-audit.yaml @@ -0,0 +1,54 @@ +name: pip-audit +on: + schedule: + - cron: "0 1 * * *" + push: + branches: [main] + paths: [requirements.txt, scripts/filter_requirements.py] + workflow_dispatch: + +jobs: + audit: + runs-on: ubuntu-latest # Docker runner, no mail access needed + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Audit dependencies + shell: bash + run: | + pip install pip-audit + python3 scripts/filter_requirements.py requirements.txt requirements.audit.txt + pip-audit -r requirements.audit.txt --no-deps --disable-pip + + notify: + needs: audit + if: ${{ always() && needs.audit.result == 'failure' }} + runs-on: host # runs directly on the server + steps: + - name: Send mail via local postfix + env: + MAIL_FROM: ${{ vars.MAIL_FROM }} + MAIL_TO: ${{ vars.MAIL_TO }} + REPO: ${{ gitea.repository }} + RUN_URL: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }} + run: | + python3 - <<'EOF' + import os, smtplib + from email.message import EmailMessage + + msg = EmailMessage() + msg["Subject"] = f"[pip-audit] Problem in {os.environ['REPO']}" + msg["From"] = os.environ["MAIL_FROM"] + msg["To"] = os.environ["MAIL_TO"] + msg.set_content( + "The dependency audit failed (known vulnerability or job error).\n\n" + f"Details: {os.environ['RUN_URL']}\n" + ) + + with smtplib.SMTP("127.0.0.1", 25, timeout=30) as s: + s.send_message(msg) + EOFq \ No newline at end of file diff --git a/scripts/filter_requirements.py b/scripts/filter_requirements.py new file mode 100644 index 0000000..3ed444f --- /dev/null +++ b/scripts/filter_requirements.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Write a copy of requirements.txt without the self-hosted packages. + +Usage: filter_requirements.py +""" +import re +import sys + +# Self-hosted packages that are not on PyPI. One per line. +SKIP = { + "lib-db-module", + "lib-secret-manager", + "lib-send-notifications", + "simple-logger-handler", +} + +NAME_RE = re.compile(r"^\s*([A-Za-z0-9][A-Za-z0-9._-]*)") + + +def main(src: str, dst: str) -> None: + skipped = [] + + with open(src) as fin, open(dst, "w") as fout: + for line in fin: + match = NAME_RE.match(line) + if match and match.group(1) in SKIP: + skipped.append(match.group(1)) + continue + fout.write(line) + + print(f"Skipped {len(skipped)} self-hosted package(s): {', '.join(skipped) or '-'}") + + +if __name__ == "__main__": + if len(sys.argv) != 3: + sys.exit(__doc__) + main(sys.argv[1], sys.argv[2]) \ No newline at end of file