Traces hands a language model influence over a running page, so the security posture is a design constraint here rather than a checklist. The reasoning, the trust boundaries, and the things deliberately left out of scope are all in docs/threat-model.md — read that first if you want to know what this project already considers an attack.
This file is the short version, plus how to tell us.
There is one supported version: whatever is on main. There are no release branches and no
backports.
| Version | Supported |
|---|---|
main |
✅ |
| Anything else | ❌ |
Please do not open a public issue for a security problem.
Use GitHub's private reporting: Report a vulnerability. It creates a private thread visible only to you and the maintainers.
If that link is unavailable, contact @ribdsp on GitHub and say only that you have a security report — no details in a public channel.
What helps:
- The tool name, if a WebMCP tool is involved, and the exact arguments
- Which WebMCP mode you were in: real origin trial, the polyfill, or the
webmcp-toolsinspector - Browser and version
- Whether the input came from a model, a human, or a recording file
- What you expected the boundary to be, and what actually happened
What to expect: this started as a hackathon project and is maintained on a best-effort basis. You will get an acknowledgement, and a real answer rather than a template. If a report is valid we will say so publicly in the fix.
The boundaries below are the ones this project claims to hold. A way past any of them is a vulnerability, and the first three are the ones with tests behind them:
- Nothing that comes from the model is executed. Bisect predicates are a closed set of validated
structured objects — never
eval, nevernew Function, never an interpolated expression, never a compiled regex.no-eval.test.tsgreps the source and fails the build ifeval(ornew Functionappears anywhere. Any path that gets model-supplied text evaluated is in scope. - Every tool response has a size budget, enforced by tests rather than by intention. A response that can be made to exceed its budget — or a compression path that leaks the full DOM — is in scope.
- No real user recordings in this repository. Every committed recording is synthetic, produced by
bugbait/. If you find one that looks like a real session, that is a valid report and an urgent one. - The recorder records shapes, not contents.
maskAllInputs: trueis mandatory: the recorder stores that a field was typed into and how many characters, never the value. The XHR/fetch recorder stores method, URL, status and timing, never request or response bodies. A path that captures a value or a body is in scope. - Selector and timestamp validation. A selector or timestamp from a model that reaches an unguarded API, or escapes the recording's time range, is in scope.
- Origin-trial token handling. The token belongs in
.env.localand must never be committed. A token in git history is a valid report.
Not because they don't matter, but because they are known and stated rather than hidden:
- A malicious recording file you load yourself. Traces replays recordings you give it. Loading a hostile file is equivalent to opening a hostile document — see the threat model for where the line is drawn.
- The WebMCP spec itself, including the absence of an
"image"content type. Report those upstream at webmachinelearning/webmcp. - Anything requiring a compromised browser, extension, or agent client. If the agent host is already hostile, it does not need our tools.
bugbait/being insecure on purpose. It is a deliberately broken checkout app whose whole job is to produce bugs to record. Its API routes return wrong data by design. Do not report those as vulnerabilities; do report anything inbugbait/that could harm someone running it locally.- Denial of service against your own tab. A bisect over a huge range is slow. That is a budget question, not a security one.
There is no backend, no database, no API key, and no account. Recordings are static JSON, and every computation — replay, binary search, DOM compression, report validation — happens in the tab. Nothing is uploaded. That removes an entire class of report, and it is the main reason the attack surface here is small.