ci: add advisory dependency and container image vulnerability scanning - #156
ci: add advisory dependency and container image vulnerability scanning#156lucastononro wants to merge 2 commits into
Conversation
CI's only security step was bandit (Python SAST) — no dependency
scanning and no image scanning, despite requirements.txt already
carrying a manual note pinning litellm to dodge a CVE that a scanner
would enforce automatically.
Adds three new jobs, all advisory (continue-on-error: true on the
scan step) so pre-existing advisories don't break CI:
- backend-vuln-scan: pip-audit against backend/requirements.txt
- frontend-vuln-scan: npm audit against the frontend lockfile
- image-scan: builds trainable-backend/trainable-frontend locally
(not pushed) and scans them with Trivy (CRITICAL/HIGH)
Each can be flipped to blocking later by dropping continue-on-error
once its current advisories are triaged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
| - name: Trivy scan | ||
| uses: aquasecurity/trivy-action@0.36.0 | ||
| continue-on-error: true | ||
| with: | ||
| image-ref: ${{ matrix.image }}:ci-scan | ||
| format: table | ||
| severity: "CRITICAL,HIGH" | ||
| exit-code: "1" |
There was a problem hiding this comment.
Trivy findings stay in job logs only — the scan outputs
format: table to stdout. GitHub's Security tab (Dependabot/Code Scanning) can ingest SARIF uploads, which would surface CRITICAL/HIGH findings directly on the PR without reviewers having to dig into job logs. Adding format: sarif, output: trivy-results.sarif, and an actions/upload-artifact (or github/codeql-action/upload-sarif) step after the scan would make the advisory findings much more visible over time.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
- pip-audit job: scan under Python 3.13 to match backend/Dockerfile (python:3.13-slim), so audited dependency resolution mirrors what ships in production. - Trivy action: pin to immutable commit SHA ed142fd0673e97e23eac54620cfb913e5ce36c25 (v0.36.0). This also fixes a broken ref: the previous `@0.36.0` tag does not exist upstream (trivy-action tags are v-prefixed), so the image-scan job would have failed to resolve the action. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
Greptile review findings — resolution (commit 4a0ca9b)Fixed
Dismissed
Verification
|
|
Merged into integration branch |
Summary
bandit(Python SAST) — no dependency scanning (pip-audit/npm audit) and no image scan, even thoughbackend/requirements.txtalready carries a manual comment pinninglitellmto dodge a specific CVE — exactly the kind of thing a scanner catches automatically instead of relying on someone noticing.pip-audit,npm audit, and a Trivy image scan, all advisory only so pre-existing (real, currently unaddressed) advisories don't turn CI red.Changes
.github/workflows/ci.yml— three new jobs:backend-vuln-scan: installsbackend/requirements.txt+pip-audit, runspip-auditagainst the environment.frontend-vuln-scan:npm cithennpm auditinfrontend/.image-scan: buildstrainable-backendandtrainable-frontendlocally viadocker/build-push-action@v5(push: false, load: true— no registry, no credentials needed) matching the matrix inpublish-image.yml, then scans each withaquasecurity/trivy-action@0.36.0(severity: CRITICAL,HIGH,exit-code: "1").Every scan step (not the whole job) has
continue-on-error: true, with an inline comment noting what to do to flip it to blocking later (dropcontinue-on-erroronce the advisories below are triaged).Test plan
Existing
backend-lint,backend-test,backend-security,frontend-lint,frontend-buildare unaffected.New
pip-auditrun locally against the installedbackend/requirements.txtenv: exits 1, found 21 known vulnerabilities across mcp, python-dotenv, python-multipart, starlette — confirmscontinue-on-erroris load-bearing, not decorative.npm auditrun locally infrontend/: exits 1, 10 vulnerabilities (6 moderate, 4 high) acrossnext,glob,js-yaml,postcss,prismjs.backend/Dockerfileandfrontend/Dockerfile(--target runner) locally — both succeed.aquasec/trivy:latestviadocker run) against the built backend image with--severity CRITICAL,HIGH --exit-code 1: found real HIGH findings (npm supply-chain packages baked into the Claude Code CLI install, plusmcp/python-multipart/starlettePython CVEs matching pip-audit) — confirms the scan step would also fail without the advisory wrapper.Caveats
continue-on-errorshould be dropped per job to make it blocking.image-scanscans images built fresh in the job (not the ones actually pushed bypublish-image.yml), since PR CI doesn't have registry credentials — same Dockerfiles/context, so findings should match what ships.Closes #118
🤖 Generated with Claude Code
Greptile Summary
This PR adds three advisory-only CI jobs —
backend-vuln-scan(pip-audit),frontend-vuln-scan(npm audit), andimage-scan(Trivy) — to surface known dependency and container-image vulnerabilities without blocking the pipeline while pre-existing advisories remain unaddressed.backend-vuln-scan: installsrequirements.txtunder Python 3.13 (matchingbackend/Dockerfile) and runspip-auditwithcontinue-on-error: true.frontend-vuln-scan: runsnpm ci+npm auditinfrontend/withcontinue-on-error: true.image-scan: builds both images locally (push: false, load: true) via a matrix strategy, then scans each with Trivy (CRITICAL,HIGH); the Trivy action is pinned to a commit SHA rather than a mutable tag.Confidence Score: 5/5
Safe to merge — all new jobs are advisory-only and no existing jobs are modified.
The change only adds informational scanning jobs that cannot break the existing CI pipeline. All three scan steps use continue-on-error: true, existing jobs are untouched, and the Trivy action is pinned to an immutable commit SHA.
No files require special attention; the single suggestion on .github/workflows/ci.yml is optional.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[push / pull_request] --> B[backend-lint] A --> C[backend-test] A --> D[backend-security bandit] A --> E[backend-vuln-scan\npip-audit advisory] A --> F[frontend-vuln-scan\nnpm audit advisory] A --> G[image-scan matrix\nTrivy advisory] A --> H[frontend-lint] A --> I[frontend-build] G --> G1[trainable-backend\nBuild + Trivy] G --> G2[trainable-frontend\nBuild + Trivy] style E fill:#fffbe6,stroke:#f6c90e style F fill:#fffbe6,stroke:#f6c90e style G fill:#fffbe6,stroke:#f6c90e style G1 fill:#fffbe6,stroke:#f6c90e style G2 fill:#fffbe6,stroke:#f6c90e%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A[push / pull_request] --> B[backend-lint] A --> C[backend-test] A --> D[backend-security bandit] A --> E[backend-vuln-scan\npip-audit advisory] A --> F[frontend-vuln-scan\nnpm audit advisory] A --> G[image-scan matrix\nTrivy advisory] A --> H[frontend-lint] A --> I[frontend-build] G --> G1[trainable-backend\nBuild + Trivy] G --> G2[trainable-frontend\nBuild + Trivy] style E fill:#fffbe6,stroke:#f6c90e style F fill:#fffbe6,stroke:#f6c90e style G fill:#fffbe6,stroke:#f6c90e style G1 fill:#fffbe6,stroke:#f6c90e style G2 fill:#fffbe6,stroke:#f6c90eReviews (2): Last reviewed commit: "fix(review): address Greptile findings o..." | Re-trigger Greptile