mcpgaze sits in your MCP protocol path and persists a record of the traffic it sees, so its security posture is a first-class concern. This document covers what it does with your data, the threat model, and how to report a vulnerability.
Please do not open a public issue for security problems.
Report privately via GitHub's private vulnerability reporting — on the repository, go to Security → Report a vulnerability. Include a description, affected version/commit, reproduction steps (a self-contained script is ideal), and impact.
We aim to acknowledge within a few days, confirm or refute, and coordinate a fix and disclosure timeline with you. Good-faith research is welcome.
| Version | Supported |
|---|---|
1.0.x |
✅ |
< 1.0 |
— |
mcpgaze's safety rests on two invariants, enforced by generative tests on every push:
- (A) Wire integrity — on the forward path, bytes in == bytes out. Redaction, classification, and every other transform touch only copies; they never alter the forwarded protocol stream.
- (B) Observer safety — the observation/logging path never throws into the wire. Adversarial input degrades a log line, never your session.
A corollary worth stating plainly: redaction is never applied to the forwarded stream. It only ever masks on-disk artifacts and outbound triage egress. A wiretap that altered the bytes it forwards would be worse than useless.
| Artifact | Written by | Default path | Mode | Redaction |
|---|---|---|---|---|
| Session log (JSONL) | wrap, wrap-http, record |
.mcpgaze/session-<ts>.jsonl |
0600 |
off by default; --redact masks at rest |
| Cassette | record |
mcpgaze.cassette.json |
0600 |
on by default; --no-redact captures verbatim |
| Health status | health |
.mcpgaze/health.json |
default | n/a (tool names + a schema hash, no payloads) |
Notes:
.mcpgaze/is git-ignored by default;mcpgaze.cassette.jsonand*.cassette.jsonare git-ignored by default.- The session log can contain JSON-RPC params/results and verbatim server stderr — which may carry secrets. That's intentional for a live debug view, which is why the file is owner-only (
0600). If you tee a session into a file you intend to share, pass--redact. - The transport
AuthorizationHTTP header is not logged inwrap-http(only the JSON-RPC body is observed). Bearer tokens appear in the log only if they're inside params/results or printed to stderr.
When redaction is active, a conservative, dependency-free, fail-safe masker (src/redact.ts) runs over observer artifacts and triage egress — never the wire. It:
- masks the values of credential-named keys (
password,secret,api_key,token,authorization,cookie,session,credential,private_key, …), and - masks secret-shaped values in free text:
user:pass@hostDSN passwords,sk-…/sk-ant-…provider keys, AWSAKIA…ids, GitHub/Slack prefixed tokens,Bearer …, and JWT-shaped blobs.
It is intentionally conservative — it can miss a bespoke secret format — and is opt-in for at-rest artifacts (--redact, except record which is on by default) and always-on for triage egress. Always review a cassette or a log before sharing it.
triage --ai POSTs failure context to https://api.anthropic.com/v1/messages. It is gated so this never happens silently:
- Requires both the
--aiflag andANTHROPIC_API_KEYin the environment. - Always redacts secret-shaped tokens at the egress boundary.
- Requires explicit consent —
--yes, or ayat an interactive preview that shows the exact bytes. In a non-interactive context without--yes, nothing is sent.
The destination is fixed to Anthropic (no attacker-controllable URL), and the API key is read from the environment, never from log content. Be mindful that redacted failure context still derives from your session.
wrap-http binds to 127.0.0.1 only and rejects cross-origin browser requests (DNS-rebinding defense — the bug class behind the MCP Inspector's CVE-2025-49596, CVSS 9.4). Binding to a non-loopback --host prints a warning; --allow-origin overrides the Origin policy.
For credentials:
- Single
--upstream/--route— there's exactly one destination, so the client'sAuthorization/Cookieis forwarded to it (nothing can be misrouted). Pass--no-forward-credentialsto strip them anyway. - Multiple
--routes — a path could resolve to a different upstream than the client intended to authenticate to, so credentials are stripped unless a route opts in (--creds-route <prefix>, or--forward-credentialsfor all). On the return path,Set-Cookie/Mcp-Session-Idare likewise stripped for non-opted routes.
A cassette is a data file that verify/replay consume. A hostile cassette (downloaded fixture, shared CI artifact, supply-chain) can name arbitrary methods/params. Mitigations:
verifyre-issues only read-only methods by default; state-changing methods (e.g.tools/call) are skipped and reported unless you pass--allow-tool-calls.- Cassettes are shape-validated on load, and the replay/verify paths are wrapped so a malformed or pathological cassette fails cleanly (no crash into the wire, no prototype pollution).
Still: only run verify --allow-tool-calls against a cassette you trust, and against a disposable/read-only server.
mcpgaze v1.0.0 underwent a source-level red-team audit with reproducible proof-of-concept exploits. Result: 0 critical, 0 high, 3 medium, 2 low — all remediated or consciously mitigated (the medium findings were secret-at-rest and triage-egress governance; the low findings were the credential-scoping and untrusted-cassette surfaces described above). Several attack classes (SSRF/DNS-rebinding via routing or redirects, cassette parser crashes, prototype pollution) were probed and found defended.
Findings and remediation status are summarized in the CHANGELOG. The proof-of-concept exploit scripts are retained privately rather than published, since they include runnable attack code.
A handful of low-severity behaviors are accepted for v1.0 and documented in KNOWN-ISSUES.md (reused-id statistics, first-Ctrl-C shutdown latency, wrap-http disconnect cleanup, and the Rust classifier's parse-free residuals). None violate invariant (A) or (B).