🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

shadowaudit

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

shadowaudit

Static API security scanner — detects undocumented and unauthenticated Express.js routes before they reach production

latest
Source
npmnpm
Version
1.0.2
Version published
Weekly downloads
1.2K
36%
Maintainers
1
Weekly downloads
 
Created
Source

shadowaudit

Static API security scanner — finds undocumented and unauthenticated routes before they reach production

GitHub Marketplace npm version npm downloads CI License: MIT Node.js

🌐 Documentation & Demo → · 🛒 GitHub Marketplace →

Installation

npm install -g shadowaudit

npx (no install needed)

npx shadowaudit --dir ./src --spec ./openapi.json

Auto-generate an OpenAPI spec (new!)

Don't have a spec yet? Generate one from your code:

shadowaudit --dir ./src --framework express --generate-spec > openapi.json

Then review the generated spec, fill in response schemas, and use it for delta comparison.

GitHub Action

Add to .github/workflows/security.yml:

- uses: darkmaster0345/shadow-Audit@v1.0.0
  with:
    dir: './src'
    spec: './openapi.json'
    fail-on: 'critical'

Real-World Testing

shadowaudit has been tested against real production codebases to validate accuracy and guide the roadmap:

TryGhost/Ghost — production publishing platform (Express.js)

  • 269 routes detected across admin API, content API, members API, and webhooks
  • 235 routes correctly authenticated (v0.3.0 AST-based detection catches custom authAdminApi middleware)
  • 0 config.get() false positives (v0.3.0 object-name filtering eliminates these)
  • 34 routes legitimately public (session, authentication, setup, webhooks — verified manually)

tiangolo/fastapi — FastAPI framework docs (FastAPI)

  • 419 routes detected across documentation source examples
  • All route patterns correctly parsed: @app.get(), @router.post(), path parameters, Depends() auth
  • Auto-spec generation tested: --generate-spec produces valid OpenAPI 3.0.3 from scanned routes

expressjs/express — Express framework examples (Express.js)

  • 58 routes detected across 15+ example applications
  • Mount prefix reconciliation working: USE /api/v1 and USE /api/v2 correctly identified
  • Auth detection working: POST /login correctly flagged as [auth]

pallets/flask — Flask framework source (Flask)

  • 3 internal routes detected in Flask's own source code
  • Flask <param> syntax correctly converted to {param}
  • Blueprint routes (@bp.route()) detected

hagopj13/node-express-boilerplate (Express.js)

  • 18 routes detected across auth, user, and docs modules
  • v0.2.1 auth( pattern fix correctly detected auth on 6 user routes that v0.2.0 missed
  • Mount prefix reconciliation working for app.use('/prefix', require('./router')) pattern

mastodon/mastodon — federated social network (Rails + Grape)

  • 766 routes detected (743 Rails + 23 from concern expansion)
  • 0 unknown#unknown controller actions (v0.9.0 concern expansion eliminated all 5 false positives from v0.8.2)
  • Controller inheritance chain walking correctly detected auth in 385 routes (v0.8.0 had 96 — 57% false-positive reduction)
  • Community OpenAPI spec comparison: 601 undocumented shadow routes found (the community spec only covers the public REST API, not web/admin/settings routes)

gitlabhq/gitlabhq — DevSecOps platform (Rails + Grape)

  • 2,449 routes detected (1,423 Rails web routes + 1,017 Grape API routes + 9 other)
  • 1,017 /api/v4/ routes from Grape lib/api/*.rb files (v0.9.0 Grape scanner)
  • 813 routes with auth detected via Grape before do authenticate! end blocks
  • Mount chain resolution working: mount ::API::Groupslib/api/groups.rb with /api/v4 prefix
  • GitLab's OpenAPI spec has 1,144 paths — 2,136 undocumented shadow routes found

These real-world tests directly shaped the roadmap — each limitation found is now a tracked improvement with priority and effort ratings.

The Problem

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, invisible to your security team.

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.

What shadowaudit does

  • Scans your codebase for all actual route definitions (Express.js, FastAPI, and Django supported)
  • Compares them against your OpenAPI 3.x / Swagger 2.0 spec
  • Flags any route missing from the documentation
  • Detects whether auth middleware is applied to each route
  • Fails your CI/CD pipeline before the PR merges — with exit codes that respect configurable severity thresholds

Severity levels

SeverityConditionCI behavior (--fail-on critical)
CRITICALUndocumented route + no auth middlewarePipeline fails
HIGHUndocumented route + auth middleware presentPipeline passes (review recommended)
INFOInformational findingPipeline passes

Supported Frameworks

  • Express.js ✅ (AST-based route extraction via Babel, mount prefix reconciliation, auth detection)
  • FastAPI ✅ (decorator-based route extraction, Depends() auth detection, include_router prefix reconciliation)
  • Django ✅ (path()/re_path()/url() extraction, DRF router support, class-based views (ModelViewSet, APIView, generics), include() prefix reconciliation, cross-file auth via views.py)
  • Flask ✅ (@app.route() / @bp.route() decorator extraction, methods= multi-method (list + tuple syntax), @login_required auth detection)
  • NestJS ✅ (@Controller() + @Get() / @Post() decorator extraction, @UseGuards() auth detection, @Public() override, controller prefix + method path combination)
  • Rails ✅ (v0.8.0+ — config/routes.rb block-aware parser: namespace, scope, resources/resource macros, member/collection blocks, devise_for, draw(:subfile), cross-file auth via before_action with controller inheritance chain walking, concern expansion)
  • Grape ✅ (v0.9.0+ — lib/api/*.rb Grape API scanner: resource, namespace, route_param, before auth blocks, mount cross-file resolution, prefix/version support. Auto-invoked when lib/api/ exists. Tested on GitLab: 1,017 /api/v4/ routes detected)

Installation

npm install -g shadowaudit

Or use it locally in your project:

npm install --save-dev shadowaudit

Quick Start

# Scan your codebase against your OpenAPI spec
shadowaudit --dir ./src --spec ./openapi.json

# Force a specific framework
shadowaudit --dir ./src --spec ./openapi.json --framework express

# Only show NEW findings since last scan (perfect for PRs)
shadowaudit --dir ./src --spec ./openapi.json --diff

# Don't have a spec? Generate one from your code
shadowaudit --dir ./src --framework express --generate-spec > openapi.json

# Output as JSON (pipe to jq for CI integrations)
shadowaudit --dir ./src --spec ./openapi.json --format json | jq '.findings | length'

# Output as SARIF 2.1.0 (for GitHub Code Scanning)
shadowaudit --dir ./src --spec ./openapi.json --format sarif > results.sarif

CLI Options

Usage: shadowaudit [options]

Static API security scanner for undocumented routes

Options:
  -V, --version       output the version number
  --dir <path>        Directory to scan (default: current directory)
  --spec <path>       Path to OpenAPI/Swagger spec file
  --format <type>     Output format: table, json, sarif (default: "table")
  --fail-on <level>   Fail CI pipeline on: critical, high, info (default: "critical")
  --framework <name>  Force framework: express, fastapi, django, flask, nestjs, rails (Grape auto-detected) (default: "auto")
  --generate-spec     Generate OpenAPI spec from scanned routes (no comparison)
  --diff              Show only new findings since last scan (requires --spec)
  --concurrency <n>   Parallel file scan concurrency (default: 8, max: 64)  [v0.7.0+]
  --watch             Watch for file changes and re-scan automatically  [v0.7.0+]
  -h, --help          display help for command

--diff mode (v0.6.0+)

Show only new findings since the last scan — perfect for PR workflows where you don't want to see the same 50 pre-existing findings on every PR.

# First run — full scan, cache saved
shadowaudit --dir ./src --spec ./openapi.json --diff

# Second run — only NEW findings shown
shadowaudit --dir ./src --spec ./openapi.json --diff
  • Cache stored at .shadowaudit-cache.json (auto-added to .gitignore)
  • Resolved findings shown as green ✓ section
  • Exit code based on NEW findings only (pre-existing findings don't fail CI)
  • For GitHub Actions: use actions/cache to persist .shadowaudit-cache.json between runs

--watch mode (v0.7.0+)

Re-scan automatically whenever you save a file — perfect for dev-time feedback while you're writing new routes.

shadowaudit --dir ./src --spec ./openapi.json --watch
  • Debounced 300ms — bursty saves (editor "save all", formatter runs) trigger one re-scan, not five
  • Ctrl+C exits cleanly with exit code 0 (no orphaned processes)
  • Works with --diff to show only NEW findings per save
  • Only watches .js, .ts, .jsx, .tsx, .py files; ignores node_modules, dist, __pycache__, .git
  • Cannot be combined with --generate-spec or --format json/sarif (table only — JSON isn't streamable)

--concurrency <n> (v0.7.0+)

Control how many files are read in parallel during a scan. Defaults to 8, clamped to [1, 64].

# Use higher concurrency on a beefy CI box
shadowaudit --dir ./src --spec ./openapi.json --concurrency 16

# Force sequential (same as v0.6.1 behavior)
shadowaudit --dir ./src --spec ./openapi.json --concurrency 1
  • Output is deterministic regardless of concurrency (verified by adversarial QA — concurrency=1 and concurrency=64 produce byte-identical route arrays)
  • Invalid values (0, negative, non-numeric) degrade gracefully to the default
  • 3-5× speedup on large codebases (Ghost CMS: 4s → ~1s)

--generate-spec (v0.4.0+)

Don't have an OpenAPI spec? Generate one from your code:

shadowaudit --dir ./src --framework express --generate-spec > openapi.json

Exit codes

ConditionExit code
No findings0
Findings below --fail-on threshold0
Findings at or above --fail-on threshold1
--fail-on valueFails when
critical (default)Any CRITICAL finding
highAny CRITICAL or HIGH finding
infoAny finding at all

Configuration

shadowaudit loads config from two sources (CLI flags take priority):

  • CLI flags (highest priority)
  • .shadowaudit.yml or .shadowauditrc.yml in your project root

📖 Full configuration documentation → — all fields, examples, and troubleshooting.

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)

Even without custom config, shadowaudit detects these auth middleware patterns:

.authenticate(    .authorize(       requireAuth        isAuthenticated
verifyToken       checkAuth         ensureLoggedIn     passport.authenticate
jwt.verify        bearerAuth        apiKeyAuth         basicAuth

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": "1.0.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"
    }
  ],
  "documentedRoutes": [...]
}

SARIF 2.1.0

Industry-standard format for GitHub Code Scanning, Azure DevOps, and other security dashboards. Maps findings to two rules:

Rule IDSeverityLevel
SHADOW001CRITICALerror
SHADOW002HIGHwarning
SHADOW002INFOnote

Demo

Below is the actual terminal output from an end-to-end scan. The test project has 7 Express routes — 5 documented in the OpenAPI spec, 2 undocumented shadow routes with no auth middleware:

[INFO] No config file found, using defaults
╔════════════════════════════════╗
║     shadowaudit v1.0.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

In the terminal, severity cells are color-coded:

  • CRITICAL → red bold
  • HIGH → yellow bold
  • INFO → blue
  • METHOD → cyan
  • AUTH YES → green / NO → red

How It Works

┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│  Express Scanner │     │  Spec Parser     │     │  Delta Comparator│     │  Formatter       │
│  (Babel AST)     │     │  (OpenAPI/Swagger)│     │  (normalize +   )│     │  (table/json/    )│
│                  │     │                  │     │  ( match routes )│     │  (sarif)         )│
│  src/**/*.js     │────▶│  openapi.json    │────▶│  Route[] vs      │────▶│  Findings[]      │
│  src/**/*.ts     │     │  swagger.yaml    │     │  DocumentedRoute[]│     │  + stats         │
│                  │     │                  │     │                  │     │                  │
│  + auth detector │     │  + basePath      │     │  + severity      │     │  + exit code     │
│  (window scan)   │     │  + serverPrefix  │     │  scoring         │     │  logic           │
└──────────────────┘     └──────────────────┘     └──────────────────┘     └──────────────────┘
  • Express scanner uses @babel/parser + @babel/traverse to walk every .js/.ts file'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 (inline or in surrounding scope), 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 basePath and OpenAPI servers[0].url prefixes, then scores each undocumented route as CRITICAL (no auth) or HIGH (has auth)
  • Formatter renders the report as a colored table, JSON, or SARIF 2.1.0

CI/CD Integration

GitHub Actions

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
      - name: Fail on critical
        run: npx shadowaudit --dir ./src --spec ./openapi.json --fail-on critical

Pre-commit hook

#!/bin/bash
shadowaudit --dir ./src --spec ./openapi.json --fail-on critical

Project Structure

shadowaudit/
├── src/
│   ├── index.ts              # CLI entry point
│   ├── action.ts             # GitHub Action entry point
│   ├── config.ts             # cosmiconfig loader (CLI flags + .shadowaudit.yml)
│   ├── comparator.ts         # Delta comparator (route diffing + severity scoring)
│   ├── diff.ts               # --diff mode (local cache + new/resolved detection)
│   ├── types.ts              # Shared TypeScript interfaces
│   ├── scanners/
│   │   ├── express.ts        # Express.js route extractor (Babel AST + mount prefixes)
│   │   ├── fastapi.ts        # FastAPI route extractor (decorators + include_router)
│   │   ├── django.ts         # Django route extractor (path() + CBV + cross-file auth)
│   │   ├── flask.ts          # Flask route extractor (@app.route + methods=)
│   │   └── auth.ts           # Auth middleware detector (AST + string-based)
│   ├── parsers/
│   │   └── spec.ts           # OpenAPI 3.x / Swagger 2.0 parser
│   ├── formatters/
│   │   ├── table.ts          # Colored terminal table
│   │   ├── json.ts           # JSON output
│   │   ├── sarif.ts          # SARIF 2.1.0 output
│   │   └── index.ts          # Formatter dispatcher
│   ├── generators/
│   │   └── spec.ts           # Auto-spec generation (--generate-spec)
│   ├── github/
│   │   └── comment.ts        # PR comment bot
│   └── utils/
│       └── logger.ts         # Colored console output
├── tests/                    # 139 tests across 16 files
│   ├── config.test.ts
│   ├── comparator.test.ts
│   ├── diff.test.ts
│   ├── scanners/
│   │   ├── express.test.ts
│   │   ├── fastapi.test.ts
│   │   ├── django.test.ts
│   │   ├── django-cbv.test.ts
│   │   ├── flask.test.ts
│   │   └── auth.test.ts
│   ├── parsers/
│   │   └── spec.test.ts
│   ├── generators/
│   │   └── spec.test.ts
│   ├── formatters/
│   │   ├── table.test.ts
│   │   └── sarif.test.ts
│   └── github/
│       └── comment.test.ts
├── docs-site/                # Docusaurus v3 documentation site
├── .github/workflows/
│   ├── shadowaudit.yml       # CI: build + test + self-scan
│   └── deploy-docs.yml       # Docs auto-deploy to GitHub Pages
├── action.yml                # GitHub Marketplace Action definition
├── ROADMAP.md                # v0.8.0 → v1.0.0 roadmap
├── CONTRIBUTING.md           # How to add framework scanners
├── package.json
├── tsconfig.json
├── LICENSE
└── README.md

Development

# Install dependencies
npm install

# Build
npm run build

# Run tests (139 tests across 16 test files)
npm test

# Run in dev mode
npm run dev -- --dir ./src --spec ./openapi.json

# Lint
npm run lint

Tech Stack

  • TypeScript — strict mode, ES2020 target, CommonJS modules
  • Babel (@babel/parser + @babel/traverse) — AST-based route extraction with error recovery
  • Commander.js — CLI argument parsing
  • chalk — terminal colors
  • cli-table3 — terminal table rendering
  • js-yaml — Swagger/OpenAPI YAML parsing
  • cosmiconfig — config file discovery
  • glob — recursive file scanning
  • Vitest — test runner

Privacy & Network Access

shadowaudit runs entirely locally — it reads your source files and OpenAPI spec on disk. No code is uploaded, no telemetry is sent, no phone-home.

The only network call is when using the GitHub Action's PR comment bot (post-comment: 'true'), which posts the findings summary to the GitHub API (api.github.com) using github.token. This is the standard GitHub Actions pattern and uses your repo's own token — no external servers are contacted.

Socket.dev audit: ✅ Vulnerability 100/100 · ✅ Quality 100/100 · ✅ License 100/100

License

MIT © Ubaid ur Rehman 2026

Keywords

security

FAQs

Package last updated on 18 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts