npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

shadowaudit

v0.7.0

Published

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

Readme

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

Global CLI (recommended)

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/[email protected]
  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

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

| 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 |


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)

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 (default: "auto")
  --generate-spec     Generate OpenAPI spec from scanned routes (no comparison)
  --diff              Show only new findings since last scan (requires --spec)
  -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

--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

| Condition | Exit code | |-----------|-----------| | No findings | 0 | | Findings below --fail-on threshold | 0 | | Findings at or above --fail-on threshold | 1 |

| --fail-on value | Fails when | |-------------------|------------| | critical (default) | Any CRITICAL finding | | high | Any CRITICAL or HIGH finding | | info | Any finding at all |


Configuration

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

  1. CLI flags (highest priority)
  2. .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": "0.6.1",
    "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 ID | Severity | Level | |---------|----------|-------| | SHADOW001 | CRITICAL | error | | SHADOW002 | HIGH | warning | | SHADOW002 | INFO | note |


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 v0.7.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           │
└──────────────────┘     └──────────────────┘     └──────────────────┘     └──────────────────┘
  1. 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.)
  2. 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
  3. Spec parser reads OpenAPI 3.x / Swagger 2.0 files (JSON or YAML), extracts documented routes, and normalizes path syntax ({id}:id)
  4. 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)
  5. 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.7.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