Cross-ecosystem dependency security scanner. Shakes the tree to see what falls.
Zero dependencies. Pure Node.js. Catches real supply chain attacks.
# Try it now — no install required
npx @penumbraforge/vexes scanSee it in action — click "Run workflow" to watch vexes scan a demo project with real vulnerabilities across npm, PyPI, and Cargo. No install needed.
$ vexes scan
vexes v0.1.0 -- scanning dependencies
Found 847 unique packages across 3 lockfile(s)
~ 847 packages checked in 2.1s (0 cached)
-- CRITICAL --------------------------------------------------
axios 1.14.1 (npm)
GHSA-xxxx -- Remote code execution via compromised dependency
Fixed in: >= 1.14.2
https://osv.dev/vulnerability/GHSA-xxxx
-- HIGH ------------------------------------------------------
lodash 4.17.20 (npm)
GHSA-yyyy -- Prototype pollution in lodash
Fixed in: >= 4.17.21
--------------------------------------------------
2 vulnerabilities . 1 critical . 1 high
in 847 packages across npm, pypi, cargo
completed in 2.1s
--------------------------------------------------
vexes is a dependency security scanner that goes beyond vulnerability databases. It uses a 4-layer detection engine to catch supply chain attacks that signature-based tools miss:
| Layer | Detection Method | What it catches |
|---|---|---|
| 1. AST Analysis | Parses JS/Python source via acorn AST | eval(), child_process.exec(), credential harvesting, obfuscated code, dynamic imports, WebAssembly, setTimeout(string), DNS exfiltration, prototype chain escapes |
| 2. Dependency Graph | Profiles newly added dependencies | Phantom dependencies (brand-new packages), circular staging, typosquatting, Unicode homoglyph attacks |
| 3. Behavioral Fingerprinting | Diffs capability profiles between versions | A utility library that suddenly gains network+exec capabilities |
| 4. Registry Metadata | Analyzes publish history, maintainers, timing | Account takeovers, rapid publishes, dormant package reactivation |
The red team test suite reconstructs 9 real-world attacks and proves vexes catches them:
- axios RAT (March 2026) -- Hijacked maintainer account, hidden dependency with RAT dropper
- Shai-Hulud worm (September 2025) -- Phished credentials, self-replicating worm via chalk/debug
- event-stream (November 2018) -- Social engineering, encrypted payload targeting bitcoin wallets
- ua-parser-js (October 2021) -- Account hijack, cryptominer + password stealer
- litellm/TeamPCP (March 2026) -- CI/CD compromise, 3-stage payload with K8s lateral movement
- Typosquatting --
expresss,loadash,reqeustsand similar name confusion attacks - Novel/hypothetical attacks -- WASM-based payloads, DNS exfiltration, capability escalation
# Global install
npm install -g @penumbraforge/vexes
# Or run directly
npx @penumbraforge/vexes scan
# Or clone and run
git clone https://github.com/penumbraforge/vexes.git
cd vexes && node bin/vexes.js scan --path /your/projectRequirements: Node.js >= 22.5.0 (uses native SQLite for caching, native fetch)
Enumerates dependencies from lockfiles, queries OSV.dev, and reports known vulnerabilities.
vexes scan # Scan current directory
vexes scan --path ./my-project # Scan a specific directory
vexes scan --ecosystem npm # Scan only npm dependencies
vexes scan --severity critical # Only show critical vulnerabilities
vexes scan --fix # Show upgrade commands for each vuln
vexes scan --json # Machine-readable JSON output
vexes scan --cached # Use cached results (skip freshness check)Ecosystems supported: npm (package-lock.json, pnpm-lock.yaml, yarn.lock), PyPI (Pipfile.lock, poetry.lock, requirements.txt, pyproject.toml), Cargo (Cargo.lock), Go (go.sum), Ruby (Gemfile.lock), PHP (composer.lock), NuGet (packages.lock.json), Java (gradle.lockfile, pom.xml), Homebrew (Brewfile.lock.json, Brewfile)
Exit codes: 0 = clean, 1 = vulnerabilities found, 2 = error/incomplete scan
Goes beyond vulnerability databases. Downloads registry metadata, runs AST analysis on install scripts, profiles behavioral changes between versions.
vexes analyze # Analyze direct dependencies
vexes analyze --deep # Download + AST-inspect actual tarball code
vexes analyze --explain lodash # Detailed breakdown for one package
vexes analyze --strict # Fail on any signal (for CI)
vexes analyze -v # Show all signals including LOW
vexes analyze --json # Machine-readable JSON outputDetection signals:
KNOWN_COMPROMISED-- Package has known OSV vulnerabilitiesMAINTAINER_CHANGE-- Publishing account changed (possible account takeover)POSTINSTALL_SCRIPT-- Has install lifecycle scriptsRAPID_PUBLISH-- Version published suspiciously quickly after previousVERSION_ANOMALY-- Major version jump or dormancy followed by sudden publishTYPOSQUAT-- Name suspiciously similar to a popular packagePHANTOM_DEPENDENCY-- Brand-new dependency added (< 7 days old)CIRCULAR_STAGING-- New dep published by the same account as the parentCAPABILITY_ESCALATION-- Package gained dangerous capabilities between versionsAST_DANGEROUS_PATTERN-- Dangerous code patterns in install scriptsTARBALL_DANGEROUS_PATTERN-- Dangerous patterns in actual package source codeHOMOGLYPH-- Package name contains suspicious Unicode (zero-width chars, RTL override, non-ASCII)MISSING_PROVENANCE-- No Sigstore provenance attestationNO_REPOSITORY-- No source repository link
Finds vulnerabilities and generates verified upgrade commands. Every recommended version is cross-checked against OSV to ensure it isn't itself vulnerable.
vexes fix # Show fix recommendations
vexes fix --json # Machine-readable outputIntercepts npm install and analyzes new/changed packages before they execute. Works by diffing lockfiles -- no network proxy needed.
vexes guard -- npm install axios # Guard a specific install
vexes guard --setup # Install shell wrappers (auto-guard)
vexes guard --uninstall # Remove shell wrappers
vexes guard --force -- npm install # Override HIGH warnings (CRITICAL still blocked)How it works:
- Snapshots current lockfile
- Runs
npm install --package-lock-only --ignore-scripts(dry-run) - Diffs the lockfile to find new/changed packages
- Runs behavioral analysis on those packages
- Blocks if CRITICAL, prompts on HIGH, allows if clean
- Runs the real install only after approval
Two modes for CI/CD and development:
# CI mode -- one-shot scan with GitHub Actions annotations
vexes monitor --ci # Default: fail on HIGH+
vexes monitor --ci --severity critical # Only fail on CRITICAL
vexes monitor --ci --sarif # SARIF output for GitHub Advanced Security
vexes monitor --ci --json # Machine-readable JSON
# Watch mode -- continuous local monitoring
vexes monitor --watch # Watch lockfiles + poll OSV hourly
vexes monitor --watch --interval 5 # Poll every 5 minutesGitHub Action:
# .github/workflows/vexes.yml
name: Dependency Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: penumbraforge/vexes@v0
with:
command: scan
severity: highOr run directly:
- name: Security scan
run: npx @penumbraforge/vexes monitor --ci --severity high
# With SARIF upload to GitHub Advanced Security:
- name: Security scan (SARIF)
run: npx @penumbraforge/vexes monitor --ci --sarif > results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifPlace in your project root. Walks up directories to find it.
{
"ecosystems": ["npm", "pypi"],
"severity": "high",
"ignore": [],
"analyze": {
"signals": {
"NO_REPOSITORY": "off",
"POSTINSTALL_SCRIPT": "off"
}
},
"cache": {
"dir": "~/.cache/vexes",
"advisoryTtlMs": 3600000,
"metadataTtlMs": 86400000
},
"output": {
"color": "auto",
"format": "text"
}
}Same format. Project config overrides user config.
vexes ships with built-in allowlists for packages with legitimate postinstall scripts (esbuild, sharp, puppeteer, etc.). Signals from these packages are downweighted, not suppressed -- a compromised version still triggers if new dangerous patterns appear.
bin/vexes.js CLI entrypoint, command router
src/
cli/
parse-args.js Hand-rolled arg parser (zero deps)
config.js Config loading with prototype pollution protection
output.js Terminal output, ANSI colors, spinner, sanitization
commands/
scan.js Vulnerability scanning via OSV
analyze.js 4-layer behavioral analysis
fix.js Verified fix recommendations
guard.js Pre-install lockfile diffing
monitor.js CI annotations, SARIF output, watch mode
core/
constants.js URLs, thresholds, exit codes
fetcher.js Single-point HTTP with retry/timeout/backoff
logger.js Leveled logger with terminal injection protection
allowlists.js Known-good packages, popular package sets
parsers/
npm.js package-lock.json v1/v2/v3, package.json fallback
pnpm.js pnpm-lock.yaml v6/v9
yarn.js yarn.lock v1 (classic) and v2+ (Berry)
pypi.js requirements.txt (-r recursive), poetry.lock, Pipfile.lock, pyproject.toml
cargo.js Cargo.lock
go.js go.sum
ruby.js Gemfile.lock
php.js composer.lock
dotnet.js packages.lock.json (NuGet)
java.js gradle.lockfile, pom.xml
brew.js Brewfile.lock.json, Brewfile
advisories/
osv.js OSV.dev batch queries, CVSS v3.1 scoring, severity mapping
npm-registry.js npm registry metadata + provenance attestations
pypi-registry.js PyPI JSON API metadata
analysis/
ast-inspector.js Acorn-based AST analysis (JS) + pattern matching (Python)
signals.js Signal orchestrator, composite risk scoring
dep-graph.js Dependency graph profiling, typosquat detection
behavioral.js Capability fingerprinting, version diffing
tarball-inspector.js Tarball download, tar parsing, source inspection
diff.js Lockfile snapshot diffing
provenance.js Sigstore provenance verification
cache/
advisory-cache.js SQLite-backed cache with TTL, corruption recovery
vendor/
acorn.mjs Vendored acorn parser (zero npm deps)
test/
test-ast-inspector.js AST detection + false positive tests
test-behavioral.js Behavioral profiling + diffing
test-cache.js SQLite cache, TTL, corruption resilience
test-dep-graph.js Typosquat detection
test-parse-args.js Argument parser
test-parsers.js All lockfile/manifest parsers
test-redteam.js 9 real-world attack reconstructions
test-robustness.js Input validation, edge cases, security
-
Fail loud, not clean. A security scanner that silently reports clean on failure is worse than useless. If queries fail, vexes exits with code 2 and prints
SCAN INCOMPLETE. Invalid ecosystems are rejected outright instead of silently scanning nothing. -
Zero dependencies. The dependency chain is the attack surface. vexes has none. Acorn is vendored. SQLite is Node.js built-in.
-
Terminal injection protection. All external data is sanitized with a comprehensive filter covering CSI sequences (with intermediate bytes), OSC (BEL and ST terminators), DCS/APC/PM/SOS sequences, C1 control codes (0x80-0x9F), and bare ESC bytes.
-
Prototype pollution protection. Config file merging rejects
__proto__,constructor, andprototypekeys. -
Command injection prevention. The guard command uses
execFileSync(no shell) with an allowlist of known package managers. Fix commands are shell-escaped before display. Guard setup resolves the vexes binary path at install time rather than usingnpxat runtime. -
Gzip bomb + SSRF protection. Tarball downloads enforce streaming size limits, HTTPS-only URLs, and a registry host allowlist to prevent memory exhaustion and SSRF attacks.
-
Cache integrity. Corrupted entries are auto-deleted. Degraded results are never cached. TTL is clamped to prevent config-based stale data attacks (max 7 days advisory, 30 days metadata).
-
Never recommend vulnerable fixes. The
fixcommand cross-checks every recommended version against OSV before presenting it. -
Critical signals are undisableable.
KNOWN_COMPROMISED,PHANTOM_DEPENDENCY,CIRCULAR_STAGING, andCAPABILITY_ESCALATIONcannot be turned off via config -- they detect active attacks. -
Allowlisted packages are still inspected. Known-good packages (esbuild, sharp, etc.) have their signals downweighted, not suppressed. AST analysis runs on all packages regardless of allowlist status.
-
Unicode homoglyph detection. Package names are checked for invisible characters (zero-width spaces, BIDI overrides) and non-ASCII homoglyphs that could disguise malicious packages.
-
Integrity-aware lockfile diffing. Guard detects when a package tarball changes without a version bump by comparing integrity hashes.
Apache-2.0
Shadoe Myers (@penumbraforge)