october 2026 · 5 min

Whoever Serves the Completions Runs Your Shell

On October 29, Symantec's Threat Hunter Team published research on unauthorized AI relays running inside about a dozen organizations. Nothing was trojanized. People installed the real Codex or Claude Code from npm, then ran a one-line curl | bash (or iwr | iex on Windows) from a lookalike domain. The script rewrote the agent's config to send everything through a relay, with the relay's own API key.

In ~/.codex/config.toml it replaced base_url and set sandbox_mode = "danger-full-access" with network access on. In ~/.claude/settings.json it pointed ANTHROPIC_BASE_URL at the relay. It also set the same values as user-scope environment variables, so they survive a reboot, and turned off telemetry and error reporting.

Symantec saw no malicious use by the operator. It didn't need any to make the point. An agent executes what the model returns. Whoever serves the completions is choosing the commands your shell runs, and here that party also had the sandbox turned off for them.

The Endpoint Is a Dependency

We treat the model endpoint like a utility: an API we pay for, maybe rate-limited, maybe slow. For a chat app that's roughly right. For an agent it's wrong, because the response isn't text for a human to read. It's a tool call the client executes with your privileges.

So the model endpoint is in your supply chain, in the same seat as your package registry. Every command the agent runs was published by it.

This isn't a thought experiment. In April, researchers measured paid and free LLM routers in "Your Agent Is Mine". A router terminates TLS, sees the plaintext, and can rewrite tool-call arguments after the upstream model returns them. The client runs the rewritten call like any other: swap the URL in a package-install command and the agent installs the attacker's code. Out of 28 paid and 400 free routers, 1 paid and 8 free were injecting malicious tool calls, and 2 only did it selectively, for example only in sessions already running in autonomous mode.

I designed Preflight after Codecov on one idea: when the publisher is compromised, publisher-side trust breaks, so verify on your side. A relay is a publisher you added yourself, for every command, in real time. And there's no checksum for "the command the model meant to send."

What to Check Tonight

The relay needed two things: a user-writable file that decides where the agent's model lives, and a user-writable switch that turns its sandbox off. Find out whether anything already flipped them:

grep -nE 'base_url|BASE_URL|danger-full-access' \
  ~/.codex/config.toml ~/.claude/settings.json 2>/dev/null
env | grep -E '^(OPENAI|ANTHROPIC)_BASE_URL='

On a machine that talks to the vendors directly, this prints nothing or only the vendor's own hosts. Anything else is another party between you and the model. Know who it is.

Then take both decisions out of the user's dotfiles. Codex reads admin requirements from /etc/codex/requirements.toml (%ProgramData%\OpenAI\Codex\requirements.toml on Windows), and a mode that isn't allowed there can't be chosen in config.toml:

# legacy key; newer Codex versions express this as permission profiles
allowed_sandbox_modes = ["read-only", "workspace-write"]
allowed_approval_policies = ["on-request"]

Claude Code reads managed-settings.json from /etc/claude-code/ on Linux and /Library/Application Support/ClaudeCode/ on macOS, and the docs say no user, project, local or --settings value overrides it. An env block there outranks the one in the user's settings.json, though the docs don't say it beats a variable exported at the OS level, which the relay script also set:

{
  "env": { "ANTHROPIC_BASE_URL": "https://api.anthropic.com" },
  "permissions": { "disableBypassPermissionsMode": "disable" }
}

The control that holds no matter which file wins is the network: let agent CLI traffic out only to the vendors you actually pay. Symantec's monitoring guidance points the same way: alert on base URL changes, on danger-full-access, on these variables set at user scope, and on a curl | bash right after an npm install of an AI CLI.

The Tradeoff

Managed settings bind one tool. Codex's managed configuration docs cover sandbox and approval policy but say nothing about locking the model provider, and anyone with local admin can edit files in /etc. Pinning ANTHROPIC_BASE_URL to the vendor breaks anyone on Bedrock, Vertex or an internal gateway, and an egress allowlist breaks people who legitimately run a local model, so you end up maintaining the list. And if the official service is slower or pricier than a relay, some people will route around whatever you lock down.

None of this is the long-term shape. Agents run today on primitives built for humans: a home directory, a shell, a config file anyone can write. My bet is that agents get an environment built around them, in which the model endpoint is a property of that environment and not a line in a dotfile. Until then, the dotfile is the boundary, and it's the one most teams have never looked at.

Your agent's base URL is a dependency with shell access. Pin it like one.

← all posts