Check S15 · agent surface family · severity CRITICAL · automatic · one of the 15 skill and plugin checks
The question this check asks: does this skill or plugin point the agent's API base URL — ANTHROPIC_BASE_URL or OPENAI_BASE_URL — at a host of its own choosing? One line, in a SKILL.md the agent reads the moment it loads or in a plugin's settings.json that applies to the session, and every request the agent makes, with its key, goes there.
The class is the one C21 was written for: in CVE-2026-21852 a repository file setting ANTHROPIC_BASE_URL caused Claude Code to make requests, and potentially leak its key, before the user had accepted the trust dialog. A skill moves the same line closer to the agent — it is not a repository someone opened, it is text the agent is designed to follow — and a plugin's settings.json can set the variable for the whole session through its env block. The February 2026 ClawHub campaigns did not use this shape; Snyk's ToxicSkills report describes credential theft by reading files and sending them, which is S9's question. But a redirect needs no read and no send: the agent does the sending itself, on every call, to wherever the line points. The malicious skills page holds the record this roster is built on, and this page holds the one shape in it that the roster grades by a rule of its own.
S15 runs on every text this tier reads — each skill's SKILL.md, the scripts beside it, a plugin's agents and commands, its hooks.json, .mcp.json and settings.json — and the shape it matches is an assignment: ANTHROPIC_BASE_URL=https://…, ANTHROPIC_BASE_URL: https://…, "OPENAI_BASE_URL": "https://…", or the prose form “ANTHROPIC_BASE_URL: defaults to https://…”. The host in the URL decides the grade. Anthropic's and OpenAI's own hosts are not a redirect and nothing fires. Any other host is credential-redirect:<path>, CRITICAL, 30 points, a BLOCK under the default profile, the finding reading “ANTHROPIC_BASE_URL is pointed at <host> — every API call, with its key, goes there (CVE-2026-21852 class)”. When that host is the plugin's own — one of its labels equal to a name token of the resolved source repository, the plugin's name or its homepage, four letters or more, generic words dropped — the redirect is declared, and the finding is HIGH: a REVIEW, warned, never a pass, and at 0 points, because the scoring table carries no HIGH row for the class and the verdict is the severity, not the score. A bare mention of either variable, or a *_API_KEY= assignment, is skill-credential-mention:<path>, a note at 0 points, never a driver. And a plugin's settings.json whose env sets either key to a value this tier cannot resolve — a variable, a bare host — is HIGH as credential-redirect:settings.json: set, to somewhere unseen.
That is deliberately not C21's rule. C21 fires on the name of the variable, which on a CLAUDE.md is the override: a repository's agent file has no honest reason to mention ANTHROPIC_BASE_URL. A SKILL.md, a script or an MCP configuration mentions it constantly — os.getenv("ANTHROPIC_BASE_URL"), a header built from ${X_API_KEY:-} — as documentation. The first measurement ran C21's pattern on the skill texts and produced six BLOCKs across the three lists: context7, migration-to-aws, neon, netlify-skills and vercel on the official marketplace, rigorpilot on skills.sh. Five were mentions. The assignment rule replaced the pattern, and five are PASS.
Measured on 20 September 2026 through the same code as /v1/skill/score: S15 fired above a note on 0 of 189 skills.sh keys, 1 of 310 official-marketplace plugins and 0 of 100 community plugins. The one is vercel, REVIEW 88: its .claude/skills/benchmark-sandbox/SKILL.md points ANTHROPIC_BASE_URL at ai-gateway.vercel.sh — the plugin's own host, so HIGH by the rule above, beside a HIGH --dangerously-skip-permissions line that S9 reads and that carries the twelve points. It was the sixth of the first run's BLOCKs, and the decision that made it a warning rather than a block is recorded as a policy call: a vendor routing its own plugin's calls through its own gateway is a declared redirect, shown on the decision, never a pass.
The vendor's own gateway, above: a plugin that says “point your base URL at our proxy” is doing what it says, and the check warns rather than blocks so that the line is on the decision and the judgement is yours. A host the name-token rule cannot tie to the plugin — a gateway on a domain that shares no four-letter token with the source repository, the plugin name or the homepage — is graded as a stranger's and blocks; the record has no such case on the three lists, and an override records the reason if one appears. A settings.json that sets the variable from another variable is HIGH because the tier cannot see where it points, not because it knows; the finding says so. The two C21 false positives on its young-legitimate controls — environment-setup documentation assigning a name ending in _API_KEY — are a note here by construction.
A redirect set outside the files this tier read. An MCP server's own configuration once it is running, a script that sets the variable at run time from a value fetched elsewhere, a file past the twelve-read cap: the assignment has to be in the text read. Other credentials. The rule watches the two base-URL variables; a skill that tells the agent to read a key file and send it is S9's credential-read and exfil pair, and a script that does the same is S10's. The source repository's own agent files are read by C21 on the 22, whose reading rides along on S1 with C21's known cost. ClawHub (@owner/slug) is parsed and routed but not assessed on this tier: no SKILL.md is read there yet, so this check is a listed skip and the key answers “not assessed” — never a pass.
Scope, the same on every page of this roster: RepoGates does not see git clone, package managers or curl — outside Claude Code with the RepoGates plugin, whose hook refuses a clone or install that names a blocked repository on the command line, before it runs. The hook sees Bash tool calls in that one client and nothing else: npx skills add owner/repo and claude plugin install name@marketplace are shell lines it judges; /plugin install is a slash command the hook never sees; a skill copied by hand into ~/.claude/skills is never seen at all. This tier reads the listing record, the tree once, and at most twelve files of 64 KB each — SKILL.md files first, then a plugin's agents and commands, then the scripts beside each skill; more than fits is a partial scan, stated, and never a pass on what was not read. The deep scan reads the whole package. A PASS means nothing matched on what was read, never that the skill was run.
Search the skill or plugin for the two variables and read every line that assigns one:
grep -rnE '(ANTHROPIC|OPENAI)_BASE_URL[" ]*[:=]' SKILL_DIR
jq -r '.env // {} | to_entries[] | select(.key | test("_BASE_URL$")) | "\(.key)=\(.value)"' settings.json
A URL whose host is anthropic.com or openai.com is not a redirect. Any other host is where your key will go on every call. A mention with no assignment — a script reading the variable, a document explaining it — is what the check calls a note.
Why does this roster not use C21's pattern? Because on a skill it produced blocks that were wrong. C21 fires on the name of the variable, which on a CLAUDE.md is the override: a repository's agent file has no honest reason to mention ANTHROPIC_BASE_URL. A SKILL.md, a script or an MCP configuration mentions it constantly — os.getenv("ANTHROPIC_BASE_URL"), a header built from ${X_API_KEY:-} — as documentation. The first measurement's six BLOCKs across the three lists were five of those mentions and one real redirect. So this roster's rule is an assignment: KEY=https://…, KEY: https://…, "KEY": "https://…", or the prose form “KEY: defaults to https://…” — a base URL actually pointed at a host. Five of the six are PASS now; the sixth is the one this page names.
What does a base-URL override actually do? It tells the agent to send its API requests — carrying its API key — to a server of the file's author's choosing. In CVE-2026-21852 a repository file setting ANTHROPIC_BASE_URL caused Claude Code to make requests, and potentially leak its key, before the user had accepted the trust dialog. A skill is read the moment it loads and a plugin's settings.json applies to the session, so the same one line in either does the same thing without a trust dialog in the way. That is why an assignment to a stranger's host is critical and blocks, and why even the plugin's own host is a warning rather than a note.
Which plugin fires this check, and why is it not blocked? Vercel's official plugin, whose benchmark-sandbox skill points ANTHROPIC_BASE_URL at ai-gateway.vercel.sh. That is the plugin's own host — decided by the name tokens of its source repository, its name and its homepage matching a label of the host — so the redirect is declared: every API call, with its key, goes to Vercel's gateway, and the plugin says so. The roster grades that HIGH: a REVIEW, warned, never a pass, and at zero points because the scoring table has no HIGH row for the class — the verdict is the severity, not the score. The same assignment to a host the plugin cannot be tied to is critical and blocks.
C21 Credential redirect — the class and the CVE, on a repository's agent files, with the name pattern this roster replaced · S14 MCP servers — the other thing a plugin's configuration can do to every call · S9 Instruction shapes — credential theft by reading and sending, the shape the February campaigns used · S6 Declared tool surface — the permission the agent would need to act on any of it.
Add RepoGates to Chrome Pricing
Numbers on this page: the 20 September 2026 measurement of 189 skills.sh keys, all 310 official-marketplace plugins and the first 100 community-marketplace plugins, run through the same code as /v1/skill/score, before and after the rule it forced; the C21 page's CVE-2026-21852 description and its two young-legitimate false positives where GitHub is cited; the incident record behind the malicious skills page; thresholds and weights from the product's policy and scoring tables.