> ## Documentation Index
> Fetch the complete documentation index at: https://docs.breachlens.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Scan a web app

> Run DAST and an autonomous pentest against a running application — authenticated, authorized, and with reproducible Proof-of-Exploit.

Where code scanning reads your source, **domain scanning tests the running application from the outside in** — the way an attacker sees it. Two tiers share one target:

* **DAST** — fast active scanning for XSS, injection, CSRF, XXE, and auth/session flaws.
* **Autonomous pentest** — multi-tool exploit orchestration that goes past detection to *exploitation*, and ships **Proof-of-Exploit**: the exploited request and response, with a reproducible `curl` or a replay video depending on the exploit class.

## Add a domain

Open **Domains → Add domain** and enter the host — a full name, a bare hostname, or `host:port`. Nothing else is needed to start a DAST scan.

## Authorize before you attack

<Warning>
  **Active scanning sends real attack traffic.** BreachLens will not run the autonomous pentest against a domain until an admin has explicitly **authorized** it — confirming you own it or have written permission to test it. Open the domain, choose **Authorize**, and confirm. DAST also should only ever run against systems you're permitted to test.
</Warning>

This gate is enforced server-side: a pentest request against an un-authorized domain is refused. It's there to keep you on the right side of the line — pointing an exploit engine at something you don't own is a crime, not a scan.

## Run DAST

<Steps>
  <Step title="Open the domain">
    Go to **Domains** and click the target, then open the **Scan** tab.
  </Step>

  <Step title="Run the scan">
    Start a **DAST** scan. It crawls the app and actively probes what it finds; expect results in minutes for a typical site.
  </Step>
</Steps>

## Authenticated scanning

Most of an app lives behind login. BreachLens reaches it two ways — pick by how the app authenticates:

<CardGroup cols={2}>
  <Card title="Credentials (breadth)" icon="key">
    Give BreachLens the login and it authenticates itself, then crawls widely. Best when a form login or token gets you in.
  </Card>

  <Card title="Recorded session (depth)" icon="record-vinyl">
    Proxy your browser through BreachLens and click through the exact flows that matter. Best for SPAs, multi-step wizards, and anything a crawler can't reach on its own.
  </Card>
</CardGroup>

### Credentials

On the domain's auth config (admin-gated), configure one of:

* **Form login** — login URL, the username/password field names, your credentials, and a logged-in / logged-out text pattern so BreachLens can tell it's still authenticated.
* **JSON API login (SPA / JWT)** — for apps that authenticate through a JSON API and return a token in the response *body* instead of a `Set-Cookie` — the norm for React, Vue, and Angular single-page apps. Point the login URL at the JSON login endpoint itself (e.g. `/rest/user/login`, **not** the SPA's home page); BreachLens posts your credentials as JSON, reads the token back from the response, and authenticates the whole scan as a bearer.
* **Bearer / header token** — a header name + value injected on every request.
* **OAuth2** — token URL, client id/secret, scope, and grant type (`client_credentials` or `password`).

Credentials are encrypted at rest and never returned; the config only shows whether credentials are set, never the values themselves.

<Note>
  **A single-page app needs its API endpoint, not its front door.** A SPA served from `/` returns the same HTML shell for every path, so a login URL pointing at the app root can never authenticate. Use the URL your browser's network tab shows when you actually sign in — a `POST` to something like `/rest/user/login` or `/api/auth/login`. BreachLens tolerates the usual field-name variations (`email`, `username`, `user`, `login`), so a token still comes back even if your config labels the field differently than the API expects.
</Note>

### Recorded session

For flows a crawler can't drive, record one:

<Steps>
  <Step title="Start recording">
    On the domain, **Start recording**. BreachLens opens a capture session and gives you a CA certificate to trust so your browser can proxy through it.
  </Step>

  <Step title="Click the flows that matter">
    Log in and walk the app — the authenticated pages, the multi-step forms, the endpoints you care about. A live pane shows captured requests; a self-test flags if capture isn't working before you invest time.
  </Step>

  <Step title="Scan or promote to pentest">
    Scan the recorded URLs directly, or **Promote to full pentest** to run it over exactly what you recorded.
  </Step>
</Steps>

## Run an autonomous pentest

Once the domain is authorized, start a **pentest** and pick a depth:

| Depth          | What it does                                                                                                                    |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **Quick**      | High + critical checks only — a fast pass.                                                                                      |
| **Standard**   | Recon, discovery, and vulnerability scanning — **no exploitation**. The default.                                                |
| **Aggressive** | Adds the **exploitation phase** — actually attempts the exploits to produce Proof-of-Exploit. Requires the authorization above. |

If you recorded a session, promoting it runs the pentest against your captured flows; you can also narrow scope to specific discovered subdomains.

## Proof-of-Exploit

DAST is famously noisy. BreachLens's answer is proof: a finding marked **`CONFIRMED`** isn't a maybe — it carries the wire-level request/response that proves it, with a reproducible `curl` or, for browser-driven exploits, an exploit-replay video. That turns the noisiest stage of testing into the most trustworthy.

Findings carry one of three confidence levels. Only the top one earns the **Proof-of-Exploit** badge:

| Confidence      | Meaning                                                                                       |
| --------------- | --------------------------------------------------------------------------------------------- |
| `POSSIBLE`      | A signature or heuristic matched — worth a look, not proven.                                  |
| `LIKELY`        | Strong corroborating signal, but the exploit wasn't run end-to-end.                           |
| **`CONFIRMED`** | BreachLens **executed the exploit** and captured the proof. Earns the Proof-of-Exploit badge. |

### A confirmed finding, annotated

Here's a real `CONFIRMED` finding — a **SQL-injection authentication bypass** on a login endpoint. It signs in as a valid user with no valid password by closing the SQL string and commenting out the rest of the query. BreachLens ships the `curl` that runs it:

```bash Reproducer theme={null}
curl -sk -X POST https://app.example.com/rest/user/login \
  -H 'Content-Type: application/json' \
  --data "{\"email\": \"' OR 1=1--\", \"password\": \"anything\"}"
```

```json Response — a real session token, from credentials that were never valid theme={null}
HTTP/1.1 200 OK

{
  "authentication": {
    "token": "eyJhbGciOiJIUzI1NiJ9…",
    "umail": "admin@app.example.com"
  }
}
```

The injected `' OR 1=1--` makes the login query's `WHERE` clause always true and comments out the password check, so the server returns a valid auth token for the first user in the table. A `200` carrying a real token — from credentials that should have been rejected — **is** the exploit, not an inference about one.

A fully reproducible `CONFIRMED` finding ships three things:

| Ingredient            | What it is                                       | Why it matters                                                          |
| --------------------- | ------------------------------------------------ | ----------------------------------------------------------------------- |
| **`curl` reproducer** | The exact command that re-runs the attack        | Your engineer reproduces it in one paste — no BreachLens account needed |
| **Wire evidence**     | The real request **and** response that proved it | Takes "is this a false positive?" off the table                         |
| **Attack context**    | The vulnerable URL, parameter, and payload       | Points straight at the code to fix                                      |

<Note>
  Because the proof is self-contained, a confirmed finding is **portable** — hand the `curl` to a developer, a customer, or an auditor and they verify it themselves. That's the difference between "our scanner flagged this" and "here's the exploit."
</Note>

## Cutting the noise: AI triage

Not every signature match is real. BreachLens runs an **AI false-positive review** over findings — each is analyzed and tagged with a verdict (*likely real* or *likely false positive*) with the reasoning attached, so you see **why** before you trust it.

For domain (DAST / pentest) findings, a high-confidence *likely false positive* verdict can **auto-dismiss** a finding to keep your active list clean — but never blindly:

* **Severity and class are a hard gate.** A **critical** or **high** finding, or anything in an **injection / exploitation** class (SQL injection, command injection, XSS, path traversal, …), is **never auto-dismissed** on an AI verdict alone — no matter how confident the model is. Only low-stakes hygiene noise (informational / low, non-injection) is eligible to be set aside automatically.
* **Everywhere else the verdict is advisory.** On other scan tiers the review still runs, but it only annotates — it doesn't hide anything.

### Reveal what was dismissed

Auto-dismissed findings are **hidden, not deleted**. On a domain's **Findings** tab, the **Status** filter brings them back: switch it to show **Ignored** (or **Fixed**) and every finding the review set aside reappears for a second look — verdict and reasoning intact. If the model got one wrong, re-open it in a click.

<Note>
  BreachLens treats *dismissed* and *deleted* as different things on purpose. An AI verdict can tidy your active queue, but it can never make a finding vanish — the **Status** filter always surfaces the full history, and the severity/class gate means the dangerous classes were never hidden to begin with. A real vulnerability can't be silently buried.
</Note>

## Fix at source

A domain finding is observed on a **running URL**, not in a file — so on its own it can't open a code pull request. But when the same application's repository has also been scanned, BreachLens correlates the vulnerable route back to the **source file that backs it**. Open any domain finding and, when a match exists, a **Fix at source** panel appears, showing:

* **Where it was observed** — the vulnerable URL, and
* **The source file and line** it maps to, in the repository that serves the app, with a confidence indicator for the trace.

From there it's the **same one-click flow as a code finding**: generate a unified-diff patch with your configured AI provider, preview it, and open a **real pull request** against that file.

For the trace to appear, three things line up:

| Requirement          | Why                                                                                                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Same application** | The domain and the repository are grouped under one Application, so only assets that belong together get correlated                                                                                    |
| **A code match**     | The vulnerable route resolves to a scanned **SAST, Secrets, or IaC** finding — a specific file and line a single-file patch can target. Dependency (CVE) findings are package-level and don't qualify. |
| **Still actionable** | The source finding hasn't already been fixed, ignored, or marked a false positive                                                                                                                      |

<Note>
  The link between a URL and a source file is a **heuristic trace** — BreachLens shows the confidence and the reason, and **never opens a PR on its own**. You review the file and the diff first.
</Note>

**Find them fast.** Domain findings that trace to source carry a **Source fix** badge in the findings list, and the **Source fix** filter isolates them in one click — so you can work the runtime findings that already have a code fix waiting.

## Next steps

<CardGroup cols={2}>
  <Card title="Monitor continuously" icon="repeat" href="/pipeline/continuous-monitoring">
    Schedule recurring DAST against staging and route new confirmed findings to your team.
  </Card>

  <Card title="Fit it into your pipeline" icon="diagram-project" href="/pipeline/cicd">
    Where DAST and pentest sit in the delivery flow, and what to configure at each stage.
  </Card>
</CardGroup>
