Pentest a web application
cxg pentest runs an AI-driven whitebox pipeline: it reads guardlink’s threat
hypotheses about a codebase, drives an authenticated browser against the running
app, and writes confirmed, refuted, and ambiguous findings to a report. This
guide sets up the two inputs it needs, a findings.sarif and a live session,
and launches it against a target you host yourself.
flowchart TB
GL["guardlink findings.sarif,<br/>inline annotations, endpoints"] --> HY["hypotheses"]
CB["your source code"] --> SEL
AP["captured auth profiles"] --> PF["pre-flight:<br/>which identity is this cookie?"]
PF --> SEL["pick a probe per<br/>vulnerability class"]
HY --> SEL
SEL --> RUN["run probes in parallel<br/>authenticated browser contexts"]
RUN --> TR["triage every result"]
TR --> REP["report.json"]
TR --> LOG["audit.jsonl"]
class REP,LOG emphasis
Before you start
Section titled “Before you start”cxginstalled. See Installation.- The Python orchestrator installed:
cxg pentest install. Run it once; it copies the pipeline into~/.cert-x-gen/pentest/and checks its Python deps. - An AI CLI on your
PATH:claude,codex, orgemini. The pipeline asks it to write probe templates. Without one, generation produces nothing. - A guardlink
findings.sariffor the codebase. See feed guardlink findings into cxg. cxg reads it from<codebase>/whitebox/findings.sarif.
Stand up a target you control
Section titled “Stand up a target you control”Any local app with a session works. The pipeline needs the app running (to
probe) and its source on disk (for guardlink and for the AI to read). Point
--target at the URL and --codebase at the source tree.
-
Confirm the app answers, and that it gates something behind a session.
Terminal window curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8002/A
302to a login page unauthenticated,200with a cookie, is the shape the pre-flight check expects. -
Confirm the guardlink hypotheses are in place.
Terminal window ls ./app/whitebox/findings.sarif
Give the pipeline a session
Section titled “Give the pipeline a session”The pipeline acts as a logged-in user, so it needs a captured session. For CI and
for a reproducible run, import a Playwright storage_state rather than capturing
interactively. Keep it out of your home store by writing to a dedicated,
0700 directory.
-
Import the session as a named profile.
Terminal window mkdir -p ci-auth && chmod 700 ci-authcxg pentest auth import --profile live --target http://127.0.0.1:8002 \--storage-state ./session.json --auth-dir ./ci-auth✓ imported 1 cookies across 1 domains, 0 localStorage items, 0 sessionStorage items[auth] imported live (storage_state, 1 cookies)The profile files are written
0600. A laterrunthat loads this profile cannot tell an imported session from an interactively captured one. -
Verify the session is alive before spending a whole run on it.
Terminal window cxg pentest auth verify --profile live --auth-dir ./ci-auth✓ session for 'live' is ALIVE against http://127.0.0.1:8002· landed at http://127.0.0.1:8002/ (no login redirect)A dead session prints
✗ session … expiredand exits non-zero, which is your cue to re-capture.
Launch the pipeline
Section titled “Launch the pipeline”Point it at the running app and its source, and give it a goal.
cxg pentest run \ --codebase ./app \ --target http://127.0.0.1:8002 \ --auth live --auth-dir ./ci-auth \ --ai --ai-provider claude \ --goal "verify the IDOR exposure on the profile endpoint" \ --attestation "Local test against a self-hosted demo, 2026-08-13"The pre-flight is deterministic and worth reading in full:
[1] guardlink: 1 SARIF hypotheses, 0 inline, 0 endpoints[scope] audit log → ~/.cert-x-gen/sessions/pentest-20260813-141227/audit.jsonl[1b] inspecting 1 captured auth profiles… · live role=user tier=20 email=demo@example.com id=1 · landed at http://127.0.0.1:8002/ (no login redirect)[2] generating JS templates via claude (reads codebase)…Step [1] is guardlink ingestion, with one SARIF hypothesis loaded from
app/whitebox/findings.sarif. Step [1b] is the session pre-flight: it resolved
the identity behind the cookie before spending any AI budget. Step [2] hands
the ranked hypotheses to your AI CLI, which reads the codebase and writes probe
templates.
flowchart TB
H["a hypothesis, such as<br/>idor on /api/profile"] --> Q{"built-in probe<br/>for this class?"}
Q -->|yes| R["run against the app<br/>as a logged-in user"]
Q -->|"no, with --ai"| AI["AI writes one from your source,<br/>then it must compile and validate"]
Q -->|"no, without --ai"| SK["skipped"]
AI -->|passes| R
AI -->|fails| SK
class R emphasis
Read the exit code
Section titled “Read the exit code”In a pipeline the exit code is the machine-readable verdict:
| Exit | Meaning |
|---|---|
0 |
No confirmed findings, a clean run. |
1 |
No templates were available to run (guardlink output missing or empty, or the ranker selected none). |
2 |
Confirmed findings present. |
3 |
The scan was hard-killed by a 5xx streak or a scope violation. |
5 |
CI mode: an auth session was dead at pre-flight, so the run stopped before spending AI budget. |
Exit 5 is what makes this safe to automate. See
run cxg in CI for the non-interactive setup that
relies on it.
Read the report
Section titled “Read the report”Artifacts land in the session directory named in the [scope] line:
report.json (the split of confirmed / mitigation-verified / ambiguous) and
audit.jsonl (every HTTP request the run made). The audit log is the
dispute-ready record of what was sent, which is why --attestation is stamped
into its header.
flowchart TB
R["a probe runs"] --> T{"deterministic triage"}
T -->|confirmed| OK["confirmed_findings"]
T -->|refuted| MV["mitigation_verifications"]
T -->|"ambiguous,<br/>environment-bound"| AMB["ambiguous"]
T -->|"ambiguous,<br/>payload-bound"| M["AI rewrites the probe<br/>to address the failure"]
M -->|"up to 3 times"| R
class OK,MV,AMB emphasis
What to change next
Section titled “What to change next”- Two identities. Cross-user IDOR and session-replay probes need at least two
profiles. Import a second and pass
--auth victim,attacker. - Confirm blind vulnerabilities out of band. For SSRF and blind injection, add OAST, and read detect blind vulnerabilities with OAST first, because the two OAST flags mean very different things.
Related
Section titled “Related”- Pentest an Electron desktop app runs the same pipeline against a desktop target.
- Feed guardlink findings into cxg
produces the
findings.sarifthis guide assumes. cxg pentestdocuments every flag.

