Shadowaudit 0.1.0: Catch Shadow APIs Before They Hit Production
Developers spin up quick test endpoints and forget to remove them. These shadow APIs bypass documentation, skip auth middleware, and get deployed to production silently — invisible to network scanners and security teams. Shadowaudit solves this by statically analyzing your codebase, comparing every route definition against your OpenAPI/Swagger spec, and failing your CI pipeline before the PR merges if it finds undocumented or unauthenticated routes.
Shadowaudit v0.1.0 supports Express.js (full AST-based route extraction via Babel), with FastAPI and Django support coming soon. It can be installed globally (npm install -g shadowaudit) or used via npx (npx shadowaudit --dir ./src --spec ./openapi.json). It also ships as a GitHub Action for easy CI integration.
How It Works
Shadowaudit's pipeline has four stages:
- Express Scanner — Uses
@babel/parserand@babel/traverseto walk every.js/.tsfile's AST and extract route definitions (app.get(),router.post(),router.route().get().post()chaining, etc.). - Auth Detector — Scans a ±15-line window around each route for auth middleware patterns (e.g.,
.authenticate(,requireAuth,passport.authenticate), with sibling-route lines stripped to avoid false positives. - Spec Parser — Reads OpenAPI 3.x / Swagger 2.0 files (JSON or YAML), extracts documented routes, and normalizes path syntax (
{id}→:id). - Delta Comparator — Cross-references scanned routes against documented routes, reconciling Swagger
basePathand OpenAPIservers[0].urlprefixes, then scores each undocumented route as CRITICAL (no auth) or HIGH (has auth).
Severity Levels and CI Behavior
| Severity | Condition | CI behavior (--fail-on critical) |
|---|---|---|
| CRITICAL | Undocumented route + no auth middleware | Pipeline fails |
| HIGH | Undocumented route + auth middleware present | Pipeline passes (review recommended) |
| INFO | Informational finding | Pipeline passes |
The --fail-on flag (default: critical) controls the threshold. With --fail-on high, both CRITICAL and HIGH findings fail the pipeline. With --fail-on info, any finding fails.
CLI Options
Usage: shadowaudit [options]
Options:
-V, --version output the version number
--dir Directory to scan (default: current directory)
--spec Path to OpenAPI/Swagger spec file
--format Output format: table, json, sarif (default: "table")
--fail-on Fail CI pipeline on: critical, high, info (default: "critical")
--framework Force framework: express, fastapi, django (default: "auto")
-h, --help display help for command
Output Formats
Table (default) — Human-readable colored table with severity, method, path, file, line, and auth status. Includes a summary box and pipeline recommendation.
JSON — Structured JSON for CI dashboards and log aggregators:
{
"scanMeta": {
"tool": "shadowaudit",
"version": "0.1.0",
"timestamp": "2026-01-15T12:00:00.000Z",
"stats": { "total": 7, "documented": 5, "undocumented": 2, "critical": 2, "high": 0, "info": 0 }
},
"findings": [
{
"severity": "CRITICAL",
"method": "GET",
"path": "/api/debug/reset",
"file": "routes.js",
"line": 21,
"hasAuth": false,
"reason": "Undocumented route with no authentication middleware detected — publicly accessible shadow API"
}
]
}
SARIF 2.1.0 — Industry-standard format for GitHub Code Scanning, Azure DevOps, and other security dashboards. Maps findings to two rules: SHADOW001 (CRITICAL, error) and SHADOW002 (HIGH/INFO, warning/note).
Configuration
Shadowaudit loads config from two sources (CLI flags take priority):
- CLI flags (highest priority)
.shadowaudit.ymlor.shadowauditrc.ymlin your project root
Example .shadowaudit.yml:
spec: ./openapi.json
dir: ./src
format: table
failOn: critical
authPatterns:
- myCustomAuth
- requireLogin
ignore:
- node_modules
- dist
- build
Default auth patterns (always detected): .authenticate(, .authorize(, requireAuth, isAuthenticated, verifyToken, checkAuth, ensureLoggedIn, passport.authenticate, jwt.verify, bearerAuth, apiKeyAuth, basicAuth.
Demo Output
In a test project with 7 Express routes — 5 documented, 2 undocumented shadow routes with no auth — the tool produced:
[INFO] No config file found, using defaults
╔════════════════════════════════╗
║ shadowaudit v0.1.0 ║
║ API Shadow Route Scanner ║
╚════════════════════════════════╝
[INFO] Directory : /tmp/auth-test
[INFO] Spec file : /tmp/auth-test/openapi.json
[INFO] Format : table
[INFO] Fail on : critical
[INFO] Framework : express
[INFO] Running Express.js route scanner...
[✓] Found 7 routes in /tmp/auth-test
[INFO] GET /public/health → routes.js:6 [auth]
[INFO] GET /public/products → routes.js:7 [auth]
[INFO] GET /api/users → routes.js:9 [auth]
[INFO] POST /api/users → routes.js:13 [auth]
[INFO] GET /api/admin/dashboard → routes.js:17 [auth]
[INFO] GET /api/debug/reset → routes.js:21 [no auth]
[INFO] POST /api/test/seed → routes.js:22 [no auth]
[INFO] Detected spec: OpenAPI 3.x
[INFO] Spec contains 5 documented routes
[INFO] ──────────────────────────────────────────────────
[INFO] Total routes scanned : 7
[INFO] Documented : 5
[INFO] Undocumented : 2
[INFO] ──────────────────────────────────────────────────
╔══════════════════════════════════════════════╗
║ shadowaudit — Scan Report ║
╚══════════════════════════════════════════════╝
┌──────────┬────────┬───────────────────────────────────┬───────────────────────────────────┬──────┬──────┐
│ SEVERITY │ METHOD │ PATH │ FILE │ LINE │ AUTH │
├──────────┼────────┼───────────────────────────────────┼───────────────────────────────────┼──────┼──────┤
│ CRITICAL │ GET │ /api/debug/reset │ routes.js │ 21 │ NO │
├──────────┼────────┼───────────────────────────────────┼───────────────────────────────────┼──────┼──────┤
│ CRITICAL │ POST │ /api/test/seed │ routes.js │ 22 │ NO │
└──────────┴────────┴───────────────────────────────────┴───────────────────────────────────┴──────┴──────┘
┌─────────────────────────────────────────────┐
│ SCAN SUMMARY │
│ Total routes scanned : 7 │
│ Documented : 5 │
│ Undocumented : 2 │
│ ───────────────────────────────────────── │
│ 🔴 CRITICAL : 2 │
│ 🟡 HIGH : 0 │
│ 🔵 INFO : 0 │
└─────────────────────────────────────────────┘
⛔ Pipeline will FAIL — 2 critical shadow route(s) detected
Exit code: 1
CI/CD Integration
GitHub Actions — Add a workflow file that runs on pull requests:
name: Security Scan
on: [pull_request]
jobs:
shadowaudit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx shadowaudit --dir ./src --spec ./openapi.json --format sarif > shadowaudit.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: shadowaudit.sarif
What's Next
The author plans to add FastAPI and Django support. For now, if you're using Express.js, you can drop Shadowaudit into your CI pipeline today. Install it and run npx shadowaudit --dir ./src --spec ./openapi.json to catch shadow APIs before they reach production.



