# PLAYBOOK — Report Templates and Workflow

## End-of-engagement workflow

```bash
# 1. All cluster 1-4 scans have run; findings are in engagements/<id>/findings/

# 2. (Optional) Map findings to OWASP Top 10
python3 plugins/security/penetration-tester/skills/mapping-findings-to-owasp-top10/scripts/map_owasp.py \
    engagements/acme-2026-q2/

# 3. Compose the vulnerability report
python3 ./scripts/compose_report.py engagements/acme-2026-q2/

# 4. Generate the executive summary
python3 plugins/security/penetration-tester/skills/generating-executive-summary/scripts/exec_summary.py \
    engagements/acme-2026-q2/

# 5. Record the engagement (chain of custody)
python3 plugins/security/penetration-tester/skills/recording-pentest-engagement/scripts/record_engagement.py \
    engagements/acme-2026-q2/ --sign --tar engagements/archives/acme-2026-q2.tar.gz
```

## Report-template variants per audience

### Technical (default — what the skill emits)

Everything: per-finding detail, remediation steps, evidence,
references. Audience: customer's security engineer or external
auditor.

### Manager-style (use `--min-severity high`)

Only HIGH and CRITICAL findings shown. Body of the report is the
same per-finding format but the customer's manager doesn't have
to scroll past medium / low findings.

```bash
python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --min-severity high \
    --report-output engagements/acme-2026-q2/reports/manager-brief.md
```

### Compliance-evidence (use `--include-info`)

Includes the INFO-severity operational records (scan ran cleanly,
no findings in this target, etc.). Audience: SOC2 / ISO 27001
auditors who want documented evidence the scan ran even where
nothing was found.

```bash
python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --include-info \
    --report-output engagements/acme-2026-q2/reports/compliance-evidence.md
```

## Per-finding remediation phrasing patterns

The skill emits each finding's `remediation` field verbatim from
the source. Cluster 1-4 skills construct these as numbered lists
with imperative voice:

```
1. Run `npm audit fix` in the project root.
2. If the fix requires a semver-major bump, evaluate the breaking changes.
3. Commit the updated package-lock.json.
```

Patterns to maintain when authoring remediation:

- **Imperative voice.** "Run X" not "You should run X" not "It is recommended to run X."
- **Numbered list when there are >1 steps.** Bullets blur the order.
- **Concrete commands when possible.** "Apply security patch" is unactionable; "`apt-get update && apt-get upgrade libssl3`" is actionable.
- **Decision points called out.** "If X, do Y; otherwise do Z" makes the operator's choice explicit.

## Evidence-redaction for distributed reports

A pentest report sometimes needs distribution beyond the immediate
customer — to their auditor, their insurance carrier, their board.
Some evidence shouldn't leave the original engagement: leaked
credentials, full request/response bodies containing PII, screenshots
showing customer data.

The skill doesn't auto-redact. The pattern for human-controlled
redaction:

1. Generate the full report with this skill.
2. Use a separate redaction pass to:
   - Replace leaked-secret values with `[REDACTED]` while keeping
     finding context.
   - Blur or remove customer-data screenshots.
   - Replace user-identifying URLs with `<USER>/path/...` placeholders.
3. Distribute the redacted version. Keep the unredacted version in
   the engagement archive.

The skill could be extended with a `--redact PATTERN` flag if this
becomes a frequent enough pattern.

## Cross-reference protocol with OWASP-mapping + exec-summary

The next two cluster 6 skills consume this report's findings file
(JSON output of THIS skill's findings → not the rendered markdown).
The protocol:

1. **Findings producer:** cluster 1-4 skills emit per-skill findings JSONLs
2. **Vulnerability report composer (this skill):** composes a single
   vulnerability-report.md AND can output a unified findings JSONL
3. **OWASP mapping:** reads the unified JSONL, adds `owasp_category`
   field to each finding, writes back
4. **Exec summary:** reads the (potentially OWASP-enriched) unified
   JSONL, produces the executive-summary.md

The order can be either:

- A: cluster-1-4 → OWASP → compose → exec-summary
- B: cluster-1-4 → compose → OWASP → re-compose → exec-summary

Pattern B regenerates the report after enrichment. Pattern A only
composes once. Both are valid; the choice depends on whether the
operator wants the OWASP tags visible in the report's per-finding
sections.

## Common composition mistakes

| Mistake | Consequence | Fix |
|---|---|---|
| Findings without `target` field | Findings dropped from report; HIGH op-finding | Ensure all source skills emit `target` |
| Mixed findings + opfindings in source file | Report contains operational noise | Separate skill output and operational output |
| Identical findings with different IDs | Dedup fails | Ensure source skills use consistent `Finding.fingerprint()` |
| Re-running with new sources but old report path | Old report overwritten silently | Use timestamped `--report-output` paths for interim reports |

## Per-engagement directory layout (canonical)

The skill assumes:

- `engagement/findings/*.json[l]` — source findings
- `engagement/reports/` — output reports (auto-created if missing)
- `engagement/roe.yaml` — has `engagement_id:` for header

Deviations are supported via flags:

- `--source FILE` — bypass auto-discovery
- `--report-output PATH` — write report anywhere
- `--engagement-id ID` — override ROE detection

## Integration with cluster 1-4 skills

The cluster 1-4 scan skills all emit findings via `report.emit()`
(in `lib/report.py`) which can write JSON / JSONL / Markdown.
For composition workflow, each scan should run with
`--format jsonl --output engagement/findings/<skill>-<date>.jsonl`:

```bash
python3 plugins/security/penetration-tester/skills/checking-http-security-headers/scripts/check_headers.py \
    https://app.acme.example --format jsonl \
    --output engagements/acme-2026-q2/findings/headers-$(date +%Y%m%d).jsonl
```

The skill then picks up every file under `findings/` by default.

## Stable-rendering verification

To verify the report is rendering stably (same input → same output
except timestamp):

```bash
# Render twice
python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --report-output /tmp/report-1.md
sleep 2
python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --report-output /tmp/report-2.md

# Diff ignoring the timestamp line
diff <(grep -v "Generated by" /tmp/report-1.md) \
     <(grep -v "Generated by" /tmp/report-2.md)
```

The diff should be empty. If it isn't, file an issue — stable
rendering is a contract this skill maintains.
