Handle captures safely¶
A HAR is a recording of everything a session sent and received. In a real capture that includes session cookies, bearer tokens, passwords in login bodies, API keys in URLs, and personal data in responses. Anyone who holds the file can use the credentials in it until they expire, and can read everything else in it for as long as they keep it.
Keep the file, share the findings¶
For most conversations you do not need to hand over the capture. You need to hand over what you found.
| Instead of the HAR, send | Command | What it holds |
|---|---|---|
| A summary of the problem | report |
Counts, hosts, errors with the server's message, slowest calls |
| What a backend team needs | handoff |
Times, correlation ids, server address and a masked curl for each failing call |
| One request in full | show-entry |
Headers and bodies with known secrets masked |
| The ranked findings | diagnose |
Findings with entry ids |
What WireTrail masks, and what it does not¶
WireTrail masks a fixed set of headers, a fixed set of JSON keys, secret-looking query parameters, and anything that looks like a long random
token. It does not recognize every secret. search and extract mask the least, errors, report, diagnose and summary print the server's messages unmasked, and a JWT in an
unusual header can pass through. How redaction works has the full list, command by command.
Read the output once before you send it. A search for the words that should not be there is a fast check:
No output is encouraging and not a guarantee.
If you must send a HAR¶
WireTrail does not write a sanitized HAR. It masks what it prints, and the file on disk is untouched. To send the capture itself, strip it
first. This jq program removes the headers that carry credentials, the cookies, and every request and response body:
jq '(.log.entries[] | .request.headers, .response.headers) |= map(select(.name | ascii_downcase | IN("authorization","cookie","set-cookie","proxy-authorization","x-api-key","x-device-token") | not)) | del(.log.entries[].request.cookies, .log.entries[].response.cookies, .log.entries[].request.postData, .log.entries[].response.content.text)' checkout.har > stripped.har && wiretrail stripped.har validate
HAR 1.2 via WireTrail demo generator (synthetic) (99 entries)
with timings: 99% · response bodies: 0% · POST req bodies: 0%
auth headers: false · cookies: false
sanitized: yes (no auth/cookies and few response bodies)
validate now says sanitized: yes, and a search for the header name finds nothing:
What the program does not remove is as important:
-
Secrets in URLs. A token in a query string stays:
-
Credentials in headers you did not name. The list above is a start. Look at the header names in your own capture and add the ones that carry tokens.
- Personal data in URLs and paths, such as an email address in a path.
- Host names, paths and timing, which can describe your system to an outsider.
A stripped capture is also a weaker one: with no bodies and no Authorization headers, errors shows no messages and auth shows nothing. Strip for
the reader, and keep the full capture for yourself.
Where captures should live¶
- Not in the repository unless it is a deliberately sanitized fixture. Add
*.harto.gitignore. - Not in a ticket attachment that many people can open.
- Not in a shared chat where it will stay in the history.
- Delete it when the investigation is over.
If a capture with live credentials has left your control, treat those credentials as exposed and rotate them.
Agents and CI¶
An AI agent that reads a capture has read all of it, and its replies can quote any of it. Run the agent on a stripped capture when you can. Keep captures out of build logs: gate on exit codes, and do not print a capture's contents. See The Claude Code plugin and Gate a build.
Related¶
- How redaction works lists exactly what is masked.
- Handoff and Report are the shareable outputs.
- What a useful capture holds for what you lose by stripping.