Skip to main content

Scanning your app: penthera <url> and --repo

Starter. For “is my app safe to ship?”
Black-box scans of a running app (localhost, preview, or staging) and white-box scans of its source. The default scan is non-destructive. Payload-based testing is opt-in.

Scan modes and profiles

Pick a profile, or add individual flags for more depth: What each profile sends to /api/login: quick nothing; standard one login with obviously invalid credentials, to catch a login that accepts anything; --deep also 8 rapid attempts (rate limiting) and an ' OR '1'='1 string (SQL errors). A CDN firewall can read the last two as an attack and start challenging you, so they only run with --deep. Individual flags: --recon, --deep, --fuzz, --adaptive, --nuclei <path>, --templates <dirs>, --all

Active modes ask first

--deep, --fuzz, --adaptive, --all and --profile deep send test payloads (and, with --deep, the login burst above), so they need your confirmation before the first request:
  • In a terminal, Penthera asks. For a target on this machine (localhost, 127.0.0.1, *.localhost) a yes is enough; for any other target, type its hostname back.
  • In CI, Docker or a script, pass --confirm-target <host> with the target’s hostname. Without it, or with a different host, the run stops with exit code 2 before anything is sent.
  • Active scans send at most 10 requests per second. Change that with --rate <n>; --rate 0 removes the limit. --rate also works on a standard scan, which has no limit by default.

Quick start

Writing to -o reports/scan.json also drops a human-readable reports/scan.md next to it. penthera init can add reports/ to your .gitignore.

Exit codes


Output

Coverage: what a repo scan read

Every --repo scan adds a Coverage section to the terminal, Markdown and JSON (coverage) reports. For each check (the secret scan, route and code-pattern analysis, runtime versions) it says how many files were read and why the others were not: file type not read, over the size limit, a language route analysis does not support, or no API routes found. Source files no code analysis read are listed as blind spots, up to 200. A file that was not read is unchecked, not clean. Dependency behaviour and agent config have their own commands (penthera deps, penthera agents) and are listed as not part of a repo scan.

Library mode: scanning a package

In an app, untrusted data arrives in a request. In a library it arrives as an argument: whoever calls an exported function decides what its parameters hold. In library mode the repo scan treats those parameters as untrusted too.
Without either flag, library mode turns on by itself when package.json looks publishable: it has main, exports or bin (or an index.js next to it), is not "private": true, and does not depend on an app framework such as Express, Koa, Fastify or Next.js. The report says when it is on, and why. How it works. Penthera finds the functions a caller can reach (module.exports, exports.x, prototype and class methods of an exported constructor, objects and instances returned by an exported factory, members copied onto exports in a loop, ESM export). It follows each parameter through assignments, closures and callbacks inside that function, and one call further: when the function passes the value to a named function in the same file, that function’s parameter is followed too. A value a constructor or setter keeps on this (this.path = path) is followed into the other methods of the same class or prototype. A shell command built from one is reported as command-injection at potential confidence and severity high. A function whose whole job is to run its argument (exec(cmd)) is reported at severity medium, with the note that callers must not pass untrusted input. In library mode the scan also reads dist/ or build/ when package.json points there, and skips tooling, examples and docs folders. How we know. On the dev half of SecBench.js (49 command injections in npm packages, the half we tuned on), strict recall went from 0.00 to 0.82, 95% CI 0.69 to 0.90 (40 of 49), in the first version. The helper step and fields on this took it to 0.94, 95% CI 0.83 to 0.98 (46 of 49); 39 of 49 without the “runs its argument” cases. It flagged no command injection in 150 npm packages of the benign baseline (dev half), and nothing new in 16 app repositories. The sealed test half is not scored yet. Limits. Only one helper call is followed, and only to a function the file calls by name. A field set from another field is not followed. Sanitizers are not recognized, so a correctly escaped command can still be reported; that is what potential means.

CI integration

Copy the full workflow from docs/examples/github-actions-scan.yml into your app’s repo. Pin the Penthera version so a scheduled run never picks up code you have not reviewed. Penthera’s own CI runs the full test suite on Node 22 and 24, self-scans a mock vulnerable API on every push, and runs CodeQL and OpenSSF Scorecard.

Configuration

For live integration tests against your own routes:
Edit the route groups, then run npm run pentest:live. Set PENTEST_BASE_URL to point at staging without editing the file. Back to the README · All commands