Inspiration
Coding agents now read documentation and install packages on behalf of developers. They read the raw text of a page, including text styled to be invisible. Supply-chain attacks such as the 2025 Shai-Hulud npm worm showed the damage a single malicious package can cause. A documentation page with a hidden install instruction combines both risks. The developer cannot see the instruction. The agent can.
What it does
- Agents fetch pages through HiddenInk's safe_fetch tool, which renders each page in headless Chrome on an isolated Vultr server.
- The detection engine finds white-on-white text, tiny and off-screen text, hidden elements, invisible Unicode and hidden fragments inside code blocks.
- The agent receives only the visible text, plus a warning when hidden content was removed.
- Gemini reads only the visible documentation and produces a strict list of expected packages and commands.
- A Claude Code hook checks every install command and dependency change against that list and blocks anything outside it.
- The X-ray Chrome extension dims the page and highlights hidden text in place, so developers see what their agent read.
- The extension also guards pastes into chatbots and PDF uploads.
How we built it
- A TypeScript detection engine using DOM and computed-style analysis, PDF render-and-sample checks, Unicode decoding and a scoring model.
- A Node server with Fastify and Playwright exposing safe-fetch, check-action and an MCP endpoint, deployed on Vultr with Docker and Caddy.
- Gemini with structured JSON output for the expected-actions manifest, with caching and an offline fallback.
- A Claude Code PreToolUse hook and permission settings that route all web access through HiddenInk.
- A Chrome extension built with WXT, React and Shadow DOM.
- A Python and Playwright evaluation harness over a labelled corpus of attack and benign pages and PDFs.
- Fictional demo packages served from a local index. The demo payload is a harmless canary.
Challenges we ran into
Hidden text takes many forms, and each needs its own detection method. Some legitimate pages also hide text, such as screen-reader labels and collapsed menus, so keeping false positives low took careful rules. Hidden fragments inside code blocks required tracking hidden spans at the character level. Integrating with Claude Code meant closing every route to the web, including shell tools.
Accomplishments that we're proud of
- Layer 1 removed all 58 hidden payloads in the test corpus (43 web, 15 PDF) before they reached the agent.
- No visible text was lost on any of the 21 benign files.
- Layer 2 blocked every unrequested install in testing and allowed every requested one.
- Scans take about 1 ms per web page and 15 ms per PDF.
- A working integration with Claude Code through MCP and hooks.
What we learned
Human review is a key safeguard for AI agents. It assumes the human and the agent see the same content. Hidden text breaks that assumption. Visibility can be measured, so this class of attack can be stopped with deterministic checks.
What's next for HiddenInk
Making safe_fetch the default fetch path for coding agents. Extending action checks to npm and other ecosystems. Detecting hidden text in images and screenshots. Packaging the hook and MCP server for one-command setup.
Built With
- caddy
- chrome
- claude-code
- css
- docker
- fastify
- gemini-api
- google-genai
- html
- javascript
- model-context-protocol
- node.js
- pdf.js
- playwright
- pnpm
- python
- react
- tailwindcss
- typescript
- vultr
- wxt