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