
An Environment Variable Can Choose the Repo You Judge
A GIT_* variable redirected Ranex checks to the wrong repository. Learn the boundary a git flag cannot enforce and the fix that closed it.
TL;DR: A git safety flag cannot protect you if inherited environment state chooses a different repository first; build the child environment deliberately. The Ranex slice log shows why the observed artifact must be the artifact you intended.
You can write “refuse” in a threat table and still leave the door open. That happens when a control answers one question while an unexamined input answers the more important one.
For a tool that judges a repository, the first question is painfully basic: which repository did it actually read?
In this note
- The flag did not choose the repository
- Ambient state is an input
- The fix builds the child environment
- What the boundary still misses
- An audit you can run
- Where Ranex stands
The flag did not choose the repository
--no-replace-objects stops git object substitution inside the repository it is querying. It does not choose that repository.
ADR-004 records how Ranex learned that distinction. An earlier ADR said every git query passed --no-replace-objects, so a git replace substitution would refuse. The statement was only half a boundary.
At nine of ten call sites, Ranex’s shared git() helper passed env=None. That inherited the caller’s GIT_* variables. A relative GIT_DIR could therefore direct subject-digest reads, trust-root reads, and scratch-index status at a shadow repository the observed party controlled.
The attack was reproduced end to end without forging anything. Genuine signed evidence produced in the shadow tree passed the governed repository’s gate even though the governed repository’s own committed check exited 1. The evidence was signed correctly. The measuring instrument was aimed at the wrong thing.
Ambient state is an input
Inherited environment variables are inputs, even when no function parameter mentions them. If the observed party can set them, they can influence what your tool observes.
This was not a git-only lesson. PATH can choose the binary behind a command name. HOME can choose global configuration. A process that silently inherits either has accepted a decision from outside its explicit interface.
That does not mean one fix closes every environment risk. ADR-004 scopes its change to the environment of git queries Ranex itself makes. It says plainly that HOME still selects ~/.gitconfig, PATH can still select a git shim, and a repository’s own .git/config remains in scope for git. Naming the limit is part of the control.
This same shape sits behind several false-pass routes: a toolchain or import path selected by the party being measured can make a check run somewhere other than the code you thought you checked. Six roads to a false pass covers the related PYTHONPATH and sitecustomize bypasses.
The fix builds the child environment
Ranex now removes every GIT_* key from os.environ before launching git, then applies only explicit call-site overrides. Ambient GIT_* values no longer choose the repository.
The helper’s parameter changed from env to overrides, because the old name suggested a complete environment when the intended meaning was a small deliberate addition. One call site needs that addition: uncommitted_paths passes the scratch index it computed as GIT_INDEX_FILE.
The choice is broader than a denylist. A future variable beginning GIT_ is excluded as soon as it exists. Git’s own environment helper was not copied: it intentionally lets configuration variables through so -c works across submodules. ADR-004 records that a copied allowlist would have preserved that gap.
The backing test does not settle for “the poisoned run did not pass.” It asserts that the poisoned evaluation returns the same verdict as the clean evaluation. A crash could satisfy the weaker assertion and still hide a broken boundary. Restoring inherited environment makes the reproduction red again while the clean controls stay green.
What the boundary still misses
The GIT_* boundary closes the reproduced redirect, not every input git honors. Repository-local config can still inject a filter; ADR-004 leaves that as a strict expected failure. HOME, PATH, and future non-GIT_* variables are also outside this specific control.
That disclosure is not an apology for the fix. It is what keeps a narrow control from becoming a broad claim. The earlier defect was not merely an unhandled edge case; it was a sad-path row that implied it covered more than it did.
If your own tool shells out, inspect the process boundary as closely as the command-line flags. Ask which inputs select the target, which inputs select the executable, which inputs select configuration, and which of those inputs came from the caller by accident.
An audit you can run
You can find this bug shape before an incident. Start with the boundary that decides what your tool reads, not only the boundary that decides what it may do.
- Locate every subprocess wrapper. A single shared helper is useful only if every relevant query uses it.
- List ambient selectors. Check environment, current directory, config discovery, executable lookup, and inherited file descriptors.
- Separate target from action. A flag can constrain an action while saying nothing about the target.
- Construct the child environment. Start from an intentional set and admit only values the program calculated or explicitly owns.
- Poison the environment in a test. Assert the clean and poisoned runs produce the same result.
- Write down exclusions. A boundary that cannot see an input must not claim to control it.
Where Ranex stands
Ranex is pre-release. The README describes a kernel with a working verdict path and a larger designed surface that is not yet built. ADR-004 records a closed control in that kernel boundary; it does not turn the project into a finished product or erase the separate risks listed above.
Your next review should begin one layer earlier than the flag. Confirm the process is asking the right repository before you celebrate the answer it received. Try it. Break it. Tell me what broke.
Questions people actually ask
Can GIT_DIR make a tool judge the wrong repository?
ADR-004 reproduced a relative GIT_DIR redirect that made Ranex read the subject digest, trust roots, and scratch-index status from a shadow repository controlled by the observed party.
Does --no-replace-objects choose the repository git reads?
No: ADR-004 says --no-replace-objects constrains object substitution inside a repository, while inherited environment variables had separately chosen which repository Ranex queried.
How did Ranex stop ambient GIT_* variables?
Ranex changed git() to build a child environment from os.environ minus every GIT_* key and then apply only explicit call-site overrides, including the computed GIT_INDEX_FILE override.
What does the GIT_* fix still not cover?
ADR-004 leaves repository-local configuration, HOME-selected global gitconfig, PATH-selected git binaries, and non-GIT_* variables outside this boundary.
Disclosure: this post was drafted with AI assistance. Every factual claim traces to the repository’s README or slice records — the same fact gate the product enforces on code. It ships only after Anthony’s own review.
About the author

Anthony Garces
Anthony Ryan M. Garces is a Senior Principal Lead Architect with 17+ years in IT, including four years at Pantheon on mission-critical platform work. He is building Ranex in public.
Related Articles

How Ranex Judges AI-Written Code: The Kernel, Explained
Ranex judges AI-written work by evidence and executable checks, never by model confidence. Here is the whole mechanism: three ports, one kernel, and only one of them produces a verdict.

Hashing Dependencies Does Not Make Them Honest
A SHA-256 hash identifies approved dependency bytes. It cannot prove that imported code will report your test result honestly.

Measure Before Learning: Stop Calling One Run a Win
A single run cannot prove a change helped. Freeze the comparison, set the rules first, and keep observations separate from grades.