diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09cc77c..5f9c076 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,6 +84,86 @@ jobs: - run: bandit -r . -x ./tests --severity-level medium -f json -o bandit-report.json || true - run: bandit -r . -x ./tests --severity-level high + backend-vuln-scan: + name: Backend Dependency Vulnerability Scan (advisory) + runs-on: ubuntu-latest + defaults: + run: + working-directory: backend + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + # Match backend/Dockerfile (python:3.13-slim) so the audited + # dependency resolution mirrors what actually ships. + python-version: "3.13" + cache: pip + - run: pip install -r requirements.txt pip-audit + # Advisory only: known advisories (e.g. transitive starlette/mcp/ + # python-multipart CVEs) already exist in the dependency tree today, + # and shouldn't block CI. Flip this to blocking (drop + # continue-on-error) once those are triaged and cleared. + - name: pip-audit + run: pip-audit + continue-on-error: true + + frontend-vuln-scan: + name: Frontend Dependency Vulnerability Scan (advisory) + runs-on: ubuntu-latest + defaults: + run: + working-directory: frontend + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + cache-dependency-path: frontend/package-lock.json + - run: npm ci + # Advisory only: known advisories (next/glob/prismjs/postcss/js-yaml) + # already exist in the dependency tree today, and shouldn't block CI. + # Flip this to blocking (drop continue-on-error) once those are + # triaged and cleared. + - name: npm audit + run: npm audit + continue-on-error: true + + image-scan: + name: Container Image Vulnerability Scan (advisory) + runs-on: ubuntu-latest + strategy: + matrix: + include: + - image: trainable-backend + context: ./backend + - image: trainable-frontend + context: ./frontend + target: runner + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - name: Build ${{ matrix.image }} image (local only, not pushed) + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.context }} + target: ${{ matrix.target || '' }} + push: false + load: true + tags: ${{ matrix.image }}:ci-scan + # Advisory only: base-image and OS-package advisories aren't triaged + # yet and shouldn't block CI. Flip to blocking (drop + # continue-on-error, or set exit-code back to 1 without the wrapper) + # once they are. + - name: Trivy scan + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + continue-on-error: true + with: + image-ref: ${{ matrix.image }}:ci-scan + format: table + severity: "CRITICAL,HIGH" + exit-code: "1" + frontend-lint: name: Frontend Lint & Typecheck runs-on: ubuntu-latest