# Audit a live site's JavaScript

Scan a live URL for exposed secrets, leaked source maps, weak protection, dangerous patterns and known CVEs. The same scanner that powers the public security scanner, from your terminal.

Source: https://www.afterpack.dev/docs/audit

`npx afterpack@latest audit <url>` fetches the JavaScript a site ships and reports what a reader can pull out of it. It is the same scanner that powers the public [security scanner](https://www.afterpack.dev/security-scanner): exposed secrets, leaked source maps, protection level, dangerous patterns, detected stack, and known CVEs.

Audit is read-only. It modifies nothing, needs no [account](https://www.afterpack.dev/docs/accounts), and touches only what the URL serves publicly.

> **A separate tool from the obfuscation engine**
>
> `audit` does not run [the obfuscation engine](https://www.afterpack.dev/docs/concepts). It fetches a site's shipped assets and analyzes what is already exposed. None of the [build flags](https://www.afterpack.dev/docs/cli#flags) apply to it.

## Run it

```bash
npx afterpack@latest audit https://yoursite.com
```

The scheme is optional: a bare hostname is normalized to `https://`. The scanner fetches the page, renders it so client-side-loaded bundles are included, then analyzes every JavaScript resource it found.

> **Live URLs only**
>
> There is no local-directory mode. `npx afterpack@latest audit ./dist` is not supported. The argument must be a URL. Audit a preview deployment instead.

## What it checks

| Check | What it finds |
| --- | --- |
| **Exposed secrets** | AWS keys, private keys, JWTs, Stripe / OpenAI / GitHub / GitLab / Slack / Google / SendGrid / Twilio / Mailgun / LaunchDarkly credentials, database URLs, bearer tokens, internal URLs, Firebase config, and high-entropy strings, around two dozen patterns. |
| **Exposed source maps** | Shipped `.map` files reachable from the bundle. |
| **Protection level** | How protected the shipped JavaScript is: plain, minified only, or actually obfuscated. The per-token version of this question is the [Protection Map](https://www.afterpack.dev/docs/protection-map). |
| **Dangerous patterns** | `eval()`, the `Function` constructor, `document.write`, `innerHTML` assignment and similar high-risk JavaScript APIs found in the shipped bundles. |
| **Detected tech stack** | Frameworks and libraries fingerprinted from the bundle. |
| **Known CVEs** | Published vulnerabilities in the dependency versions it detects. |

### Exposed source maps

A source map chains your shipped bundle back to your original, readable source. Never ship one to production: an attacker downloads it and reads your code as you wrote it.

Audit catches it. AfterPack itself writes no `.map` sibling in a [production](https://www.afterpack.dev/docs/config#build-mode) build by default (see [`sourceMap.enabled`](https://www.afterpack.dev/docs/config#sourceMap-enabled) and [`sourceMap.emitUrl`](https://www.afterpack.dev/docs/config#sourceMap-emitUrl)), but it cannot stop your bundler from having already emitted one.

## Reading the results

The CLI prints each phase as it completes, surfaces `critical` and `high` findings inline as they arrive, then closes with a summary box: score out of 100, resource counts, findings by severity, detected stack, and readability. It ends with a list of every critical finding and a link to the full report on the web.

Read it top-down:

1. **Secrets and source maps first.** These are the findings that make obfuscation pointless. Rotate any leaked secret, then stop shipping it; remove any `.map` from production.
2. **Protection level next.** If the bundle reads as plain or merely minified, it has no meaningful protection. Run a build through AfterPack ([Quickstart](https://www.afterpack.dev/docs/quickstart) takes about five minutes).
3. **CVEs and dangerous patterns last.** Triage by severity; not every match is exploitable, but each is something a reader can see.

Output is human-readable text by default. `--diagnostics.format=json` puts one JSON document on stdout instead — the findings, the score and the report link — with every human-readable line moved to stderr; `--diagnostics.level=none` silences the progress phases on top of that. The shareable report is always the web link the run ends with.

## Exit codes

| Code | When |
|---|---|
| `0` | The scan completed. **Including when it found critical issues.** |
| `1` | The scan failed: an unreachable URL, a server-side error, the daily rate limit, or a stream that ended without completing. |
| `64` | Misuse: no URL given, more than one URL, or the URL is malformed. |

These are audit's own codes. The [build exit codes](https://www.afterpack.dev/docs/diagnostics#exit-codes) an obfuscation run uses are separate.

> **Audit is not a CI gate**
>
> Because a completed scan exits `0` regardless of what it found, `npx afterpack@latest audit … && deploy` does not fail on findings. There is no severity threshold flag: read the findings from `--diagnostics.format=json` yourself if you want to gate on them. Use audit as a pre-release review step you read manually. The obfuscation run is [the part that fails a build](https://www.afterpack.dev/docs/builds).

Unauthenticated scans are limited to **3 per IP per 24 hours**. Hitting the limit prints how long to wait and exits `1`.

## Next

- [Builds & CI](https://www.afterpack.dev/docs/builds): baking obfuscation into your release pipeline, and pinning a seed for reproducible output.
- [Best practices](https://www.afterpack.dev/docs/best-practices): what to mark, what to leave alone, and the source-map rule.
- [Security scanner](https://www.afterpack.dev/security-scanner): the same checks in your browser, no install, with the full shareable report.
- [Protection Map](https://www.afterpack.dev/docs/protection-map): the per-token view of your own build, once you're obfuscating.
- [What AfterPack does not replace](https://www.afterpack.dev/docs/threat-model#what-afterpack-does-not-replace): a leaked secret needs rotation; obfuscating it after the fact does not help.
