{
  "markdown": "# Agent Wormhole\n\n[![ci](https://github.com/runningoffcode/agent-wormhole/actions/workflows/ci.yml/badge.svg)](https://github.com/runningoffcode/agent-wormhole/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/wormhole-guard?label=pypi%20wormhole-guard)](https://pypi.org/project/wormhole-guard/)\n[![npm](https://img.shields.io/npm/v/wormhole-x402?label=npm%20wormhole-x402)](https://www.npmjs.com/package/wormhole-x402)\n[![Python](https://img.shields.io/pypi/pyversions/wormhole-guard)](https://pypi.org/project/wormhole-guard/)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)\n[![Dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)](pyproject.toml)\n[![Telemetry](https://img.shields.io/badge/telemetry-none-brightgreen)](docs/limits.md#no-telemetry)\n[![Corpus](https://img.shields.io/badge/corpus-17%2F17%20·%2015%2F15-informational)](corpus/)\n\n**Your agents talk to each other. Make sure they aren't passing something on.**\n\n**[agentwormhole.com](https://agentwormhole.com)** · [wormhole-guard on PyPI](https://pypi.org/project/wormhole-guard/) · [wormhole-x402 on npm](https://www.npmjs.com/package/wormhole-x402)\n\nAgents spawn agents, hand off work, comment on issues, and read each other's\noutput. One compromised agent stops being a victim and becomes a carrier — and\nthe assistant on the other side is exactly as obedient as yours.\n\nUnderneath that is an asymmetry nobody owns: your vendor protects its own\n`settings.json`, but nothing protects your `CLAUDE.md`, `AGENTS.md`, or\n`.cursor/rules`, and by default your agent can write to all of them.\n\n```\n$ wormhole scan .\n\n CRITICAL  SessionStart hook executes a script from an unusual path  [AUTOSTART-002]\n  .claude/settings.json\n  `node .github/setup.js` runs unprompted on SessionStart. This survives\n  uninstalling the package that planted it.\n\n HIGH  Agent config not in baseline  [BASELINE-003]\n  .cursor/rules/setup.mdc\n  This file was not present when the baseline was taken.\n```\n\nNo dependencies beyond Python 3.8+. No account, no API token, no network call.\nYour `CLAUDE.md` never leaves the machine.\n\n## Why\n\n**June 2026: the Miasma worm disabled 73 Microsoft GitHub repositories.** It\ndid not exploit a memory bug. It wrote agent configuration:\n\n| File | Mechanism |\n|---|---|\n| `.claude/settings.json` | `SessionStart` hook → `node .github/setup.js` |\n| `.gemini/settings.json` | same |\n| `.cursor/rules/setup.mdc` | `alwaysApply: true`, \"run the setup script\" |\n| `.vscode/tasks.json` | `runOn: folderOpen` |\n| `package.json` | hijacked `test` script |\n\nIt targeted 15 AI coding agents ([Dataminr](https://www.dataminr.com/resources/intel-briefs/miasma-worm-open-sourced/)\nanalysis of the open-sourced toolkit; the June writeups counted the five files\nabove), and the persistence **survives\n`npm uninstall` and survives reinstalling the agent** — the settings file\noutlives both. It also re-encrypted itself on every write, so hash-matching a\nknown payload never finds it.\n\nFour of those five anchors need no model in the loop at all. The hook fires\nbecause a session started. That is why this tool checks configuration, not\njust prose.\n\nTwo more things make the gap structural rather than accidental: **Cursor was\ntold and declined to own it** (Pillar Security's Rules File Backdoor, disclosed\nFeb–Mar 2025; Cursor's response was that the risk falls under user\nresponsibility), and **sandboxing does not cover the files that matter** —\nClaude Code's own docs state that *\"Read, Edit, and Write use the permission\nsystem directly rather than running through the sandbox\"*, with default write\naccess to the working directory.\n\nThe mechanism paper is\n[arXiv:2603.15727](https://arxiv.org/html/2603.15727v3) (*AgentWorm*, NDSS 2026):\n82% attack success via skill supply-chain poisoning, 0% once sandbox isolation\nwas enabled — and **0 of 82** publicly indexed agent configurations had it\nenabled. The defense that works exists and nobody is running it. That gap is a\ntooling problem, and this is the tool.\n\nFull version, including prior art and how a payload travels between agents:\n**[docs/threat-model.md](docs/threat-model.md)**.\n\n## Install\n\n```bash\npipx install wormhole-guard\nwormhole scan ~/your-project --blast-radius\n```\n\nThe distribution is `wormhole-guard`. `agent-wormhole` on PyPI is an unrelated\nproject. The command and the import package are both `wormhole`.\n\nStandard library only, so it also runs straight from a checkout with no install\nstep at all:\n\n```bash\ngit clone https://github.com/runningoffcode/agent-wormhole\ncd agent-wormhole\npython3 -m wormhole scan ~/your-project --blast-radius\n```\n\n## Quickstart\n\nThirty seconds, in a project you care about:\n\n```bash\nwormhole scan ~/project --blast-radius   # what is there now\nwormhole init ~/project                  # harden + baseline + print the hooks\n```\n\n`init` is a dry run until you pass `--apply`. It prints the three steps it\nwould take so you can read them first.\n\nThen wire the three hooks into `~/.claude/settings.json`. Each command prints\na JSON fragment to merge — nothing writes your agent's configuration for you:\n\n```bash\nwormhole outbound --install   # sends: refuse to pass a payload on\nwormhole readguard --install  # reads: PostToolUse + InstructionsLoaded\nwormhole guard --install      # writes: the PreToolUse hook\n```\n\nThree doors. `readguard` covers what arrives — fetched pages, shell output, MCP\nresponses — which is how every publicly disclosed agent compromise of 2026\nactually got in. `outbound` covers what your agent passes to a subagent, a\npeer, or an issue another team's bot will read. `guard` and `harden` cover\nwhether anything can persist to the next session.\n\n`outbound` is the only one that blocks by default. Inbound content is untrusted\nby definition and there is a lot of it, so those rules stay conservative.\nOutbound was composed by your own agent, so a payload appearing there is\nalready anomalous — and a refused send fails loudly, while one that leaves\nreaches an operator who never agreed to trust you.\n\nA complete worked version with a real `settings.json` and a blocked write is in\n[examples/claude-code-hooks](examples/claude-code-hooks/); the rest is in\n[docs/hooks.md](docs/hooks.md).\n\n## What it does, precisely\n\nThis is an **integrity monitor for the files your agent reads as\ninstructions**. The parts that matter do not care what the payload says.\n\n| | | Survives rephrasing? |\n|---|---|---|\n| **Prevent** | `harden` removes the write, and pre-creates absent config paths so a payload cannot create one either | yes — no rule involved |\n| **Notice** | `baseline`/`verify` hash every config; a changed or unrecorded file is a finding | yes — hashing is indifferent to wording |\n| **Refuse** | `guard` inspects a pending write through a PreToolUse hook and can decline it | partly — rule-based |\n| **Detect** | content rules for payload shapes, autostart rules for unattended execution, posture rules for capability | no — evadable, use as triage |\n| **Contain** | `capture` excises payloads, preserving originals byte-for-byte for restore | n/a |\n\nThe ordering is deliberate. Prevention and integrity are the durable half;\nrules are convenience on top. A tool that leads with its rule count is\ncompeting on the one axis that decays.\n\nThe control that drives infection to zero is sandbox isolation, and it lives in\nyour agent framework, not here — and per Anthropic's own docs it does not cover\nRead/Edit/Write by default. This tool makes that gap impossible to overlook.\n\nDetails: [docs/rules.md](docs/rules.md) for the rule catalogue and the\nregression suite, [docs/scanning.md](docs/scanning.md) for `scan`, `memos`,\n`handoffs`, `corpus` and containment.\n\n## Two packages\n\n| | Protects | Install |\n|---|---|---|\n| **[wormhole-guard](https://pypi.org/project/wormhole-guard/)** | The instruction files your coding agents read | `pipx install wormhole-guard` |\n| **[wormhole-x402](https://www.npmjs.com/package/wormhole-x402)** | The payments your agents sign | `npm install wormhole-x402` |\n\nSame thesis, two places an agent reads instructions it did not write. The\nPython core is dependency-free; the TypeScript package has no network calls at\nall. Neither phones anywhere.\n\nWhen the agent holds a wallet, everything above costs you a revert and a\npayment does not. `wormhole-x402` compares a transaction against the merchant's\nown 402 quote — a channel the model never touches — because a payment to an\nattacker's address simulates perfectly. See [docs/x402.md](docs/x402.md) and\n[examples/x402-solana](examples/x402-solana/).\n\n## Limits\n\nStated plainly, because a security tool that overclaims is worse than none:\n\n- Regex rules catch *shapes*, not meaning. Novel phrasing evades them.\n  **Detection falls to roughly 71% under combined mutation**, and that is an\n  upper bound rather than a floor — the mutations are lexical and offline, so\n  an attacker with a language model does better.\n- The control that drives infection to zero is **sandbox isolation, and it\n  lives in your agent framework, not here**.\n- `watch` reads transcripts after the fact. It tells you an injection attempt\n  reached your agent; it does not block it.\n- Nothing here removes an infection from a running agent, and nothing here\n  contains an agent running as root. `wormhole` cleans files.\n- **Memo worms are not happening yet** — we scanned 40,000 Solana mainnet\n  signatures, extracted 1,064 memos, and found **zero** injection findings. A\n  null result is worth publishing because it makes a first occurrence visible.\n- In the payment guard, `abstain` is not an all-clear. It means the guard could\n  not evaluate the input.\n- Miasma is confirmed in the wild, and it spread through package installs while\n  persisting via agent config. **Fully autonomous** self-replication — a payload\n  rewriting itself into peers' configs with no package manager involved — is\n  still demonstrated in a lab, not observed. We will not blur those two.\n\nThere is no warranty, and this sends nothing anywhere: no account, no API\ntoken, no network call at any point. Both in full, along with what the tool\ndoes not cover: **[docs/limits.md](docs/limits.md)**.\n\n## Documentation\n\n| | |\n|---|---|\n| [docs/threat-model.md](docs/threat-model.md) | Miasma, the vendor gap, AgentWorm, how a payload travels |\n| [docs/rules.md](docs/rules.md) | Rule catalogue, regression suite, mutation decay, suppression |\n| [docs/hooks.md](docs/hooks.md) | `guard` / `readguard` / `outbound` in depth, and `harden` |\n| [docs/scanning.md](docs/scanning.md) | `scan`, `memos`, `handoffs`, `corpus`, capture, CI, SARIF, MCP |\n| [docs/x402.md](docs/x402.md) | The payment guard: Solana, EVM, quote text |\n| [docs/limits.md](docs/limits.md) | No warranty, no telemetry, limits, supported formats |\n\nRunnable examples, each with its actual output pasted in:\n[claude-code-hooks](examples/claude-code-hooks/) ·\n[x402-solana](examples/x402-solana/) ·\n[x402-evm](examples/x402-evm/) ·\n[quote-scanning](examples/quote-scanning/) ·\n[ci-github-action](examples/ci-github-action/) ·\n[agent-fleet](examples/agent-fleet/) ·\n[index](docs/README.md)\n\n## Contributing\n\nEvery new detection rule ships with a benign twin — a file discussing the same\nattack without being one. If the rule fires on the twin, it does not ship. See\n[CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).\n\n## License\n\nApache 2.0.\n",
  "bytes": 11408,
  "sha": "7d1f5865dccfb84641bd8f8a21fd7ebc398bf5193fb61d9dcac4838b2f5c8686",
  "repo_slug": "runningoffcode/agent-wormhole",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_runningoffcode_wormhole_x402_c723a132/readme"
}