Check G17 · execution surface family · severity MEDIUM · automatic · one of the 22 GitLab checks
The question this check asks: does this gitlab.com project carry an .envrc — a shell script that direnv will source, as you, every time you enter the directory? It is C17, the GitHub check, run unchanged on a project hosted at gitlab.com.
direnv is a convenience that many developers install once and never think about again: enter a project directory, and its .envrc sets up the environment. The file is bash. It can export a variable, and it can do anything else bash can do, and it runs on every cd for as long as the directory exists — a directory cloned from gitlab.com is no different from one cloned from anywhere else once it is on your disk. direnv's own defence is that a new or changed .envrc is blocked until you type direnv allow — which is a real step, and also the step developers who use direnv perform by reflex, the way the rest of us click Trust. The vetting standard's summary is three words: arbitrary bash on cd.
G17 is C17 on a gitlab.com project: the same code, run on a snapshot RepoGates builds from gitlab.com's public API. The file list is one call — the recursive repository tree at per_page=10000, no clone; inkscape/inkscape's 7,182 entries arrive in 0.36 s. A full page is reported as truncated rather than read as complete, and a project with more than one page — twelve of the 100 most-starred, gitlab-org/gitlab among them at 136,997 entries — is a partial scan: the tree checks ran on the first page only, the verdict is cached briefly and retried, and it is never a clean PASS. On that list the check fires when .envrc sits at the root. The finding is MEDIUM, costs 5 points, and reads “arbitrary bash on cd (direnv)”. A project that trips nothing else scores 95, the badge stays green, the verdict is REVIEW, and the default personal policy warns. It never blocks on its own; the direnv allow step is the reason it sits at the light end of the execution-surface family, whose evidence note in the scoring table reads:
“28.8% of top-100 repos carry devcontainers — informative, not damning”
That note is GitHub's Phase 0 measurement. On 19 September 2026 the same code ran, anonymously, over the 100 most-starred gitlab.com projects: a root .envrc on exactly one of them — the rarest of the auto-execution rows in that run, beside 16 submodule definitions and 13 wrapper JARs.
Where the verdict reaches you today: the API (GET /v1/gl/score/{path}), the MCP server (check_repo with platform="gl"), and the Claude Code plugin's preflight (a hook on Claude Code's Bash tool: it reads the command line, never a process), which turns a warning into an ask before a git clone https://gitlab.com/… line runs. The extension's board on gitlab.com pages is a later release.
Every project whose maintainers use direnv — common in Nix, Go and infrastructure repositories, where the file usually says use flake or exports a path and nothing else. The check cannot tell that file from one that fetches a script, so it warns at five points and names the file, and reading it takes ten seconds. One of gitlab.com's hundred most-starred projects carries one.
It sees the file, not its lines; a deep scan reads them. It reads .envrc only — a .env file is data, not code, and is not flagged, even though the one loader pattern GitLab's own threat-intelligence team has documented on gitlab.com kept its next-stage URL and request headers there, base64-encoded and disguised as ordinary variables, for code elsewhere in the project to read. Shell configuration files a project might ask you to source by hand are not on the list. It does not see whether you have direnv installed, and a machine without it is not exposed. And the finding is on the verdict before the project reaches your machine; the script runs when you enter the directory and allow it.
And RepoGates as a whole is a browser download gate and an answer an agent can ask for: it 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.
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 file on gitlab.com — https://gitlab.com/NAMESPACE/PROJECT/-/raw/BRANCH/.envrc — and read it; it is usually a few lines. With curl against gitlab.com's API, the project path URL-encoded so / becomes %2F:
curl -s 'https://gitlab.com/api/v4/projects/NAMESPACE%2FPROJECT/repository/files/.envrc/raw?ref=BRANCH'
Then keep the habit that makes this check unnecessary: never direnv allow a file you have not read, and re-read it whenever direnv says it changed.
Does direnv not ask before running an .envrc? It does — a new or changed .envrc is blocked until you run direnv allow. That is exactly why the finding is 5 points and a warning rather than more: there is a step between the file and your shell. The risk is the reflex. Developers who use direnv type direnv allow the way they click Trust, and the file it allows is arbitrary shell that will run on every cd from then on — the same on a directory cloned from gitlab.com as on any other.
Is an .envrc the same as a .env file? No. A .env file is data — key=value pairs a library reads. An .envrc is a shell script that direnv sources; it can export variables, and it can run anything else. The check reads only .envrc, because only .envrc executes. That cuts both ways: the one loader pattern GitLab's own threat-intelligence team has documented on gitlab.com hid its next-stage URL and request headers as ordinary-looking variables in a .env file, and the code that read them lived elsewhere in the project. A .env file is not flagged, and would not have been the file to flag.
Does RepoGates stop git clone of a gitlab.com project? In the browser, no: an extension cannot see a process outside it, and 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. This finding warns rather than blocks, so a clone of a project that carries it becomes an ask there, not a refusal. A browser gate for gitlab.com's own download links is a later release; the script runs when you enter the directory and allow it.
C17 Directory-entry hooks — the GitHub original · G10 Devcontainer and Workspace devfile auto-execution — the same idea with no allow step, and GitLab's own Workspaces devfile beside it · G16 Install scripts — code that runs at install rather than on entry · G11 Nested bare repository — the execution-surface finding that does block; the two BLOCKs in gitlab.com's top 100 were this one.
Add RepoGates to Chrome Pricing
Numbers on this page: the 100 most-starred gitlab.com projects, measured 19 September 2026 through the same code as the verdict API; GitLab's threat-intelligence post of 19 February 2026; weights and actions from the product's scoring and policy tables; direnv's allow model from its documentation.