We Scanned Our AI Stack with Trivy — Here's What We Found
29 million secrets were leaked on public GitHub last year. That number is from GitHub’s own data — passwords, API keys, tokens committed and pushed before anyone noticed.
We decided to stop assuming our stack was clean and actually check. Here’s what a real scan found in real production repos — and what we fixed.
The Setup
We scanned five repos using Trivy — Aqua Security’s open-source scanner:
menonpg/agent-validator— FastAPI service running live on Cloud Runmenonpg/menonlab-blog— this blog (Astro, deployed on Railway)menonpg/stockscout4— AI trading desk (Railway)menonpg/soul.py— PyPI packagemenonpg/monica-workspace— agent workspace
Install is one line:
# macOS
brew install trivy
# Linux
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
Then scan:
trivy fs --scanners secret,vuln --format table .
What We Found
agent-validator — 2 HIGH, live public service
The most serious findings were in the agent-validator service — a FastAPI app running on Cloud Run that validates AI agent repositories. It’s publicly accessible.
python-multipart 0.0.9 → 2 HIGH vulnerabilities
├── CVE-2024-53981 Severity: HIGH Fixed: 0.0.18
│ DoS via deformed multipart/form-data boundary
└── CVE-2026-24486 Severity: HIGH Fixed: 0.0.22
Arbitrary file write via path traversal ⚠️
The second one is the serious one. CVE-2026-24486 is an arbitrary file write vulnerability — a crafted multipart request could write to arbitrary paths on the server. That’s a full server compromise vector on a public endpoint.
The fix was one line in requirements.txt:
- python-multipart==0.0.9
+ python-multipart==0.0.22
Pushed, Cloud Run redeployed. Total time to fix: under 5 minutes once we knew it existed.
menonlab-blog — 11 vulnerabilities in npm/pnpm lockfiles
The Astro blog had more findings, mostly in transitive dependencies:
pnpm-lock.yaml: 10 vulnerabilities (HIGH: 4, MEDIUM: 2, LOW: 4)
package-lock.json: 1 vulnerability (HIGH: 1)
The HIGH findings:
| Package | CVE | What It Does |
|---|---|---|
rollup 4.57.1 | CVE-2026-27606 | RCE via path traversal — fix: 4.59.0 |
h3 1.15.5 | CVE-2026-33128 | SSE injection via unsanitized newlines — fix: 1.15.6 |
fast-xml-parser 5.3.6 | CVE-2026-33036 | Numeric entity expansion DoS — fix: 5.5.6 |
svgo 4.0.0 | CVE-2026-29074 | XML entity expansion DoS — fix: 4.0.1 |
The rollup RCE is the notable one — remote code execution via path traversal in the build tool. Fixed:
npm audit fix # patches what it can
git add package-lock.json && git push
One finding — fast-xml-parser HIGH — had no available fix at scan time. It’s a transitive dependency of Astro waiting on an upstream patch. It’s logged; we’ll update when the fix ships.
stockscout4, soul.py, monica-workspace — all clean
StockScout v4: ✅ 0 secrets, 0 vulnerabilities
soul.py: ✅ 0 secrets, 0 vulnerabilities
monica-workspace: ✅ 0 secrets, 0 vulnerabilities
Worth noting: StockScout v4 was clean because we found and removed hardcoded API keys from it the day before scanning. Trivy would have caught those too — but we got lucky that we fixed them first. More on that in a moment.
The Secret Scanning Story
Running secrets scan across the repos:
trivy fs --scanners secret .
Everything came back clean. But this is where context matters.
We had just spent a session auditing StockScout v4 for hardcoded values and found:
FRED_KEY = "4b10e4313ff2d2784d439bee0bb58893"hardcoded at module levelFRED_API_KEYwith a hardcoded default in config.pytrump_v3.pypointing to a dead URL (not a secret, but dead code)
We fixed those before running Trivy. If we’d run the scan first, it would have flagged all of them. The lesson: secrets scanners work on what’s currently in the code. If you’ve already committed and pushed a secret, it’s in git history even if you remove it from the current file — and that requires git filter-repo to fully clean.
Running Trivy in CI — The Right Way
Finding vulnerabilities after deployment is better than not finding them. Finding them before merge is better still.
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
scanners: 'vuln,secret'
severity: 'HIGH,CRITICAL'
exit-code: '1' # Fail the build on HIGH/CRITICAL
format: 'table'
This runs on every PR and fails the build if HIGH or CRITICAL findings exist. We’re adding this to agent-validator and stockscout4 now.
The Honest Summary
| Repo | Secrets | Vulns | Status |
|---|---|---|---|
| agent-validator | 0 | 2 HIGH | ✅ Fixed (python-multipart → 0.0.22) |
| menonlab-blog | 0 | 4 HIGH, 7 lower | ✅ Mostly fixed (1 pending upstream) |
| stockscout4 | 0 | 0 | ✅ Clean |
| soul.py | 0 | 0 | ✅ Clean |
| monica-workspace | 0 | 0 | ✅ Clean |
One live public service had an arbitrary file write vulnerability. The blog had a build tool RCE. Neither was obvious — both were in dependency versions that looked fine at a glance.
Two commands. Under ten minutes to scan everything. A path traversal CVE in a public Cloud Run endpoint found and fixed before anyone else found it.
brew install trivy and run it on your stack before someone else does.