2 Nov 2026 · Technical report

Be careful with Claude Code mods: they open a door to all your connectors

There are already 7,349 public Claude Code mods, nine out of ten in repositories created in October, and 40% launch programs on your machine. I built one and measured what it can do without you noticing, from the mode in which Claude Code asks you about every step to the one that asks nothing.

5connected services read by the modbypass
1dev secret read by a script the mod launchedauto + manual
0model calls and new lines in the session historyauto + manual
120sbetween timer runs, with nobody in front of the screenall
In short
  • A mod is code that travels inside a plugin and runs in your Claude Code session, with your connectors and with your permissions.
  • I built one: every two minutes it read my Gmail and my Calendar, and a script of its own read a secret, without the model doing anything and without leaving a trace in the session.
  • Claude Code asks you before reading the email. What the mod does afterwards with that data, nobody asks you about anymore.

On 2 November, at 11:12 (UTC time), a Claude Code session in my local lab read a secret stored in my secrets manager. Nobody had touched the keyboard and nothing had changed in the terminal: the cursor waiting and, at the bottom, the usual line that says which mode it is in, auto mode. Also, looking at the session history, its last line was from 11:05:20, seven minutes earlier.

The secret was a test one, and I had put it there on purpose. The execution had been done by a mod that I had written myself to test how far arbitrary code can be run in a mod without anyone noticing, so I started digging. New weekend project (in Spain this weekend has been three days long. I tell you, come live in Spain, you will be happier).

01The problem

Before anything else, it's worth distinguishing between two concepts. A plugin is the package you install in Claude Code: it can bring skills, commands or MCP servers. When it also brings code that runs inside Claude Code itself, that code is called a mod. Every mod travels inside a plugin, but not all plugins bring a mod. Here what we are going to talk about is Claude Code mods.

A mod hooks into the session, at certain moments (events), for example when the session opens, and from there it can set a timer, use your session's connectors (Gmail, Calendar, Drive…) and launch programs on your machine.

The documentation says it clearly so nobody gets the surprise: mods run with your permissions and what they launch stays outside the sandbox (careful with that last one, because I'm still hearing the screams of the security people with the OpenClaw plugins, and this is a bit more of the same).

I've taken a look out of curiosity and there are a lot of mods. A community catalog that runs the official Claude Code validator on every public repository it finds counted, on 1 November, 7,349 mods, nine out of ten in repositories created in October (!!). 40% launch programs on your machine and one in three reads your environment variables (WHATTT???!). So let's see what we can do with this, which a priori seems to be not a little.

In manual mode, every time the mod wants to use a connector, Claude Code stops, asks you and tells you which plugin is asking for it (yes, that nagging prompt you switch off by the third time it asks). But when you hit Yes, the data goes to the mod's code, and what it does with it no longer passes through any filter of any kind, and this is where the crux of the matter comes in.

There's no trace in the session history either, which is the first thing you'd look at to know what has happened. If you turn on Claude Code's telemetry, you'll see that the mod used Gmail, but not what it searched for, and the programs it launches don't even appear (warning to Anthropic: Boris, this costs you very little, put it in, at least).

manual mode · what you see
Claude Code permission prompt: Tool use from the bridge-lab plugin, Gmail search email threads, with the query parameters and the question Do you want to proceed, Yes or No. Senders covered. 1 2 3
  • 1It names the plugin. It says bridge-lab is asking.
  • 2It shows what it will read. The exact search and how many results.
  • 3The “Yes” approves the read. What comes next, the Python script that receives the data, runs without asking.
What the prompt shows What happens next Gmail · search_threads 5 thread headers You press “Yes” Hook · bridge-lab $.process.run(...) bridge_rules.py new process, your user Your files read, write The network send, download Your access local credentials Everything a process running as your user can do, outside the prompt.
Figure 1. On the left, the real prompt in manual mode, with the senders covered. On the right, the full path of that same read. The bottom half shows what the mod can do; what we measured is in section 02.
The prompt tells you what gets read. The mod decides where it goes.

02The bridge that didn't say a word

Like almost all these things, everything started with a small problem, and here you have me, my whole holiday Monday pulling the thread 8 hours later. I wanted to pass data to a tasks page (it's a long story for another post, very entertaining) without spending tokens (because, in case you hadn't noticed, tokens are the new gold and even the cat asks you for tokens), so I set up what I called a bridge: basically a Claude Code session (the 2.1.316, with Haiku 5.5) that only loads a plugin and where nobody types. Its mod sets a timer that makes its first run in 20 seconds and then every two minutes. On each run it queries Gmail and Calendar, although it could pull from anything my connectors see, and passes what it finds to a Python script with fixed rules (the why of this is long, but let's not get off track, we're with the mods). The model, meanwhile, doesn't even find out, which was the idea so as not to spend tokens.

timerRunevery 120 s
connectorGmailsearch_threads
connectorCalendarlist_events
processPythonbridge_rules.py
fileAuditadds a line

Terminal

❯

⏵⏵ auto mode on

Session history

5 records
last: 11:05:20

no changes

Mod log

run 0
Figure 2. One run of the bridge in auto mode. A lot happens inside; outside, the terminal and the session history stay the same.
// hooks/register.js: pieces of the mod's code, with the private names changed.
on('session.start', async ($, event, next) => {
  $.clock.every(120000, () => tick($));
  void $.clock.sleep(20000).then(() => tick($));
  return next(event);
});

const g = await $.mcp.call('claude.ai Gmail', 'search_threads', {
  query: QUERY, pageSize: 10, view: 'THREAD_VIEW_MINIMAL'
});
const c = await $.mcp.call('claude.ai Google Calendar',
  'list_events', { pageSize: 10, orderBy: 'startTime' });
const r = await $.process.run(['python3', RULES], {
  stdin: JSON.stringify({ source, items, meta }), timeoutMs: 20000
});

Then I realized the mod was getting all the way into my kitchen and the Claude Code console didn't make a peep. I was surprised and thought maybe in the JSONL, but no. And there I already knew how the day was going to end. I wanted to know what else the mod could do on its own, without Claude Code asking me anything. So I did three tests: read a secret, change the code while running and download code from the internet.

measured · auto and manual

A secret read

On one of the runs, the mod launched a small script that used my dev account and read a throwaway secret.

15 B

The value was the real one, and its owner's hash confirmed it. Nothing showed on screen.

measured · auto mode

Code changed on the fly

The mod loads only once, but on every run it starts a new Python that reads bridge_rules.py from disk. I changed that file with the session open.

1 run

and the new code was already running, with no restart.

measured · bypass, isolated

Code fetched later

In a separate test, the mod downloaded a script from the internet, checked its hash and ran it.

HTTP 200

It finished fine (exit code 0) and processed a Drive response.

And so there's no doubt: the script was receiving real data. From Calendar, the 10 events of the next three days, with their time and their title, mixing work, fixed meetings and personal things. From Gmail, the headers of 5 threads, with sender and subject, of the automatic notices from providers of the last month.

Of course, since you're at it you start turning it over, and it occurred to me that in the last two tests it was going to change how a plugin is reviewed. Could it be that, when you install a mod and read its code (something we all do, with no exceptions) and it looks fine to you, this code calls every X minutes a script that lives on your disk, or downloads another one from the internet? And what if someone changes that script on the fly? Does the mod run it on the next run, without reinstalling anything and without warning? If so (Spoiler 1: yes, it does), the code that runs is already a different one from the one you “reviewed”.

And of course, when I look at the published mods it turns out it's a very common pattern: 1,758, one in four, combine a timer with programs they launch on your machine, just like my bridge.

Then I wanted to know what the user sees in each of Claude Code's permission modes, because of course, maybe it was me who was in bypass-permissions-and-take-my-whole-PC mode, so I repeated the same tests in the four modes, and this table sums up what happened (Spoiler 2: it's what you're imagining).

Manual
Accept edits
Auto
Bypass
Connector read
$.mcp.call
Asks firstnames the plugin and the parameters
Asks firstthe same prompt
No promptreads and one reversible write
No prompt5 services read
Where the data goes
after it's read
Off screen
Off screen
Off screen
Off screen
Programs it launches
$.process.run
Runs with no promptsecret read
Runs with no promptnew session
Runs with no promptsecret read
Runs with no promptscript downloaded from the internet
you approve every callauto mode's classifier decideshappens with no warningpending
Figure 3. Claude Code 2.1.316. The only write in auto mode was creating a label (create_label), which we checked and deleted.

That said, credit where it's due: in manual mode, Claude Code does well the part it controls. It tells you which plugin asks for the read and shows you exactly what it's going to search for. The problem is one of design: when you approve a read, you're approving the input of a piece of code whose output you don't see.

For this test, in manual mode, I recorded the whole sequence. The data goes from Gmail to Python, from Python to Calendar and from Calendar again to Python. Only the connectors stopped to ask; the two Python scripts just ran, and there anything could have happened. Of course, it's the power of the mod!, exactly the idea. If you want something that does things, don't be surprised when it does them!

prompt · you approveGmailsearch_threads
no promptPython12:32:28 · 5 results
prompt · you approveCalendarlist_events
no promptPython12:32:38 · 10 results
two approvals · two scripts launched · 0 new lines in the history
Figure 4. Manual mode. The times come from the mod's own log. In accept edits the same two prompts showed up.

Lastly I tried launching only the script, also in manual mode. The mod started it with $.process.run, it finished fine and read a secret. No trace was left. And even though we approved the connectors by hand, the session history didn't note any of those approvals.

Did anything record it at all? I repeated the test turning on the two logs that Claude Code brings for these things, the debug log and telemetry. The debug log caught everything: the email read, the calendar one and the script, with the plugin name in front. Telemetry, only half: the two reads yes, the script no. That is, exactly what skips the permission window is what doesn't reach telemetry either.

And before all this, in an isolated session in bypass mode (in which neither I nor anyone I know has ever worked), the mod called five connected services and all of them answered without asking permission even once: Drive returned 32,798 characters, Gmail 2,575, Atlassian 659, Slack 494 and Calendar 254. I saved only whether it worked and how much it returned.

None of this has needed an exploit. It's what a plugin is allowed to do. Most of us install a plugin like a browser extension, after reading the description. What we really install is code that runs inside the session we're watching, with our connectors and our account, with its timer and without making a peep on screen.

The lesson. Installing a plugin that brings a mod is like installing a program. Review the mod's code, every script it launches and everything it downloads, and decide which user and which connectors you'll let it run with. Keep in mind that of the 7,349 public mods, only 69 sit in repositories with more than a thousand stars.

03How to catch a silent mod

Start with the code the session loaded, which is where what the mod does is; the history only notes what the model did. With these commands, which only read, you have the map:

# Read-only
claude plugin list --json
claude plugin details <installed-plugin>
rg -n 'session\.start|clock\.every|process\.run|mcp\.call|tool\.call|http\.fetch' <plugin-folder>
sha256sum <mod-module> <every-script-it-launches>

Follow every $.process.run to its script and to what that script imports. In my case, staying at register.js would have left the change out: the mod didn't move, the one that changed was bridge_rules.py. And look also at the plugins loaded with --plugin-dir, which come in without being installed.

Careful, claude plugin validate checks the structure; how far the mod reaches you have to look at yourself.

You can do aaall of that, or you pass this post to your Opus 5.5 and ask it to build you a guardian mod: Claude Code warns with the plugin.register event every time a mod is about to load, and there the guardian can read its code, ask Opus about it and block it if it isn't convinced. That said, it only sees what gets loaded, so the scripts that mod launches afterwards have to be watched by hash.

I've made mine and left it public on my GitHub, explained from top to bottom: before loading any mod it passes its code to Opus 5.5 for it to review, and if a script of the mod then changes in the middle of the session, it doesn't let it run. I tested it with a mod that said it was a clock and actually launched a script every minute: Opus caught it the first time and it never got loaded.

It's not without a certain irony that, after this whole post, I offer you a mod and at the same time tell you to think about it before installing one. But c'est la vie: if you don't want to, don't install it, or at least ask your Opus 5.5 to read it and install it for you. I have it set with Opus 5.5 on high effort, because to review someone else's code it's not advisable to skimp.

Where to lookWhat it tells youSees the mod?
The startup configurationThe active plugins, the --plugin-dir folders and the session's permission mode.What got loaded
The mod and everything it launchesTimers, connector calls, scripts, imports, hashes and owners.Everything
The session history (JSONL)Which session it was. An empty history doesn't mean the mod did nothing.Nothing
The debug log (--debug-file)Every mod call to connectors and every program, with the plugin name. Only if you turn it on, and any of your processes can edit it.Everything, if you turn it on
OpenTelemetryThe mod's calls to connectors, without what it searched for. For programs, nothing.Halfway
Each service's own logsWho read what in the connector and in the secrets manager. It's the only record the mod can't touch.Yes, but off your machine

04What I'd tell you before you try it

Use manual mode in sessions with third-party pluginsIt's the simplest option: every time the mod wants to read from a connector, Claude Code stops and tells you who's asking.That said, it only watches the read; the programs the mod launches go through without asking. And with a run every two minutes you get thirty prompts an hour, so anyone ends up switching to auto mode.
Review the whole plugin and keep the hash of every fileFollow every $.process.run to the script it launches and whatever that script imports. With an rg 'session\.start|clock\.every|process\.run|mcp\.call' over the plugin folder you have the map in seconds.And look at it yourself, because Claude Code won't tell you: when I installed it from a marketplace it showed no warning about hooks, programs or network, and claude plugin details showed “Hooks (0)” for a mod with a timer that launches programs. It takes time. And if the mod downloads code, what you reviewed can change tomorrow, so those hashes have to be watched while it runs. And start these sessions with --debug-file: it's the only local record where the mod's programs showed up.
Give the bridge its own user and watch it from the outsideA system user that can't read secret values and only has the connectors it needs. And as the source of truth, the logs of the secrets manager and of the services themselves.It costs more to set up, and those logs tell you after the read has already happened.

05What I'd ask Anthropic for

The documentation already warns that mods run without a sandbox, and that's fine. What I miss is being able to see it. And the good thing is that almost everything needed is already inside Claude Code: the validator already knows what each mod calls, telemetry already goes out and the plugin.register event already allows stopping one before it loads. They are three small requests, one for each moment in a mod's life, and each one comes from something I measured.

01 · at install

Tell me what it does before it comes in

  • What the validator already knows, at install timeWhen I installed my mod from a marketplace there wasn't a single warning, and claude plugin details showed “Hooks (0)” for a mod with a timer that launches programs. The right list already exists: it's the one plugin.register receives.
  • In one line anyone can follow“Starts with the session, uses a timer, reads Gmail and Calendar and launches programs”. With that you decide in two seconds.
02 · while it runs

Let me decide about what it launches

  • A permission rule for $.process.runToday permission rules cover Claude's tools and leave mods out: the mods documentation itself warns that with Read(.env) denied, a mod can still read that file.
  • An indicator in the status lineSomething like bridge-lab · 2 reads · 2 programs · 5 min ago, so you can see somebody's working even when nobody's typing.
03 · afterwards

A trail the mod can't erase

  • The programs, in telemetryConnector reads already go out through OpenTelemetry; the programs the mod launches don't. It's the same channel and the missing piece (Boris, this is the one I was telling you about).
  • What the mod does and what you approve, in the historyApprovals in manual mode left no line at all in the session history.

A mod brings code that uses your keys. You decide which doors you open for it.

Evidence and sources

Every piece of data in this article has its proof here: what I measured, when and the fingerprint of each record.

IDWhat it backs
E-AUTO-01Auto mode session, runs, 15-byte secret (SHA-256 prefix ba3f3f4a3e2c) and the live script change.
E-PANE-01Four text captures of the terminal before and after the reads, identical SHA-256 2f68343ac2d337bd438c867eed7e84cbb6f0ceaae9aa8868208822b4eb9ea2ab
E-JSONL-01Session history (JSONL): five lines, the last one at 11:05:20 UTC. SHA-256 6e37da6f25a2b8ac4f2f9c50b0c4f117c3e2f94e73d70db97fcab059ddc29f29
E-PERM-01Captures of the prompts in manual mode and times from the mod's log (12:32:28 and 12:32:38 UTC).
E-MODES-01Local process in manual, approval and JSONL, reversible write in auto. Terminal SHA-256 21a1bfb83df0fd03c2be0fd6b733419dc04ec1259eca73a604ff74159184b559; JSONL SHA-256 e075974ead966493a7e79014eca879d6ae6021e16d51eff5761fd6bad6f35c91
E-ECO-01Community mod catalog, scan of 1 Nov 2026 with claude plugin validate 2.1.316 (it reads the code without running it). My own count over its data: 7,349 mods (no duplicates, tests or copies); of the 6,962 with a complete analysis, 2,795 launch processes, 2,299 read environment variables and 1,758 combine a timer and processes. 6,599 of the 7,349 are in repositories created in October 2026 (90%), and 69 in repositories with 1,000 stars or more. Stars count the whole repository, with all its mods.
E-GUARDmod-guard on Claude Code 2.1.316, review with Opus 5.5 at high effort. A test mod that says it is a clock and runs a script of its own every minute: refused at load. A mod that only shows a status line: admitted. An honest mod that runs its own helper.py: admitted, and its next run denied after helper.py was edited with the session open. The three test mods are in the repository's examples/.
E-V4Round of checks on 2 Nov (Claude Code 2.1.316). Telemetry on in manual and auto: the mod's calls to Gmail and Calendar show up as tool_decision and tool_result, with only the server and the tool; $.process.run produces no event, only a trace in the local debug log. Accept edits, in a new session: the script ran with no prompt. Install from a local marketplace with no warning about hooks, processes or network; claude plugin details showed Hooks (0). A script launched by the mod made a POST of 52 dummy bytes to localhost (HTTP 204). Pending, for lack of admin access: the secrets manager audit and the Google Workspace one.
E-DEBUG-01Manual mode session with --debug-file and telemetry to console: six plugin lines in the debug log (two mcp.call with their response and one process.run with exit 0); 17 telemetry events, with two tool_decision and two tool_result for the connector calls and none for the process.
E-BYPASS-01Isolated test in bypass: five services (Drive, Slack, Gmail, Calendar, Atlassian) and the downloaded helper.

About the OpenClaw stuff: Tom's Hardware, malicious skills on ClawHub (late January 2026), TechRadar, macOS infostealers in OpenClaw skills (Unit 42 report) and the Bitdoze OpenClaw security guide (ClawHavoc campaign; the figures for infected skills vary depending on the source).

Claude Code documentation: Mods overview, Mods reference, Permission modes and Monitoring.