Committed binaries

Check G15 · execution surface family · severity HIGH · automatic · one of the 22 GitLab checks

The question this check asks: are there executables committed inside this gitlab.com project's source tree — files nobody can read as code and nobody can rebuild from what is there? It is C15, the GitHub check, run unchanged on a project hosted at gitlab.com.

Why it matters

The OpenSSF Scorecard's Binary-Artifacts check exists for one reason, and the xz backdoor of 2024 (CVE-2024-3094) made it vivid: a released source tarball should contain only what is in version control, and anything you cannot rebuild from source is a place to hide a payload. The xz payload sat in test fixtures — opaque binary files that every reviewer scrolled past for years, because what is there to review in a fixture? A .exe, .dll, .so or .jar in a tree is the same proposition whichever host serves the tree: it will do what it does, and the source beside it does not tell you what. On gitlab.com there is one more reason to ask the question yourself — Scorecard's project list is GitHub-only and its API answers 404 for a gitlab.com project, so the second opinion that exists on GitHub does not exist here.

How RepoGates scores it

G15 is C15 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 any path ends in .exe, .dll, .so, .dylib, .jar, .pyc, .class, .bin, .msi, .scr, .apk or .node. The finding is HIGH, costs 8 points, and names up to four of the files — “executables committed to source: vendor/x.dll, tools/y.exe”. A project that trips nothing else scores 92, the badge stays green, the verdict is REVIEW — and the default personal policy sets this finding to allow: it is on the verdict, and on its own it holds nothing. The strict organisation profile blocks. The evidence note in the scoring table:

“27/80 top repos carry binaries”

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 and found committed binaries on 26 — and on 20 of GitHub's 100 most-starred the same day. CalcProgrammer1/OpenRGB and fdroid/fdroidclient are two of the 26; on four projects this was the only finding that stood between them and a PASS. The run as a whole came back 14 PASS / 84 REVIEW / 2 BLOCK, and the GitHub control 0 PASS / 96 REVIEW / 4 BLOCK: the REVIEW rate is the roster's, not GitLab's — on the largest, oldest, busiest projects the 22 flag execution surface that is ordinary there, and GitLab's top 100 is the milder of the two.

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) before a git clone https://gitlab.com/… line runs. The extension's board on gitlab.com pages is a later release.

False positives we know about

A quarter of the most-starred projects on gitlab.com: 26 of 100, against 27 of 80 in the GitHub Phase 0 controls and 20 of GitHub's top 100 measured the same day. Test fixtures, vendored native libraries, a Gradle wrapper, a compiled helper for a platform without a toolchain. That prevalence is why the finding is recorded rather than acted on by default — the verdict tells you the files are there, and a team that wants the strict behaviour can have it and maintain the allowlist it implies.

What this check does not cover

It matches extensions. A binary renamed .dat, a payload inside an image or a font, an archive that is itself a container — none of those match, and the xz fixtures would not have either. It does not open the files, so it cannot say whether a .so is the library its name claims; a deep scan runs ClamAV and Trivy over the bytes. It reads the tree, not the releases: on gitlab.com a release's assets are links — G9 — with no published download count, each a redirect to a free-text URL that may leave gitlab.com altogether, and they are scored separately because they live somewhere separate. And on this platform there is no Scorecard Binary-Artifacts result to set beside 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.

Check it yourself

Without installing anything: browse the tree on gitlab.com and look at the extensions. With curl against gitlab.com's API — one call for the whole tree, the project path URL-encoded so / becomes %2F; the tree entry carries a path and a blob id but no size, which the raw endpoint reports in a header:

curl -s 'https://gitlab.com/api/v4/projects/NAMESPACE%2FPROJECT/repository/tree?recursive=true&per_page=10000' | jq -r '.[] | select(.type == "blob") | .path' | grep -iE '\.(exe|dll|so|dylib|jar|pyc|class|bin|msi|scr|apk|node)$'
curl -sD - -o /dev/null 'https://gitlab.com/api/v4/projects/NAMESPACE%2FPROJECT/repository/files/PATH%2FTO%2FFILE/raw?ref=BRANCH' | grep -i '^x-gitlab-size'

Then ask, for each one, whether the project could rebuild it from what is beside it. If not, it is something you are trusting on somebody's word.

Questions

Why does a committed binary not warn by default? Because so many honest projects carry one — 27 of the 80 GitHub repositories in the Phase 0 controls, 26 of the 100 most-starred gitlab.com projects, 20 of GitHub's 100 most-starred the same day — test fixtures, vendored libraries, a wrapper JAR. A finding that warns on a quarter of a platform is a finding people switch off. So it costs 8 points and the default personal policy sets it to allow: it is on the verdict, and on its own it holds nothing. The strict organisation profile blocks on it, which is the right setting for a team that can afford the allowlist work.

What is the difference between this and the release-asset check? Where the binary lives. G15 reads the source tree — files under version control. G9 reads the links attached to releases, which on gitlab.com are not in the tree, carry no download count, and resolve through a redirect to a free-text URL that may leave gitlab.com altogether. On GitHub that is where FakeGit's payload actually was; no gitlab.com campaign of that shape exists in the 2024–2026 record, so there is no catch rate to publish here. A project can be clean in the tree and dangerous in its releases; the two checks are scored separately for that reason.

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 is recorded, not acted on, by the default policy, so on its own it changes nothing there either. A browser gate for gitlab.com's own download links is a later release.

Related checks

C15 Committed binaries — the GitHub original · G9 Release-asset binaries — the same question asked of the releases page, where the answer is a redirect · G14 Build scripts and wrapper JARs — the committed binary that runs on every build · G8 OpenSSF Scorecard — the second opinion that does not run on gitlab.com.

Add RepoGates to Chrome Pricing

Numbers on this page: the 100 most-starred gitlab.com projects and the 100 most-starred GitHub repositories, both measured 19 September 2026 through the same code as the verdict API; the GitHub Phase 0 controls of 14 August 2026 (27 of 80 popular repositories); CVE-2024-3094 as published; weights and actions from the product's scoring and policy tables.