Hybrid white-box + black-box penetration testing across 7 dimensions (PT1-PT7). Stack-aware source-to-sink tracing, exploit verification, CMS overlay, and deterministic finding aggregation. Uses explicit candidate schemas, canonical-key deduplication, score caps, and MUST-GATE enforcement. Flags: zuvo:pentest [path] | --url <url> | --from-audit <dir> | --cms <type> | --quick | --scope <path> | --dimensions PT1,PT2 | --verify-live | --rate <N> | --source | --runtime
Scanned 5/28/2026
Install via CLI
openskills install greglas75/zuvo---
name: pentest
description: "Hybrid white-box + black-box penetration testing across 7 dimensions (PT1-PT7). Stack-aware source-to-sink tracing, exploit verification, CMS overlay, and deterministic finding aggregation. Uses explicit candidate schemas, canonical-key deduplication, score caps, and MUST-GATE enforcement. Flags: zuvo:pentest [path] | --url <url> | --from-audit <dir> | --cms <type> | --quick | --scope <path> | --dimensions PT1,PT2 | --verify-live | --rate <N> | --source | --runtime"
codesift_tools:
always:
- analyze_project
- index_status
- index_folder
- index_file
- plan_turn
- scan_secrets # PT5 secret-leakage primitives
- search_patterns # injection sinks, eval, deserialize, command injection
- trace_call_chain # KEY — source-to-sink tracing for white-box pentest
- find_references # who reaches this dangerous sink
- audit_scan
- search_text # untrusted-input markers
- search_symbols # endpoint handlers (sinks)
- get_symbol
- get_symbols
- get_file_tree
- get_file_outline
- trace_route # entry points (sources)
by_stack:
typescript: [get_type_info, resolve_constant_value]
javascript: []
python: [python_audit, analyze_async_correctness, resolve_constant_value]
php: [php_project_audit, php_security_scan, resolve_php_namespace]
kotlin: [analyze_sealed_hierarchy, find_extension_functions, trace_flow_chain, trace_suspend_chain, trace_compose_tree, analyze_compose_recomposition, trace_hilt_graph, trace_room_schema, analyze_kmp_declarations, extract_kotlin_serialization_contract]
nestjs: [nest_audit]
nextjs: [framework_audit, nextjs_route_map]
astro: [astro_audit, astro_actions_audit, astro_hydration_audit, astro_middleware, astro_sessions]
hono: [analyze_hono_app, audit_hono_security]
express: []
fastify: []
react: [react_quickstart, analyze_hooks, analyze_renders]
django: [analyze_django_settings, effective_django_view_security, taint_trace]
fastapi: [trace_fastapi_depends, get_pydantic_models]
flask: [find_framework_wiring]
jest: []
yii: [resolve_php_service, trace_php_event, find_php_views]
prisma: [analyze_prisma_schema]
drizzle: []
sql: [sql_audit]
postgres: [migration_lint]
---
# zuvo:pentest -- Hybrid Penetration Testing
White-box source-to-sink analysis combined with optional black-box runtime verification. `zuvo:pentest` is stack-aware: it selects sources, sinks, framework-safe patterns, and CodeSift recipes based on the detected project stack before dispatching any agents. Findings are built from a deterministic candidate schema, then merged with canonical-key deduplication and centralized scoring.
**When to use:** Before releases, after `zuvo:security-audit` flags issues that need exploit verification, testing CMS installations, verifying security fixes.
**Out of scope:** Static code quality (use `zuvo:code-audit`), broad security posture review (use `zuvo:security-audit`), API contract audit (use `zuvo:api-audit`).
## Argument Parsing
| Argument | Effect |
|----------|--------|
| `[path]` | Limit analysis to a specific directory |
| `--url <url>` | Target URL for runtime probes (enables hybrid mode) |
| `--from-audit <dir>` | VERIFICATION mode -- verify PT/SEC findings from a prior audit directory |
| `--cms joomla\|wordpress\|drupal` | Force-enable CMS overlay |
| `--quick` | PT1 + PT3 + PT4 only (critical gates), no live probing |
| `--scope <path>` | Alias for `[path]` |
| `--dimensions PT1,PT2` | Audit only specified dimensions |
| `--verify-live` | Enable Phase 3 exploit verification against a running app |
| `--rate <N>` | Requests per second cap (default: 5) |
| `--source` | Force white-box track only -- skip live probes even if `--url` present |
| `--runtime` | Force black-box track only -- skip source analysis. Requires `--url`. |
### Mode Table
| Mode | Track | Agent Type | Max Concurrent |
|------|-------|------------|----------------|
| `[path]` | White-box | Source analyzers | 3 global |
| `--url` | Hybrid (white + black) | Source + scanner | 3 global |
| `--cms <type>` | CMS overlay | CMS-aware analyzers | 3 global |
| `--quick` | White-box (critical only) | Source only | 1 |
| `--verify-live` | Verification | Exploit verifier | 1 |
| `--from-audit` | Import / verification | Lead + verifier | 3 global |
| `--source` | White-box only | Source analyzers | 3 global |
| `--runtime` | Black-box only | Scanner agents | 3 global |
## Mandatory File Loading
Read in two stages. Stage 1 before Phase 0. Stage 2 before Phase 7.
**Stage 1 -- Before starting (STOP if any missing):**
```text
CORE FILES LOADED:
1. ../../rules/security.md -- READ/MISSING
2. ../../shared/includes/env-compat.md -- READ/MISSING
3. ../../shared/includes/codesift-setup.md -- READ/MISSING
4. ../../shared/includes/pentest-output-contract.md -- READ/MISSING
5. ../../shared/includes/pentest-stack-detection.md -- READ/MISSING
6. ../../shared/includes/pentest-stack-profiles.md -- READ/MISSING
7. ../../shared/includes/pentest-safe-pattern-registry.md -- READ/MISSING
8. ../../shared/includes/pentest-finding-registry.md -- READ/MISSING
9. ../../shared/includes/pentest-source-sink-registry.md -- READ/MISSING
10. ../../shared/includes/pentest-dedup-scoring.md -- READ/MISSING
11. ../../shared/includes/no-pause-protocol.md -- READ/MISSING (HARD: no mid-loop pauses across dimensions/findings)
```
**Stage 2 -- Before Phase 7 (report writing):**
```text
11. ../../shared/includes/backlog-protocol.md -- READ/MISSING
12. ../../shared/includes/run-logger.md -- READ/MISSING
13. ../../shared/includes/retrospective.md -- READ/MISSING
```
If any Stage 1 file is missing, STOP.
## Environment Compatibility
Read `../../shared/includes/env-compat.md` for agent dispatch, path resolution, and progress tracking.
## MANDATORY TOOL CALLS — Pentest Validity Gate
**This pentest is INVALID if any tool below is skipped when its trigger condition holds.** "DEFERRED", "N/A", "scope too narrow" are NOT valid reasons.
| Tool | Trigger | Reason | Skip allowed? |
|------|---------|--------|---------------|
| `scan_secrets` | Always | PT5 secret-leakage primitives | **NO** |
| `search_patterns(injection sinks/eval/deserialize/command-injection)` | Always | PT2/PT3 sink discovery | **NO** |
| `trace_call_chain` | Every PT2/PT3 candidate sink | KEY — white-box source-to-sink tracing | **NO** when sinks exist |
| `find_references` | Every cited sink | Who reaches this dangerous sink | **NO** when sinks exist |
| `trace_route` | Backend framework detected | Entry points (sources) for source-to-sink chains | **NO** when backend exists |
| `audit_scan` | Always | Compound check covering several PT dimensions | **NO** |
| `taint_trace` | Django detected | PT2/PT3 Django taint propagation | **NO** when Django |
| Stack-specific (nest_audit/php_security_scan/python_audit) | Framework/language detected | Stack-specific pentest gates | **NO** when matches |
### Forbidden escape hatches: same as security-audit (DEFERRED, N/A, codesift: unavailable, retrospective: skipped — all REJECTED).
### Required POSTAMBLE: report on disk → retro appended → `~/.zuvo/append-runlog` exit 0. Every HIGH/CRITICAL finding needs `path/to/file.ext:LINE` citation resolving in current tree (verify-audit gate).
### Mandatory acknowledgment (REQUIRED — print verbatim before Phase 0)
```
Mandatory-tools-acknowledgment: I will run scan_secrets + search_patterns(injection sinks) + trace_call_chain (every cited sink) + find_references (every cited sink) + trace_route (when backend) + audit_scan + taint_trace (when Django) + stack-specific tools for this pentest. Every HIGH/CRITICAL finding will cite a `path/to/file.ext:LINE` resolving in the current tree.
```
---
## CodeSift Integration
**Use the deterministic preload helper FIRST.** Run `~/.zuvo/compute-preload pentest "$PWD"` before any ToolSearch. Copy the printed `[CodeSift matching trace]` verbatim and issue the printed `ToolSearch(query="select:...")` line. Math gate enforced.
Read `../../shared/includes/codesift-setup.md` for the full initialization sequence.
**Summary:** Run the CodeSift setup from `codesift-setup.md` at skill start. Use the stack profile from `pentest-stack-profiles.md` to choose the right tools for the active stack. If CodeSift is unavailable, fall back to grep/find/manual tracing.
### CodeSift Optimizations
| Task | CodeSift | Fallback |
|------|----------|----------|
| Scope inventory | `get_file_tree` or stack-specific route map tools | `find` + `rg --files` |
| Sink discovery | `search_text` | `Grep` |
| Trace input to sink | `trace_call_chain` + `find_references` | Manual grep for call sites |
| Handler / view inventory | `get_file_outline`, `search_symbols`, `trace_route` | Sequential `Read` + `Grep` |
| Guard / auth coverage | `search_symbols` + `find_references` | `Grep` for decorators, middleware, auth helpers |
### Degraded Mode (CodeSift unavailable)
| CodeSift tool | Fallback | Lost capability |
|---------------|----------|-----------------|
| `trace_call_chain` | `Grep` for import references | No transitive call graph |
| `find_references` | `Grep` for callers | Weaker constant propagation |
| `search_symbols` | `Grep` for function / class names | Less precise matching |
| `get_file_outline` | `Read` full file | More tokens consumed |
| `trace_route` | Manual route tracing | No route-to-handler map |
---
## Safety Gates (NON-NEGOTIABLE)
### Gate 1 -- Authorization
Prompt the user for explicit authorization before any probing. Block on "no".
```text
PENETRATION TESTING requires authorization.
Target: {URL or CWD}
Do you have authorization to test this system? [y/n]
```
### Gate 2 -- No Production
Refuse if URL lacks localhost/127.0.0.1/staging/dev. Override only with `--i-know-what-im-doing`.
### Gate 3 -- Read-Only Default
GET requests proceed freely. POST/PUT/DELETE require per-endpoint approval. Exception: POST `/auth/login`.
### Gate 4 -- Rate Cap
Max `--rate` req/s (default 5). Stop after 3 consecutive 5xx.
### Gate 5 -- PII Scrub
Mask tokens, passwords, cookies, email addresses, and API keys in all output.
### Gate 6 -- Database Safety
White-box: zero DB queries. Black-box: time-based detection only. Verify-live: read-only payloads.
---
## Phase 0: Detect Mode, Stack, and Scope
### 0.1 Detect Mode
```text
Source code in CWD? -> WHITE-BOX track (Phase 1 source analysis)
--url without source? -> BLACK-BOX track (Phase 1 scanner)
--from-audit? -> VERIFICATION mode (skip to Phase 3)
```
### 0.2 Detect Stack and Load Stack Rules
Follow `pentest-stack-detection.md`.
Required actions:
1. Detect the primary backend stack.
2. Detect frontend and site-framework overlays, including Next App Router, Next Pages Router, and React SPA where applicable.
3. Detect headless CMS overlays.
4. Detect CMS overlays, including WordPress / Joomla / Drupal.
5. Detect framework overlays from `pentest-stack-detection.md`.
6. Load the matching files from `../../rules/`.
7. Load safe-pattern IDs from `pentest-safe-pattern-registry.md`.
8. Load finding-type to CWE / probe mappings from `pentest-finding-registry.md`.
9. Load source/sink discovery regexes from `pentest-source-sink-registry.md`.
10. Select the active stack profile and any active overlays from `pentest-stack-profiles.md`.
### 0.3 Build Authoritative Scope Inventory
Before dispatching any Phase 1 agent:
1. Resolve the target scope from `[path]` or `--scope`.
2. Build the complete file inventory for that scope.
3. Record one authoritative `files_in_scope` count.
4. Pass that exact count to every agent.
Print:
```text
SCOPE INVENTORY
Scope: src/example/
Files in scope: 92
Stack profile: python-django
```
### 0.4 CMS Detection
```bash
[ -f configuration.php ] && echo "CMS: Joomla"
[ -f wp-config.php ] && echo "CMS: WordPress"
[ -f sites/default/settings.php ] && echo "CMS: Drupal"
```
If CMS detected or `--cms` is set, enable the CMS overlay in Phase 5.
### 0.5 Tool Availability
```bash
which curl 2>/dev/null && echo "curl: OK" || echo "curl: MISSING"
which semgrep 2>/dev/null && echo "semgrep: available" || echo "semgrep: fallback to grep"
which testssl 2>/dev/null && echo "testssl: available" || echo "testssl: fallback to openssl"
which gitleaks 2>/dev/null && echo "gitleaks: available" || echo "gitleaks: skip secret scan"
```
Missing tools never block the audit -- they reduce coverage. Document this in the report.
Check whether Playwright browser tooling is available in the current environment.
### 0.6 Build Phase 1 Context
Create a shared context block for all source-analysis agents:
```text
PHASE 1 CONTEXT
Stack profile: {active profile}
Framework overlays: {overlay list}
Files in scope: {N}
Scope path: {path}
Active dimensions: {PT list}
Safe-pattern IDs: {relevant pattern_id list}
Source families: {relevant source regex families}
Sink families: {relevant sink regex families}
```
Materialize these logical artifacts for later phases:
- `phase1-context`
- `phase1-findings`
- `phase1-exclusions`
- `probe-plan`
PT6 and PT7 must consume `phase1-context`, `phase1-findings`, and `phase1-exclusions`.
---
## Phase 1: Source Analysis (WHITE-BOX) or Scanning (BLACK-BOX)
### WHITE-BOX Track -- Source Analyzer Agents
Spawn via the task/agent dispatch model from `env-compat.md`. Max 3 agents in parallel globally.
#### Global Rules
Every source-analysis agent must:
1. Use the active stack profile from `pentest-stack-profiles.md`.
2. Seed discovery from `pentest-source-sink-registry.md` before using broader semantic search.
3. Match framework defenses by canonical `pattern_id` from `pentest-safe-pattern-registry.md`.
4. Resolve `cwe` and `probe_template_id` from `pentest-finding-registry.md` when the `type` exists there.
5. Use the shared `files_in_scope` inventory from Phase 0.3.
6. Emit **candidate records** using `pentest-output-contract.md`.
7. Classify each record as `confirmed`, `excluded`, or `needs_review`.
8. Never assign final IDs such as `PT3-001`.
**Batch 1 (parallel) -- independent sink tracing:**
- **PT1 (Injection: SQL, NoSQL, command, SSTI):** Trace every attacker-controlled input to query/exec/eval sink using the active stack profile.
- **PT2 (XSS and Output Encoding):** Trace attacker-controlled input to HTML/template/output sinks using the active stack profile.
- **PT5 (SSRF and External Interactions):** Trace attacker-controlled URLs or hosts to outbound HTTP/file retrieval sinks. If the stack has no outbound client primitives, emit a coverage note as `status=excluded`, not a finding.
**Batch 2 (parallel) -- cross-endpoint reasoning:**
- **PT3 (Auth and Session):** Session fixation, cookie flags, OAuth / OIDC state handling, password / 2FA flows, token binding, replay windows.
- **PT4 (Authorization and Access Control):** Guard coverage on every mutation endpoint. Guard + query filter defense in depth. IDOR / org / tenant scoping.
**Batch 3 (parallel, after Batch 1 + Batch 2 merge):**
- **PT6 (Input Vectors):** File uploads, CSRF, header injection, redirect edge cases, parser abuse. Must consume the merged Phase 1 context and exclusions.
- **PT7 (Business Logic):** TOCTOU, race conditions, state-machine bypass, price-from-client, replay windows. Must consume the merged Phase 1 context and exclusions.
### Candidate Record Example
Agents emit candidate records, not final findings:
```json
{
"schema_version": "4.0",
"dimension": "PT3",
"type": "oauth_state_missing",
"title": "OAuth implicit flow does not require state parameter",
"severity": "HIGH",
"status": "confirmed",
"reason_code": "proof_complete",
"source_agent": "PT3-auth-session",
"source_file": "src/app/oauth.py:101",
"sink_file": "src/app/oauth.py:528",
"function_symbol": "OAuthAuthorizeView.approve",
"endpoint": "GET /oauth/authorize/",
"trace": ["step1", "step2"],
"scope_ok": true,
"files_checked": 92,
"files_in_scope": 92,
"defense_present": false,
"defense_sufficient": false,
"confidence": 0.85,
"confidence_rationale": "Complete code trace with no effective defense and a clear exploit path.",
"verification_status": "trace_confirmed",
"safe_pattern_matched": [],
"canonical_key": "PT3|OAuthAuthorizeView.approve|oauth_state_missing|GET /oauth/authorize/",
"probe_template_id": "probe-oauth-state",
"suggested_probe": "GET /oauth/authorize/?response_type=token&state=",
"runtime_preconditions": ["pre-approved app exists"],
"safe_to_probe": true,
"cwe": "CWE-352"
}
```
### BLACK-BOX Track -- Scanner Agents
If no source code is available, dispatch scanner agents instead. One per dimension, max 3 in parallel globally. Each follows a 4-phase cycle:
1. Recon
2. Experiment
3. Test
4. Verify
Scanner agents must still emit candidate records using `pentest-output-contract.md`.
Findings below confidence `0.7` are not promoted to final findings.
### VERIFICATION Mode
Skip Phase 1 entirely. Parse PT/SEC findings from `--from-audit` and normalize them into candidate records.
Rules:
- Preserve original IDs in audit trail only.
- Recompute `canonical_key`.
- Set `verification_status="needs_runtime_check"` unless runtime evidence already exists.
- Preserve or assign `reason_code`.
- If more than 50% of imported findings reference stale code (file missing or line content changed materially), warn the user and suggest re-running the underlying audit.
---
## Phase 2: Merge, Scope Enforcement, Dedup, Coverage Sanity
Collect all Phase 1 candidate records into a single queue.
### 2.1 Scope Enforcement
Before any scoring or reporting:
1. Check `scope_ok` on every candidate.
2. If `source_file` or `sink_file` is outside the requested scope, force:
- `status="excluded"`
- exclusion reason `out_of_scope`
3. Do not count out-of-scope records in final findings.
### 2.2 Deduplication
Follow `pentest-dedup-scoring.md`.
Do not deduplicate on `file:line`. Use `canonical_key` and merge alias evidence into one survivor.
### 2.3 Audit Trail and Context Artifacts
Preserve an `audit_trail[]` collection for:
- source agent names
- duplicate merges
- status changes
- `reason_code`
- matched safe patterns
- resolved `probe_template_id`
Update these logical artifacts after merge:
- `phase1-findings`
- `phase1-exclusions`
- `probe-plan`
### 2.4 Coverage Sanity
Follow `pentest-dedup-scoring.md`.
Compare every agent's `files_checked` against the authoritative `files_in_scope` count from Phase 0.3. Any unexplained shortfall below 90% is `low_signal` and must be called out in the report.
### 2.5 Queue Summary
Print a merge summary with separate counts:
```text
MERGED QUEUE
Confirmed: 9
Needs Review: 2
Exclusions: 6
Duplicates collapsed: 3
Low-signal dimensions: PT1
```
---
## Phase 3: Exploit Verification (CONDITIONAL -- requires `--verify-live`)
**Skip if:** `--verify-live` is not set. Findings remain `trace_confirmed` or `needs_runtime_check`.
Process findings sequentially (one at a time -- Gate 3 read-only).
For each confirmed finding:
1. Feasibility check: endpoint reachable? auth required? environment safe? `safe_to_probe=true`?
2. Resolve `probe_template_id` from `pentest-finding-registry.md`.
3. Reuse `suggested_probe` if present; otherwise craft a minimal read-only probe from the mapped template.
4. Prefer the matching verify-live template from `pentest-finding-registry.md`, then refine it with overlay-specific ideas from `pentest-stack-profiles.md`.
5. Execute via `curl` or Playwright.
6. Capture evidence.
Update `verification_status`:
- `runtime_verified`
- `blocked_by_defense`
- `needs_runtime_check`
Update confidence:
- `runtime_verified` -> 0.95
- `blocked_by_defense` -> 0.6 and move to `excluded`
- failed reproduction without proof of defense -> remain `needs_runtime_check`
If `safe_to_probe=false`, do not improvise around the constraint. Record the unmet `runtime_preconditions` and keep the finding in `needs_runtime_check`.
---
## Phase 4: Runtime Checks (PT8)
**Skip if:** `zuvo:security-audit --live-url` already ran and its live results still match the current code / environment.
Dispatch a lightweight agent to check:
- security headers
- CORS
- TLS version
- error disclosure
- cookie flags
Rate limit per `--rate`.
PT8 contributes narrative runtime coverage, not a new scoring dimension unless explicitly requested by the user.
---
## Phase 5: CMS Overlay (CONDITIONAL)
**Skip if:** No CMS detected in Phase 0.4 and no `--cms` flag.
Apply the CMS overlay from `pentest-stack-profiles.md`:
1. Version -> CVE lookup
2. Extension / plugin inventory + known vulnerability check
3. Admin panel exposure
4. Config review (debug mode, error reporting, DB credentials)
5. File permissions
6. Custom extension code review (trace input to sink)
---
## Phase 6: Confidence Filter and MUST-GATEs
Apply three filter layers to the merged queue:
### Layer 1 -- Confidence Threshold
`status=confirmed` with confidence `< 0.7` is demoted to `needs_review`.
### Layer 2 -- Status Correction
Apply `pentest-output-contract.md` hard rules centrally:
- `defense_sufficient=true` -> `excluded`
- `scope_ok=false` -> `excluded`
- hedged language without proof -> `needs_review`
- preserve `reason_code` on every disposition change
### Layer 3 -- MUST-GATEs
- **ASSUME-EXPLOIT:** Treat a trace as exploitable unless defense is proven sufficient.
- **NO-HEDGING:** "Might be vulnerable" -> prove it or move it out of final findings.
- **PROOF:** Every final finding needs a source trace or runtime evidence.
- **FULL-COVERAGE:** Agent must report `files_checked` against the full scope inventory.
- **STRICT-SEQUENCE:** Agent must follow `sink -> trace -> defense -> verdict`.
Print filter summary:
```text
CONFIDENCE FILTER
Confirmed input: 13
Demoted to Needs Review: 2
Excluded by rules: 4
Final findings: 9
```
---
## Phase 7: Report Generation
### 7.1 Scoring
Follow `pentest-dedup-scoring.md`.
Requirements:
- compute `raw_score` first
- compute `final_score` after score caps
- emit `cap_reason`
- emit `grade`
- emit `verdict`
Per-dimension score remains 0-10:
- 0 = critical gate fail (undefended sink confirmed)
- 5 = defenses present but gaps found
- 8 = minor issues only
- 10 = all checks pass
**Critical gates:** PT1=0 OR PT3<3 OR PT4<3 -> grade `FAIL`.
### 7.2 Output Directory
```bash
mkdir -p pentest-results/evidence
```
### 7.3 Write Reports
**`pentest-results/pentest-report.md`** -- Full report:
1. Metadata (target, date, mode, scope, stack profile, tools used, gates passed)
2. Executive summary (grade, verdict, raw score, final score, cap reason, finding counts)
3. Critical findings
4. All final findings grouped by dimension
5. Needs Review
6. Exclusions applied
7. Audit trail summary
8. Coverage sanity results
9. Variant analysis results
10. Remediation roadmap
11. Manual checklist
**`pentest-results/findings.json`** -- Machine-readable:
```json
{
"schema_version": "4.0",
"metadata": {
"target": "/repo",
"scope": "src/app/",
"stack_profile": "python-django",
"files_in_scope": 92
},
"summary": {
"total_findings": 9,
"by_severity": { "critical": 0, "high": 4, "medium": 5 },
"by_dimension": { "PT3": 3, "PT4": 2, "PT7": 4 },
"raw_score": 73,
"final_score": 60,
"cap_reason": "HIGH findings present",
"grade": "C",
"verdict": "WARN",
"critical_gates": "PASS"
},
"findings": [{ "id": "PT3-001", "reason_code": "proof_complete", "probe_template_id": "probe-oauth-state", "merged_from": ["PT3-auth-session"] }],
"needs_review": [{ "dimension": "PT6", "reason_code": "runtime_required" }],
"exclusions": [{ "reason_code": "out_of_scope" }],
"audit_trail": [{ "canonical_key": "PT3|...", "source_agent": "PT3-auth-session" }]
}
```
`summary.*` must be generated from the final `findings[]` collection only. If the counts do not match, the run is invalid and must be corrected before output.
### 7.4 Self-Check
Validate the machine-readable output before printing the completion block:
```bash
./scripts/validate-pentest-output.sh pentest-results/findings.json
```
Required checks:
- `summary.total_findings == len(findings[])`
- `summary.by_severity` matches final `findings[]`
- `summary.by_dimension` matches final `findings[]`
- every record in `findings[]`, `needs_review[]`, and `exclusions[]` has `reason_code`
If validation fails, the run is invalid. Correct the output before printing `PENTEST COMPLETE`.
**`pentest-results/executive-summary.md`** -- 1-page stakeholder overview.
**`pentest-results/evidence/PT3-001/`** -- Per-finding evidence:
- `trace.md`
- `poc.sh` (if `--verify-live`)
- `screenshot.png` (if Playwright)
### Finding Format
```text
### PT{D}-{NNN}: {Title}
Dimension: PT{D} -- {name}
Severity: CRITICAL / HIGH / MEDIUM
Confidence: {0.0-1.0}
Verification Status: trace_confirmed / runtime_verified / needs_runtime_check
Probe Template ID: {probe_template_id}
CWE: CWE-{NNN}
Reason Code: {reason_code}
Source: {file}:{line}
Sink: {file}:{line}
Trace: source -> [intermediate steps] -> sink
Defense Present: {yes/no}
Defense Sufficient: {yes/no}
Canonical Key: {canonical_key}
Merged From: {source_agent list}
Evidence: {code trace or runtime capture, scrubbed, max 20 lines}
Impact: {what an attacker achieves}
Fix: {remediation with code}
```
### 7.4 Backlog Integration
For each CRITICAL / HIGH final finding, suggest a backlog entry:
```text
zuvo:backlog add "PT3-001: OAuth implicit flow missing state" --severity HIGH --source pentest --dimension PT3
```
### 7.5 Next-Action Routing
| Condition | Suggested Action |
|-----------|------------------|
| CRITICAL/HIGH findings | `zuvo:refactor` to fix vulnerable code paths |
| Auth gaps found | `zuvo:security-audit full` for broader posture check |
| Out-of-scope issues surfaced | Suggest a focused follow-up audit on the owning module |
| Runtime preconditions not met | Emit a follow-up `--verify-live` probe plan instead of closing the item |
| CMS issues | Update CMS + extensions, re-run `zuvo:pentest --cms` |
| All clear | Auto-chain to `zuvo:review` (see below) |
---
## PENTEST COMPLETE
Grade: [A/B/C/D/FAIL] | Verdict: [PASS/WARN/FAIL]
Raw Score: [N] / 100 | Final Score: [N] / 100 | Cap: [reason/none]
Mode: [white-box / hybrid / black-box / verification]
Dimensions: [N scored] | Critical gates: [PASS/FAIL]
Findings: [N critical] / [N high] / [N medium] / [N total]
### Validity Gate (REQUIRED — print BEFORE Run line, AFTER retro append + append-runlog)
```
VALIDITY GATE
triggers_held: language=<X> framework=<X> backend=<yes|no> django=<yes|no>
required_tool_calls:
scan_secrets: [<N> hits | NOT_CALLED — VIOLATES_TRIGGER]
search_patterns(sinks): [<N> hits | NOT_CALLED — VIOLATES_TRIGGER]
trace_call_chain: [<N> sink chains traced | NOT_CALLED — VIOLATES_TRIGGER]
find_references: [<N> ref-checks | NOT_CALLED — VIOLATES_TRIGGER]
trace_route: [<N> entry points | not_required (no backend) | NOT_CALLED — VIOLATES_TRIGGER]
audit_scan: [<N> compound findings | NOT_CALLED — VIOLATES_TRIGGER]
taint_trace: [<N> taints | not_required (no Django) | NOT_CALLED — VIOLATES_TRIGGER]
stack_specific: [<result> | not_required | NOT_CALLED — VIOLATES_TRIGGER]
postamble:
retros_log_appended: [yes(bytes_added=N) | NOT_APPENDED — VIOLATES_REQUIRED_POSTAMBLE]
retros_md_appended: [yes(entry_count=N) | NOT_APPENDED — VIOLATES_REQUIRED_POSTAMBLE]
verify_audit_pass: [yes(<verified>/<total>) | NOT_RUN | REJECTED]
gate_status: [PASS | FAIL — <which gates missing>]
```
If `gate_status = FAIL` → VERDICT = INCOMPLETE, append `[VALIDITY GATE FAIL]` to NOTES.
Append the Run line via the retro-gated wrapper (NOT direct `>> runs.log`):
```bash
echo -e "$RUN_LINE" | ~/.zuvo/append-runlog
```
Run: <ISO-8601-Z> pentest <project> <N-critical> <N-total> <VERDICT> - <N>-dimensions <NOTES> <BRANCH> <SHA7> <INCLUDES> <TIER>
### Retrospective (REQUIRED)
Follow the retrospective protocol from `retrospective.md`.
Gate check -> structured questions -> TSV emit -> markdown append.
If gate check skips: print `RETRO: skipped (trivial session)` and proceed.
After printing this block, append the `Run:` line value (without the `Run: ` prefix) to the log file path resolved per `run-logger.md`.
Verdict rules come from `pentest-dedup-scoring.md`.
---
## Post-Pentest: Automatic Review Chain (MANDATORY)
After logging the pentest run, automatically invoke `zuvo:review` on changed files. Do not ask the user.
**Scope selection:**
- If there are uncommitted / staged changes: `Skill(skill="zuvo:review")`
- If pentest applied and committed fixes: `Skill(skill="zuvo:review", args="HEAD~N")`
- If pentest was audit-only (no code changes made): skip the chain, print `No code changes -- review chain skipped.`
```text
# Check for uncommitted changes first
git diff --stat HEAD
# If output is empty, check for recent commits from this session
git log --oneline -5
# Then invoke with appropriate scope
Skill(skill="zuvo:review")
```
This ensures adversarial code review runs before push on all supported platforms.
---
## Execution Notes
- Use stack-aware recipes from `pentest-stack-profiles.md`, not hard-coded TS-only heuristics.
- Use framework overlays from `pentest-stack-detection.md` and `pentest-stack-profiles.md` when the repo matches them.
- Use Sonnet-class models for source analyzers and scanners.
- Use a stronger model for exploit verification on CRITICAL findings when available.
- Max 3 parallel agents globally.
- White-box agents are read-only.
- Black-box agents require Gate 1 authorization.
- Estimated durations: `--quick` ~5 min, white-box ~15 min, hybrid ~25 min, full + `--verify-live` ~40 min.
No comments yet. Be the first to comment!