Imported from jeremylongshore/tons-of-skills-marketplace (
plugins/security/penetration-tester/skills/recording-pentest-engagement/SKILL.md). Install upstream withnpx skills add jeremylongshore/tons-of-skills-marketplace --skill recording-pentest-engagement. Copyright stays with the author (MIT).
Recording Pentest Engagement
Overview
A penetration test produces a lot of artifacts: scan outputs in multiple formats, screenshots showing the state of vulnerable pages, raw tool logs (nmap, Burp, custom scripts), the ROE and any amendments, exec-summary docs, and the findings themselves. Six months after the engagement closes, a question arises — sometimes benign ("can you remind me what we found?"), sometimes adversarial ("the customer claims we accessed an out-of-scope system; show us the logs"). The answer needs to be: "yes, here's the archive, and here's cryptographic proof it hasn't been touched since the engagement closed."
This skill packages the engagement directory into a single timestamped archive with a SHA-256 manifest covering every file. Optionally signs the manifest with GPG. The result is a portable, self-verifying record of what the engagement produced.
The skill also surfaces inconsistencies that would weaken the chain-of-custody claim: out-of-tree paths referenced in findings (meaning the finding refers to a file not actually in the archive), files in the directory not listed in the manifest, manifest entries whose hashes don't match. These are HIGH findings — they mean the archive is incomplete or has been modified after the fact, neither of which is acceptable for evidence purposes.
When the skill produces findings
| Finding | Severity | Threshold | Affected control |
|---|---|---|---|
| File in tree not in manifest | HIGH | Found during walk; manifest entry missing | (evidence integrity) |
| Manifest entry hash mismatch | CRITICAL | Computed SHA-256 differs from manifest | (evidence integrity) |
| Manifest entry without file | HIGH | Manifest lists a path that doesn't exist | (evidence integrity) |
| Findings reference out-of-tree path | MEDIUM | A finding's evidence field points to a file not in the archive |
(evidence completeness) |
| Symlink in tree | MEDIUM | Symlinks break archive portability and integrity | (evidence integrity) |
| Empty file in tree | INFO | 0-byte file; possibly an export error | (operational) |
| Archive package complete | INFO | All checks pass | (positive confirmation) |
| Manifest signed | INFO | GPG signature present and valid form | (positive confirmation) |
Prerequisites
- Python 3.9+
- An engagement directory laid out as recommended (see structure below)
- Optional GPG installed for manifest signing
Recommended engagement directory structure
engagements/acme-2026-q2/
├── roe.yaml
├── roe.amendments/
│ └── amendment-001-20260615.yaml
├── scope/
│ ├── allowed-ips.txt
│ └── normalized-targets.json
├── findings/
│ ├── cluster1-tls-2026-06-05.json
│ ├── cluster1-headers-2026-06-05.json
│ ├── cluster3-secrets-2026-06-07.json
│ └── ...
├── evidence/
│ ├── screenshots/
│ ├── tool-logs/
│ └── raw-scan-output/
├── reports/
│ ├── vulnerability-report.md
│ ├── owasp-mapping.json
│ └── executive-summary.md
└── manifest.sha256 # produced by this skill
Instructions
Step 1 — Identify the engagement directory
python3 ./scripts/record_engagement.py engagements/acme-2026-q2/
The skill walks the directory recursively, builds a SHA-256 manifest, and produces a chain-of-custody report.
Step 2 — Run the packager
Options:
Usage: record_engagement.py PATH [OPTIONS]
Options:
--output FILE Findings output
--format FMT json | jsonl | markdown (default: markdown)
--min-severity SEV default info
--manifest FILE Manifest output path (default: PATH/manifest.sha256)
--tar FILE Also create a .tar.gz archive at this path
--sign Sign the manifest with GPG (uses default identity)
--signer KEY GPG key ID to sign with
--exclude GLOB Skip files matching glob (repeatable)
Step 3 — Verify the manifest
The skill emits a SHA-256 manifest in standard sha256sum format
(one line per file: hash + two-space + path). The manifest is
verifiable independent of the skill:
sha256sum -c engagements/acme-2026-q2/manifest.sha256
Anyone with access to the archive can run this check; if the output is "all OK," the archive is intact.
Step 4 — Sign the manifest (optional)
python3 ./scripts/record_engagement.py engagements/acme-2026-q2/ --sign
The skill shells out to gpg --detach-sign against the manifest,
producing manifest.sha256.asc. Later verification:
gpg --verify manifest.sha256.asc manifest.sha256
sha256sum -c manifest.sha256
Both checks together: the manifest's contents match the archive, AND the manifest itself is signed by an identifiable party.
Step 5 — Create the portable archive (optional)
python3 ./scripts/record_engagement.py engagements/acme-2026-q2/ \
--tar engagements/archives/acme-2026-q2.tar.gz
The tarball contains the engagement directory + manifest + signature. Hand the tarball to legal / archive / customer.
Examples
Example 1 — End-of-engagement closeout
python3 ./scripts/record_engagement.py engagements/acme-2026-q2/ \
--sign \
--tar engagements/archives/acme-2026-q2.tar.gz \
--output engagements/acme-2026-q2/chain-of-custody.md
Example 2 — Daily snapshot during a long engagement
DATE=$(date +%Y%m%d)
python3 ./scripts/record_engagement.py engagements/acme-2026-q2/ \
--manifest engagements/acme-2026-q2/manifest-$DATE.sha256 \
--output engagements/acme-2026-q2/snapshot-$DATE.md
Daily snapshots build a time-series of the engagement's state which can be useful in incident-investigation contexts.
Example 3 — Pre-handoff integrity check
# Customer claims the archive is incomplete; verify before disputing
python3 ./scripts/record_engagement.py engagements/acme-2026-q2/ --min-severity high
If exit code is 0, the archive is internally consistent.
Output
JSON / JSONL / Markdown per lib/report.py. Exit codes: 0 clean,
1 high/critical, 2 error.
Each Finding includes:
id—evidence::<issue>::<path>severity— CRITICAL / HIGH / MEDIUM / INFOcategory—evidence-chainsummary— what's wrong with the artifactevidence— file path, expected hash, observed hash, manifest entry
Error Handling
- Path doesn't exist → exits 2 with operational error.
- Permission denied reading a file → emits HIGH finding, continues walking other files.
- GPG not installed with
--signrequested → emits HIGH finding, manifest written unsigned, exits 1. - GPG signing fails (no default identity, etc.) → emits HIGH finding, manifest written unsigned, exits 1.
- tar command fails with
--tarrequested → manifest still written; archive creation failure surfaces as HIGH finding.
Resources
references/THEORY.md— Chain of custody as a legal concept, evidence-integrity standards (NIST SP 800-86), SHA-256 vs SHA-3 vs SHA-512 tradeoffs, GPG detached-signature semantics, archive format choices (tar vs zip vs WORM), retention horizons per jurisdictionreferences/PLAYBOOK.md— Engagement directory templates per engagement type, daily-snapshot cron pattern, customer-handoff protocol, dispute-resolution playbook (when the customer challenges the archive), long-term storage and access-control patterns