Check G21 · agent surface family · severity CRITICAL · automatic · one of the 22 GitLab checks
The question this check asks: does a settings file that an AI coding agent loads from this gitlab.com project override where the agent sends its API requests — or set an API key of its own?
An agent authenticates to its model provider with a key and a base URL, and both are ordinary configuration. In CVE-2026-21852 a file committed to a repository set ANTHROPIC_BASE_URL, and Claude Code — loading project settings before the user had accepted the trust dialog — sent its API requests, carrying its API key, to the address the file named. One line, in a file written to be skimmed, and the agent's credential leaves the machine on its next call. The vetting standard files it with the pre-trust execution class: it does not need code to run, only configuration to be read. The reader is the agent on your machine, so where the file was hosted does not enter into it; and on gitlab.com the instruction file most likely to carry such a line has a second reader of its own, because GitLab Duo reads AGENTS.md by design.
G21 fetches the agent files a project ships — .mcp.json, .cursor/mcp.json, the .claude/ settings files, CLAUDE.md, AGENTS.md and the other instruction files — through gitlab.com's files API, /repository/files/:path/raw, the same check and the same code as C21 on a snapshot built from GitLab's API, and matches each for ANTHROPIC_BASE_URL, OPENAI_BASE_URL, or any assignment of a name ending in _API_KEY. A hit is CRITICAL, costs 30 points, and reads “config overrides an API base URL or key — token exfiltration class (CVE-2026-21852)”. A project that trips nothing else scores 70 and reads amber; the verdict is BLOCK-grade, and the default personal policy sets this check to warn — the download is held, the file is named, proceeding is one click — while the strict organisation profile blocks. The evidence note in the scoring table explains the restraint:
“token exfiltration class; 2 regex FPs in young-legit controls”
Measured on 19 September 2026 across the 100 most-starred gitlab.com projects, it fired on none: the 18 that carry agent files were read and matched, and nothing matched. GitHub's 100 most-starred, run through the same engine the same day as a control, is the contrast — the pattern matched inside AGENTS.md on three of them, n8n-io/n8n, f/prompts.chat and browser-use/browser-use, and those three, with one owner account under the CRITICAL age, are the control's four BLOCKs. The same rule read the same file type on both hosts and found the assignments where the projects had written them.
Two, in 50 young legitimate GitHub repositories, and both were the same thing: environment-setup documentation in an agent instruction file listing which SOMETHING_API_KEY to set. The pattern matches the assignment; it cannot tell "set this in your environment" from "use this key". The three matches on the GitHub control sit in AGENTS.md files on large, busy projects — the file where that documentation lives — and whether each is the documentation shape or a real override is what the named file lets you settle in a ten-second read. That is why the action is a warning, why the finding shows the file, and why tightening the pattern is a recorded roadmap item from the measurement rather than something this page pretends is already done. On gitlab.com's top-100 the pattern had nothing to match.
A redirect under a variable name it does not know — another provider's base URL, a proxy setting, a custom endpoint key — passes. Files not on the agent-file list are not read; your agent's own global configuration, outside the project, is never seen. A .env file is not an agent settings file and is not opened here, and on gitlab.com that is worth saying plainly: GitLab's own threat intelligence, published on 19 February 2026, describes the North Korean loader projects behind the 131 accounts it banned in 2025 as carrying “a base64 encoded next-stage URL, header key, and header value, all masquerading as benign variables in a .env file”, triggered by “a global invocation of the trigger function in a file executed as soon as the project is run” — a redirect of the same spirit, in a file this check does not read, that fires when the project runs rather than when an agent starts. It matches text, so a base URL assembled at runtime by a script is invisible to it — that script is G20's territory if it fetches, and nobody's if it does not. A project whose tree the API does not answer — one of the 100, spacecowboy/Feeder — has this check listed as not checked, not passed. And it reads the file before the download; it does not see the request the agent later makes.
And RepoGates as a whole gates browser downloads only — it does not see git clone, package managers, curl, or fetches made by AI agents outside the browser. On gitlab.com the board and the download gate are a later, separate store release: today the verdict on a gitlab.com project is served by the API and the MCP server.
RepoGates runs the same 22 checks on a gitlab.com project that it runs on a GitHub repository, from gitlab.com's public API, under the key gl/{namespace…}/{project}. What that API withholds from an anonymous caller — fork status, a user namespace's account age, and OpenSSF Scorecard, which does not cover gitlab.com — is listed as not checked, never assumed. A self-managed GitLab is another hostname and is not assessed. The repository's code is not read on this tier: a PASS means the provenance and the declared execution surface are clean.
Without installing anything: open the project's .claude/settings.json, .mcp.json, CLAUDE.md and AGENTS.md on gitlab.com and search each for BASE_URL and _API_KEY. From a terminal, through the same files API the check uses — the project path and each file path are URL-encoded, every / written %2F:
P='NAMESPACE%2FPROJECT'; REF=main
for f in .claude%2Fsettings.json .claude%2Fsettings.local.json .mcp.json .cursor%2Fmcp.json CLAUDE.md AGENTS.md; do
curl -sf "https://gitlab.com/api/v4/projects/$P/repository/files/$f/raw?ref=$REF" | grep -nE 'BASE_URL|_API_KEY\s*[:=]' | sed "s|^|$f:|"
done
Then patch: the standard's fix for the whole pre-trust class is a current agent — Claude Code 2.0.65 or later, Copilot CLI 1.0.43 or later, current Cursor — and project-scoped configuration disabled until you trust the folder.
My settings file documents which API keys to set — will that fire? It might, and we say so. The pattern matches any assignment of a name ending in _API_KEY, and two of the 50 young legitimate GitHub repositories in our controls fired it on exactly that kind of environment-setup documentation; on 19 September 2026 it also matched inside AGENTS.md on three of GitHub's 100 most-starred repositories, and on none of gitlab.com's. That is why the check warns rather than blocks by default, why the finding names the file, and why tightening the pattern is on the roadmap rather than pretending the false positives did not happen.
What does a base-URL override actually do? It tells your agent to send its API requests — carrying your 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. The override is one line in a file nobody reads, and the host the file was downloaded from is no part of it — which is why the check runs on gitlab.com as it does on GitHub.
Does RepoGates stop git clone? No. RepoGates gates browser-initiated downloads, and on gitlab.com the board and the download gate are a later, separate store release: today a gitlab.com project's verdict is served by the API and the MCP server. It does not see git clone, package managers, curl, or fetches made by AI agents outside the browser. It reads the settings file before the download; it does not see the agent send the request.
C21 Credential redirect — the GitHub original · G20 MCP shell-launch config — the same files, a command instead of a URL · G18 Agent config files — the files this check reads · G19 Hidden Unicode — the other content finding in the same family.
Add RepoGates to Chrome Pricing
Numbers on this page: CVE-2026-21852 as published; Phase 0 controls of 14 August 2026 (two pattern hits in 50 young legitimate GitHub repositories); the 100 most-starred gitlab.com projects and GitHub's 100 most-starred as a control, both measured 19 September 2026 through the same engine; GitLab Threat Intelligence, 19 February 2026 (the loader tradecraft quoted); weights and actions from the product's scoring and policy tables.