100 CVEs in 63 Milliseconds: What's Actually Hiding in Your Dependencies
We built a fake Flask + React web application with dependencies pinned to versions from 2017–2018. Then we ran Trivy against it. In 63 milliseconds, it found 100 vulnerabilities — 21 CRITICAL, 79 HIGH — across Python and npm packages. No Docker required. The strongest finding: three separate remote code execution paths in a single npm package that's in 90% of Node.js projects.
Why Dependency Scanning Is Step Zero
Most security testing focuses on the application layer — SQL injection in your endpoints, XSS in your forms,
IDOR in your API. But before any of that, your application is already carrying its dependencies into production.
Every package in your requirements.txt and package.json is code you didn't write,
running with the same permissions as your application.
The attack surface is enormous. The average Node.js project has hundreds of transitive dependencies. A vulnerability in lodash affects every project that uses lodash — and lodash is in 98% of Node.js projects. A vulnerability in PyYAML affects every Python application that parses YAML. Neither of those are edge cases.
Trivy is Aqua Security's open-source scanner for exactly this problem. It's the same engine that AWS ECR, Google Artifact Registry, and GitHub Advanced Security use for their native scanning features. We added it to SecurityClaw because it fills a gap the other tools don't cover: known CVEs in your declared dependencies, across every major language ecosystem, in CI pipeline time.
The Demo Target
We built a representative "typical web application" package manifest:
- A Python backend using Flask, Django, Pillow, PyYAML, PyJWT, and
cryptography— all pinned to versions from 2017–2018 - A Node.js frontend using lodash, axios, handlebars, express, minimist, and
serialize-javascript— similarly outdated
We deliberately chose versions that were current three to four years after initial release — the version range where many real production applications live. Teams that pinned dependencies in 2019 or 2020 and haven't updated them since are running exactly these packages.
The Scan Command
trivy fs --severity HIGH,CRITICAL --scanners vuln ./demo-target
Filesystem mode: no Docker daemon needed. Trivy reads the package manifest files directly and cross-references every dependency against its vulnerability database. Works in any CI runner, including those that block the Docker socket for security reasons.
Results Summary
| Metric | Value |
|---|---|
| Scan runtime | 63ms |
| Total vulnerabilities | 100 |
| CRITICAL | 21 |
| HIGH | 79 |
| Python packages scanned | 6 of 6 (100%) |
| npm packages scanned | 7 of 8 (87.5%) |
| Docker required | No |
What We Found: The Five Most Dangerous Findings
🔴 1. Handlebars — Three Separate RCE Paths
Handlebars is the JavaScript templating engine used in millions of Node.js applications. Version 4.0.12 has three distinct remote code execution vulnerabilities:
- CVE-2019-19919 (CVSS 9.8): Prototype pollution leading to remote code execution when processing user-controlled template data
- CVE-2021-23369 (CVSS 9.8): RCE via compilation of untrusted templates — if your app renders user-submitted template strings
- CVE-2021-23383 (CVSS 9.8): A second bypass of CVE-2021-23369's fix — the patch was incomplete
Three independent code execution paths. Any one of them is sufficient for full server compromise. The fixed version is 4.7.7+. Trivy flagged all three in 63ms.
Why does this matter if you "don't use user-controlled templates"? Because handlebars is often
a transitive dependency — a library you depend on uses it internally. Check your node_modules.
🔴 2. PyYAML 3.13 — Arbitrary Python Code Execution Since 2017
yaml.load() in PyYAML versions before 5.1 will execute arbitrary Python code
if the input contains a !!python/object/apply: tag. An attacker who can influence
YAML input to your application can call any Python function — including os.system().
import yaml
# This YAML input executes a shell command:
yaml.load("!!python/object/apply:os.system ['id']", Loader=yaml.FullLoader)
CVE-2017-18342 has been public for nine years. CVE-2020-14343 — filed three years later —
documents a bypass of the initial fix. The correct call is yaml.safe_load().
Many production codebases still use yaml.load() because the function signature
looks harmless and the documentation warning was added quietly.
🔴 3. Django 2.1.0 — Four SQL Injection CVEs
Django 2.1.0 reached end-of-life in April 2019. By that point, four SQL injection vulnerabilities had been documented in it:
- CVE-2019-14234: SQL injection via JSONField and HStoreField key lookups when user-controlled input is passed as a key name
- CVE-2019-19844: Account takeover via
reset_password— a carefully crafted email address bypasses user lookup and can match a different user's account - CVE-2020-7471: SQL injection in
StringAgg(delimiter=...)aggregation — passes raw string directly into the SQL query - CVE-2025-64459: A new SQL injection path found in 2025 — the EOL branch never received the patch
This is what EOL means in practice: you're not just missing feature updates. You're inheriting every vulnerability found after the branch was abandoned. Trivy caught all four.
🔴 4. Pillow 5.0.0 — Memory Corruption in the Image Parser
Pillow is the most popular Python imaging library. If your application accepts user-uploaded images and passes them through Pillow, version 5.0.0 has multiple memory corruption vulnerabilities:
- CVE-2020-11538: Out-of-bounds reads and writes in the SGI image file parser
- CVE-2020-5310: Integer overflow leading to buffer overflow in the TIFF decoder
- CVE-2020-5311: Out-of-bounds write in the SGI RLE decoder
Trivy found these three plus five more CRITICAL and 25 HIGH vulnerabilities in Pillow 5.0.0. The pattern is: user uploads a crafted image file, the image parser mishandles it, and the attacker achieves code execution on the server. This is particularly dangerous in applications that process images in a web-accessible context.
🔴 5. lodash 4.17.4 and minimist 1.2.0 — Prototype Pollution
Two of the most widely distributed npm packages in existence, both carrying CVSS 9.8 CRITICAL prototype pollution vulnerabilities:
- lodash 4.17.4 — CVE-2019-10744:
defaultsDeep()merges a user-controlled object into the target in a way that allows__proto__to be polluted. Once Object.prototype is modified, all objects in the process inherit the polluted properties. - minimist 1.2.0 — CVE-2021-44906: Argument parsing allows
__proto__to be set via command-line style arguments — affects any application that parses untrusted input through minimist (which is a very large number of applications, since minimist is a common transitive dependency).
These are not theoretical. Prototype pollution has been demonstrated as a stepping stone to RCE in several real-world Node.js applications. The protections against it depend heavily on whether the application uses frozen objects and how it handles the polluted properties.
A Notable HIGH: PyJWT — JWT Algorithm Confusion
Among the 79 HIGH severity findings, CVE-2022-29217 in PyJWT 1.5.0 deserves mention. This is a classic JWT security failure: PyJWT accepts HMAC-SHA256 (HS256) tokens signed with an RSA public key. An attacker who has a copy of the public key — which by definition is public — can forge a valid JWT token by switching the algorithm to HS256 and signing with the public key. The server, expecting RS256 but accepting either, validates the forged token as legitimate.
Authentication bypass via a public key you can download from the target's JWKS endpoint. This class of vulnerability is covered in depth in The Web Application Hacker's Handbook — still the reference for this type of auth layer attack.
The Honest Gap: path-parse Was Not Flagged
We planted path-parse 1.0.5 in the npm dependencies.
It has a known vulnerability: CVE-2021-23343, a ReDoS (Regular Expression Denial of Service)
flaw in path string parsing.
Trivy did not flag it — because CVE-2021-23343 is classified as MEDIUM severity.
Our scan was configured for --severity HIGH,CRITICAL. When we re-ran the scan
with --severity LOW,MEDIUM,HIGH,CRITICAL, path-parse appeared immediately.
This is a configuration choice, not a detection failure. The right approach:
- Daily CI scans:
--severity HIGH,CRITICAL --exit-code 1— block builds on anything actionable, fast feedback - Weekly full audits:
--severity LOW,MEDIUM,HIGH,CRITICAL— full picture for triage and backlog planning
Trivy exposes both modes cleanly. The threshold is yours to set based on your team's risk tolerance and sprint capacity for remediations.
How Filesystem Mode Works (No Docker Required)
Trivy has three scanning modes. For CI/CD integration, filesystem mode is the one that matters:
# Scan a local directory — reads package manifests, no Docker socket needed
trivy fs --severity HIGH,CRITICAL --scanners vuln ./
# Scan a specific manifest file
trivy fs --severity HIGH,CRITICAL requirements.txt
# Fail the CI build if any HIGH or CRITICAL findings exist
trivy fs --severity HIGH,CRITICAL --exit-code 1 .
# Full JSON output for downstream processing
trivy fs --format json --output trivy-results.json .
Supported manifest formats: requirements.txt, Pipfile.lock,
poetry.lock, package.json, package-lock.json,
yarn.lock, Gemfile.lock, go.sum,
pom.xml, build.gradle, Cargo.lock, and more.
One command, every language.
Runtime overhead in a CI pipeline: 50–300ms for a typical project. The database auto-updates daily. For teams new to the space, Black Hat Python is worth reading alongside practical tooling like this — understanding how attackers think about dependency attacks changes how you prioritise findings.
SecurityClaw Scorecard: Container Security Category Opens
D11 adds a new capability category to the SecurityClaw platform — the first time we've tested container and dependency-level vulnerability scanning. Before this, SecurityClaw could detect malicious packages (supply-chain-scanner, D9) but had no coverage for vulnerable packages. Different threat class, different detection mechanism.
| Metric | Before D11 | After D11 |
|---|---|---|
| Total campaigns | 17 | 18 |
| Overall pass rate | 88.24% | 88.89% |
| Active categories | 5 | 6 |
| New category | — | Container & Dependency Security ✅ |
D11 result: PASS. 100% detection on Python packages, 87.5% on npm
(path-parse intentionally excluded at HIGH+ threshold). The gap is documented and
reproducible — run --severity MEDIUM and it's found.
If you work in DevSecOps and are evaluating dependency scanning tooling, Penetration Testing by Georgia Weidman gives strong context for how vulnerability data translates into exploitability — useful for triage decisions when the scanner returns 100 findings and you need to decide what actually matters.
The Immediate Action
If you run any web application and you haven't scanned your dependencies with Trivy, do it now. It takes 63 milliseconds and it's free:
# Install
brew install trivy # macOS
apt-get install trivy # Debian/Ubuntu
# Or: https://github.com/aquasecurity/trivy/releases
# Scan your project
cd your-project
trivy fs --severity HIGH,CRITICAL .
The 100 CVEs we found aren't edge cases planted for dramatic effect. They're what happens when you use packages that were current in 2018 and haven't been updated since. A significant portion of production deployments are in exactly that state.
The fix isn't complicated: update your dependencies and add Trivy to CI. The hard part is finding out which of your 400 transitive dependencies need updating — which is exactly what this took 63 milliseconds to tell us.