From claude-bughunter
Guides redaction of cookies, PII, and secrets in bug-bounty evidence (screenshots, HAR files, HTTP requests). Invoke before capturing PoC evidence or attaching HARs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-bughunter:evidence-hygieneThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Use this skill BEFORE capturing any screenshot, exporting any HAR, or attaching any evidence to a bug-bounty submission. It catches the most common evidence-hygiene mistakes that cause cookies to leak, PII to be shared without consent, or screenshots to be unsuitable for triage.
Use this skill BEFORE capturing any screenshot, exporting any HAR, or attaching any evidence to a bug-bounty submission. It catches the most common evidence-hygiene mistakes that cause cookies to leak, PII to be shared without consent, or screenshots to be unsuitable for triage.
The core principle: Bug-bounty evidence is meant to convince a triager. Anything beyond that — live cookies, real-user PII, internal trace IDs that aren't useful — should not be in the evidence.
Every PoC artifact (screenshot, HAR, raw HTTP request, terminal transcript) potentially contains data that needs different treatment.
| Category | Examples | Treatment |
|---|---|---|
| Your-account secrets | Session cookies, OAuth tokens, refresh tokens, API keys | Always redact. Even in private bug-bounty platform attachments. Your account, your session — protect it. |
| Other users' PII | Real names, emails, phone numbers, addresses, profile photos, account IDs | Redact unless explicitly demonstrating cross-account impact. Even then, mask faces and minimize the data you display. |
| Triager-useful metadata | Trace IDs (x-datadog-trace-id), request IDs, server timestamps, your test account UID/email, GraphQL operation names, response shapes | Leave visible — these help the triager correlate to logs and reproduce. |
| Test-account passwords (limited use) | Throwaway passwords on a test account (e.g., Testing@5678) | Acceptable in screenshots if you rotate immediately after submission so the value shown is dead. Don't leave real-use passwords in evidence. |
The session cookie value is the highest-value secret in any PoC. Mask:
authn, session, sid, __Secure-id, etc. — name varies per target)csrf-token if it's bound to your sessionAuthorization headers (Bearer tokens, JWT)Cookie request header values for any session-bearing cookieSet-Cookie response header values for any session-bearing cookie__cf_bm, _cfuvid) — these are bot-management, not session-bearingajs_anonymous_id, _ga)x-datadog-trace-id, x-request-id)Server: cloudflare, X-Frame-Options)bugcrowd-reporting)Method A — Don't capture the cookies in the first place (preferred when possible)
credentials: 'include' so the browser sends cookies automatically. Console output won't echo the cookie. Screenshot the Console output, never the Network tab Headers panel.Method B — Black-bar in image editor (when capture inevitably includes cookies)
Method C — Find/replace in raw text (for HAR files, terminal transcripts)
Before clicking Capture:
[ ] Network tab Headers panel is collapsed or out of frame
[ ] Burp's Request panel is hidden behind the divider drag
[ ] No "Copy as cURL" output is visible on screen
[ ] DevTools Application → Storage → Cookies tab is closed
[ ] Browser URL bar doesn't show a session token in query string (rare but possible)
After capturing:
[ ] Open the screenshot at full resolution before saving
[ ] Search for the session cookie name substring in any visible text — if present, redact
[ ] Search for the literal first 6 chars of your cookie value — if present, redact
[ ] Compare to the previous PoC screenshot in the same engagement — same redaction discipline
When a PoC necessarily exposes another user's data (e.g., demonstrating IDOR by showing the victim's email in an attacker-session response), redact the actual PII even in private attachments.
"first_name": "<REDACTED>")Bad (leaks victim's full PII):
{"data":{"contact":{"first_name":"Nadene","last_name":"Afton","email":"[email protected]","phone":"+1-555-867-5309"}}}
Good (proves the bug, masks the PII):
{"data":{"contact":{"first_name":"<REDACTED — real first name>","last_name":"<REDACTED — real last name>","email":"<REDACTED>@example.com","phone":"<REDACTED>"}}}
In screenshot form, black-bar each value with a rectangle annotation labeled "REAL PII REDACTED" if there's space.
Reference the redaction explicitly:
## Proof of Concept
The screenshot below demonstrates the IDOR. The attacker session (uid 12345678) successfully retrieves the victim's profile data (uid 99887766). **Real PII fields in the response are masked with black rectangles to limit unauthorized exposure of victim data, per responsible-disclosure hygiene.** The unredacted response is available privately on request.
This signals the triager that you're disciplined and gives them a clear path to the unredacted version if they need it for verification.
HAR (HTTP Archive) files are JSON dumps of network traffic with full request/response bodies and headers. They include cookies, auth tokens, and any PII that was in transit.
Chrome DevTools → Network tab → right-click anywhere in the request list → "Save all as HAR with content"
Use jq to strip sensitive headers. Save this as a shell function or one-liner you can re-use:
sanitize_har() {
local input="$1"
local output="${1%.har}.sanitized.har"
jq '
.log.entries |= map(
(.request.headers |= map(
if .name | ascii_downcase | IN("cookie", "authorization", "x-csrf-token") then .value = "<REDACTED>" else . end
)) |
(.response.headers |= map(
if .name | ascii_downcase | IN("set-cookie") then .value = "<REDACTED>" else . end
)) |
(.request.cookies |= map(.value = "<REDACTED>")) |
(.response.cookies |= map(.value = "<REDACTED>"))
)
' "$input" > "$output"
echo "Sanitized: $output"
}
Usage:
sanitize_har /path/to/exported.har
# Output: /path/to/exported.sanitized.har
# Check that no Cookie or Authorization values are leaking
grep -i 'authn\|"cookie"\|authorization' /path/to/exported.sanitized.har | head -20
If you see your real cookie value in the output, the sanitization missed something — fix the jq filter for that specific field name.
If the HAR captured cross-account data (e.g., during an IDOR demo), additionally strip the response body fields that contain victim PII. Add to the jq filter:
(.response.content.text |= (
if . then
(fromjson? // .) | tostring | gsub("real.first.name.example"; "<REDACTED>")
else . end
))
Customize the gsub patterns to your specific captured data.
The Results window is the strongest evidence for rate-limit findings. To capture cleanly:
Request#, Payload, Status code, Response received, LengthAlmost never the right screenshot — it shows entire request/response pairs with cookies. Use Repeater for demos instead.
The Scanner tab's Issues panel is generally safe to screenshot — it shows finding summaries without the underlying request bodies. Click into a specific finding before screenshotting only if you've redacted its evidence first.
fetch('/api/endpoint', {
method: 'POST',
headers: {'content-type': 'application/json'},
credentials: 'include', // sends cookies automatically — they won't appear in your code
body: JSON.stringify({ /* your payload */ })
}).then(r => r.json()).then(j => console.log("LABEL:", JSON.stringify(j)))
Why this is clean:
credentials: 'include' means the browser sends cookies. Your code never references them. They never appear in screenshots.console.log("LABEL:", ...) produces a labeled output line you can search for in the screenshotJSON.stringify(j) formats the response on a single line — easier to crop tightlyFor a 4-step PoC (verify before / change / verify after / revert), clear the console between calls so each screenshot only shows ONE call and ONE response:
Cmd+KCtrl+LTake the screenshot immediately after the response prints — don't wait for unrelated framework warnings to appear.
If the response body is too long for a clean screenshot, log a summary instead:
fetch('/api/endpoint', { /* ... */ })
.then(r => r.json())
.then(j => {
console.log("RESPONSE SHAPE:", Object.keys(j.data || {}));
console.log("CRITICAL FIELD:", j.data?.victim_id, j.data?.email);
// (Don't dump the full j — your screenshot becomes unreadable)
})
Chrome blocks paste into DevTools Console as anti-self-XSS protection. Type allow pasting (literal phrase) once at the start of each session. Do this BEFORE you take any "running PoC" screenshot — otherwise the warning text appears in the screenshot.
For findings like "password change without step-up," the canonical 5-screenshot pattern is:
verify_password("current") → true. Proves you knew the starting state.update_password(...) succeeds without step-up. THE most important screenshot.verify_password("old") → false. Proves the change took effect.verify_password("new") → true. Proves the new credential is active.Numbered, descriptive, hyphen-separated:
{finding-#}-step{step-#}-{description}.png
Examples:
04-step1-verify-true-before.png
04-step2-update-password-no-stepup.png
04-step3-verify-false-after.png
04-step4-verify-true-new-pw.png
04-step5-inbox-notification-check.png
In the report body, reference each by filename:
- **Screenshot 1 (`04-step1-verify-true-before.png`)** — verify_password returns true with the current password.
- **Screenshot 2 (`04-step2-update-password-no-stepup.png`)** — update_password returns User with no StepUpRequiredError. THIS IS THE BUG.
- **Screenshot 3 (`04-step3-verify-false-after.png`)** — verify_password with old password returns false.
- **Screenshot 4 (`04-step4-verify-true-new-pw.png`)** — verify_password with new password returns true.
- **Screenshot 5 (`04-step5-inbox-notification-check.png`)** — inbox showing whether out-of-band notification was sent.
After clicking Submit on the bug-bounty platform:
1. Log out of the target
2. Log back in (this rotates the session cookie — any cookie shown in your screenshots is now dead)
3. Change the password to a fresh value not used elsewhere (any password shown in your screenshots is now dead)
Keep your unredacted HARs / screenshots in a local folder accessible only to you. The triager may ask for the unredacted version during verification. Never share unredacted artifacts via email — use the platform's private attachment system on the existing submission thread.
Most platforms' confidentiality applies to BOTH unresolved and resolved issues. Don't tweet, blog, or discuss the finding in any forum until the program explicitly says you can.
Quarterly, sweep your ~/security-research/ and ~/Downloads/ for stale HARs / screenshots from old engagements. Either move them to a properly-encrypted archive or delete them. Bug-bounty engagement artifacts are sensitive — they accumulate fast and become a liability.
| For this question / task | Use this skill |
|---|---|
| "Should I report this finding at all?" | triage-validation |
| "What's the report body template?" | report-writing |
| "What VRT / severity / OOS rebuttal for Bugcrowd?" | bugcrowd-reporting |
| "How do I redact / sanitize this evidence?" | This skill (evidence-hygiene) |
| "How do I demonstrate the bug actually fires?" | security-arsenal |
| "Where are the recon probes for this asset class?" | offensive-osint |
This skill is meant to be loaded together with report-writing and (for Bugcrowd) bugcrowd-reporting when preparing a submission. It does NOT cover:
triage-validation and report-writing's job — they tell you what evidence is required for the report to be accepted)security-arsenal's job — payloads to send)offensive-osint's job — recon)This skill covers ONLY the redaction / sanitization / hygiene layer that sits between "I have evidence" and "I'm attaching it to the platform."
report-writing — When evidence is redacted and ready for the report body. Workflow primitive: after redaction passes the pre-screenshot checklist, hand off to report-writing for the platform-specific report template (H1 / Bugcrowd / Intigriti / Immunefi).bugcrowd-reporting — When the redacted evidence is destined for Bugcrowd specifically. Workflow primitive: redacted screenshots + sanitized HARs become attachments referenced in bugcrowd-reporting's VRT-mapped submission body.triage-validation — When deciding which evidence is required at all. Workflow primitive: triage-validation says "you need a 5-screenshot PoC for password-change-without-step-up"; this skill says "here's how to capture and redact those 5 screenshots cleanly."security-arsenal — When the payload that produced the evidence is being documented. Workflow primitive: the payload (from security-arsenal) goes in the report verbatim; its output / response (handled by this skill) gets cookie / PII redacted before screenshotting.bb-methodology — When Phase 5 (Validate & Report) needs evidence captured. Workflow primitive: Phase 5's evidence-capture step routes through this skill before the report is drafted.npx claudepluginhub elementalsouls/claude-bughunterSanitizes security evidence (HAR, logs, screenshots, curl, PoC output) by redacting tokens, cookies, PII, and internal data while preserving reproducibility for reports and writeups.
Provides Bugcrowd-specific reporting tactics: VRT category fallback strategy, manual severity override, OOS-clause rebuttal templates, chained-finding cross-references, and target selection for QA-vs-prod programs.
Hunts API keys, tokens, and credentials in JS bundles, GitHub repos, Postman collections, and API specs using TruffleHog, Gitleaks during pentest phase 2 from recon data.