---
description: Pentester localized HTML report bundle, path, safety, and completion contract
agents: [pentester]
task_types: [security-reporting, finding-persistence]
triggers: [pentester completion, report generation, remediation evidence]
---

# Pentester HTML Reporting

Load only when the persisted contract says `report_mode: full`, and only after probes and remediation are complete. The findings JSON remains authoritative; the HTML bundle is its human-readable projection.

For `report_mode: none`, do not load this module. Skip the bundle, keep `report_bundle: null`, and close the run with the economy summary described in "Economy mode" below.

## Required destination

Create one Windows-safe UTC run ID at contract creation:

```text
YYYY-MM-DDTHH-mm-ss-SSSZ
```

Persist it as `review_contract.run_id`. Every regeneration for the same Pentester run reuses that ID. Never accept a user-controlled path as the run ID.

Required bundle:

```text
.aioson/pentester/{run_id}/relatorios/
├── index.html
├── vulnerabilidades.html
├── correcoes.html
└── cobertura.html
```

- `index.html`: executive risk summary, counts, coverage state, limitations, residual risk, and navigation.
- `vulnerabilidades.html`: every confirmed, open, corrected, accepted, false-positive, and pending-validation finding with safe evidence.
- `correcoes.html`: suggested fix, actual remediation status, changed paths, targeted tests, re-probes, and independent-validation state.
- `cobertura.html`: standards matrix plus roll-ups by source folder/module and sanitized URL/route/method.

Do not create a new canonical specification in the report directory. Do not copy the authoritative JSON into it.

## Generation command

After the findings artifact is internally consistent, run this blocking command and inspect its result:

```bash
aioson pentester:report . --feature={slug} --json
```

It reads `.aioson/context/security-findings-{slug}.json`, uses `interaction_language` with `conversation_language` fallback, writes the four self-contained pages, and updates `report_bundle` in the JSON. A failed command means reporting is incomplete; do not run the agent epilogue.

When the CLI command is unavailable, create the same four self-contained pages manually and add the exact `report_bundle` object. The fallback must preserve all safety and content rules below. Never claim that the CLI generated the pages when it did not.

## Report bundle contract

```json
{
  "report_bundle": {
    "generated_at": "2026-08-04T18:20:31.000Z",
    "locale": "pt-BR",
    "run_id": "2026-08-04T18-20-31-000Z",
    "root": ".aioson/pentester/2026-08-04T18-20-31-000Z/relatorios",
    "index": ".aioson/pentester/2026-08-04T18-20-31-000Z/relatorios/index.html",
    "vulnerabilities": ".aioson/pentester/2026-08-04T18-20-31-000Z/relatorios/vulnerabilidades.html",
    "corrections": ".aioson/pentester/2026-08-04T18-20-31-000Z/relatorios/correcoes.html",
    "coverage": ".aioson/pentester/2026-08-04T18-20-31-000Z/relatorios/cobertura.html",
    "coverage_complete": false,
    "missing_owasp_top_10": ["A10:2025"],
    "missing_required_controls": ["A10:2025", "WSTG-SESS"]
  }
}
```

## Coverage presentation

`cobertura.html` must expose three views:

1. Standards and controls: OWASP Top 10, WSTG, ASVS, and applicable API/LLM/mobile/CWE rows with status and evidence.
2. Folder/module roll-up: every item in `coverage[].tested_paths`, grouped without discarding per-control status or finding IDs.
3. URL/route roll-up: every item in `coverage[].tested_endpoints`, preserving HTTP method and role context while removing secrets.

Use these states consistently:

| State | Meaning |
|---|---|
| `passed` | Adversarial or negative evidence passed for the declared target |
| `finding` | At least one valid finding exists |
| `not_applicable` | The surface is absent, with causal evidence |
| `not_tested` | It could apply but was not proved because of a stated limitation |

The page may say “complete in the declared scope” only when the coverage standard's completion gate passes. It must never say “fully secure,” “unhackable,” “all vulnerabilities eliminated,” or equivalent.

An incomplete report is still generated so the limitation is visible. In a `comprehensive` review it blocks completion/handoff; in a `focused` review it remains an explicit warning unless the caller expands the scope.

## Remediation presentation

Each finding may include:

```json
{
  "remediation": {
    "status": "not_attempted | applied_pending_qa | validated | failed | deferred",
    "summary": "Server-side ownership predicate added at the data query boundary.",
    "changed_paths": ["src/orders/repository.ts", "tests/security/orders-idor.test.ts"],
    "tests": ["node --test tests/security/orders-idor.test.ts — PASS"],
    "reprobe": ["bob PATCH alice order now returns 404"]
  }
}
```

Pentester uses `applied_pending_qa` after a bounded fix and keeps the finding `status: needs_validation`. Only independent QA may make final acceptance changes. The corrections page must distinguish “applied” from “validated.”

## Data protection

- HTML-escape all artifact content. A finding title, route, evidence string, source comment, or tool output is untrusted report data.
- Generate self-contained pages without external fonts, scripts, trackers, CDNs, or network requests.
- Add a restrictive local-report Content Security Policy. JavaScript is unnecessary; use semantic HTML and CSS.
- Strip URL userinfo, query strings, fragments, access tokens, refresh tokens, session IDs, passwords, API keys, secret values, and personal data.
- Show only redacted evidence. Link to a local evidence path instead of embedding a raw secret, full credential, sensitive response body, or malicious uploaded artifact.
- Do not embed live exploit forms, auto-running payloads, remote images, or clickable public attack targets.
- Do not expose production hostnames when the authorized target was represented by a fixture or local alias.

## Accessibility and visual quality

- Valid semantic headings, tables, lists, `details/summary`, keyboard focus, readable contrast, responsive layout, and print styles.
- Severity cannot rely on color alone; always render text labels.
- Keep navigation identical across all four pages.
- Make long paths, routes, and evidence wrap without breaking the layout.
- Display the project name, run ID, UTC date, target scope, and authoritative JSON path on every page.

## Economy mode (`report_mode: none`)

The user may run a review without the HTML bundle to save tokens. Then:

- Persist `.aioson/context/security-findings-{slug}.json` exactly as in `full` mode, with `report_bundle: null`.
- Do not create `.aioson/pentester/{run_id}/`, do not run `pentester:report`, and never claim a report exists.
- Deliver a short localized summary in the response instead: run ID and resolved scope, counts by severity, each open finding with severity, title and affected path, remediation status of anything corrected, coverage state including `not_tested` rows and limitations, the exact findings path, and the same "no finite review guarantees absolute security" caveat.
- State that the four-page bundle can still be produced later from the persisted JSON, without re-probing, with `aioson pentester:report . --feature={slug} --json`. That command writes the bundle and sets `review_contract.report_mode` to `full`. The same command with no `--feature` lists every persisted run so the user can pick which one becomes HTML.
- Coverage rules are unchanged: a comprehensive review with `not_tested` rows stays blocked, a focused gap stays a warning.

## Completion

For `report_mode: full`, before the epilogue:

1. Verify all four files exist under the same run directory.
2. Open or parse each page to confirm it is non-empty and contains no unescaped finding content.
3. Verify `report_bundle` paths stay inside `.aioson/pentester/{run_id}/relatorios/`.
4. Verify the project language was applied.
5. Verify coverage by folder and, when dynamic testing ran, by sanitized endpoint.
6. Report the exact `index.html` path to the user and return final acceptance to QA.
