Use GitHub's private vulnerability reporting. Please do not open a public issue for something exploitable.
Expect an acknowledgement within a week. This is a small project maintained in spare time, so a fix may take longer than that, and I will say so rather than go quiet.
Read this before reporting. Several things that look like vulnerabilities here are documented design, and saying so up front is more useful than saying it in a reply.
A tool is first-party code. A tool is written by whoever runs the site and bundled at build time.
There is no mechanism to load a tool from a URL at runtime, and that is deliberate: doing it on a static
site means eval, which costs bundling, tree shaking, and any Content-Security-Policy worth having.
Worker mode is a stability boundary, not a security boundary. A worker-mode tool runs on its own thread so a runaway loop can be terminated without freezing the page. It shares the page's origin, cookies and storage. It is not a sandbox and the documentation never claims otherwise. If you need to run code you did not write, this is the wrong design and you want an iframe with a separate origin, or a server.
What a reader types is visible to the page that embedded the tool. <tool-host> attaches its shadow
root in open mode, so the host page can already read every control through shadowRoot, and the values
getter makes that supported rather than a reach into internals. So a tool is not a private place to put
something: whatever a reader pastes, the surrounding page can read and send anywhere. That is the same trust
level as any script on the page, and the host is first-party code by the paragraph above, but it is worth
stating because these tools invite people to paste real data. If you need input the embedding page cannot
see, this is the wrong design for the same reason worker mode is not a sandbox.
In scope, and worth reporting:
- an XSS path through tool output, a manifest, or a seed. Every node is built through the three helpers
in
packages/runtime/src/dom.ts, which usecreateElement,createElementNSandtextContent. Neither package containsinnerHTML,insertAdjacentHTML,evalornew Function, so a way to inject markup would be a real bug. The optional hosthighlighthook must return aNodefor the same reason: the runtime appends that node and will not interpret a string as HTML. A host that parses a highlighter's HTML string into a node is taking on that XSS surface; the runtime will not do it for them; - a way for one tool on a page to reach another tool's state or results;
- a way to escape the shadow root and alter the host page's DOM;
- the manifest validator accepting something that then causes unsafe behaviour downstream;
- a prototype-pollution path through
upgradeManifest,validateManifestor the fixture comparison, all of which walk untrusted-shaped JSON; - a way to make the runtime fetch or execute code from a location the host did not configure;
- anything in a published tarball that is not in this repository.
Out of scope:
- a tool that computes the wrong answer. That is a bug, not a vulnerability;
- a tool that is slow, or that hangs on the main thread.
min/maxon inputs and worker mode are the documented mitigations, and a host that ignores both gets what it configured; - a worker-mode tool reading
document.cookiethrough its own means. See above: not a sandbox; - a host choosing to render untrusted Markdown from a tool's
helpfield. Rendering that is the host's job and its choice of renderer; - denial of service by feeding a tool an enormous input, where the manifest declared no bounds. The
validator requires
minandmaxon number inputs for this reason; atextareawith nomaxLengthis the host's decision.
- Both published packages have zero runtime dependencies other than
@toolbench/runtimedepending on@toolbench/sdk. There is nothing else in the tree a consumer installs. - Releases are published from CI with no stored npm credential. Authentication is an OIDC token
validated against a trusted publisher pinned to this repository, this workflow, and the
npm-publishenvironment, which requires a manual approval. Verify withnpm audit signatures. - Releases carry npm provenance, so npm shows a signed attestation tying the tarball to the commit and workflow that built it.
- 0.1.0 is the one exception. It was published by hand during bootstrap, so it carries no attestation. npm will not let a trusted publisher be configured until a package exists, and provenance needs OIDC, which only exists in CI: the first release of a package cannot have both. Every release from 0.1.1 onward is published from CI and attested. A later version without provenance would be worth treating as suspicious.
files: ["dist"], and CI printsnpm pack --dry-runfor both packages on every build, so what ships is visible in the log.- Development requires Node 24 and pnpm with a frozen lockfile in CI.
Pre-1.0, only the latest minor gets fixes. Once 1.0 ships this section will say something more useful.