
The Security Check That Checked Nothing
A trust-root check returned when its file was absent. Learn how to test security controls for missing inputs, not only bad ones.
TL;DR: A security check that treats a missing required file as nothing to check can let attacker-chosen policy decide the result. Test absence, not only tampering. The parent slice log keeps the failure on record.
Your security check catches a changed file. What does it do when the file is not there at all? That question found a hole in Ranex after a completed control had already been marked done.
In this note
- The check returned without checking
- Missing is a security state
- Test your own security gates
- The fix and its limit
- Make absence loud
The check returned without checking
The bug was not a scanner that missed a clever payload. It was a trust-root check that returned when the evaluated commit did not carry the path it was asked to inspect.
Ranex evaluates evidence against a gate catalog and a producer keyring. Those files are policy: the catalog says which claims a verdict needs; the keyring says which producers can supply evidence. A working-tree edit to either committed file was caught. But refuse_uncommitted_trust_root had an early return for a path the evaluated ref did not carry. It had compared no bytes and allowed the run to continue.
ADR-002 records the three reproduced routes: an uncommitted catalog named with --gate-catalog, a keyring hidden by a committed .gitignore and named with --producers, and a committed symlink whose reviewed name did not contain the bytes ultimately read. None needed key theft or a forged signature. Each reached the same return.
The lesson is smaller than the incident and more useful. A control can work perfectly for the input you expected while doing nothing for the input an attacker gets to choose.
Missing is a security state
A missing required input must produce a decision. If it reaches a default branch, it has already become policy.
It is tempting to think of absence as an operational nuisance: a file was not provisioned, a setting was omitted, a report was not generated. That is only true when the system owns the omission. When the party being measured can name the path, omit the field, or redirect the lookup, absence is part of the attack surface.
Ranex already had the right general rule: absence blocks. No evidence is a FAIL, never a default and never a skip. The trust root was the exception, even though every later check depends on it. The function was written for an operator selecting a path. Its caller also allowed the measured party to select that path. The security boundary had changed; the branch had not.
This is different from the SLICE-004 lesson in 59 Refusals, Zero Tests. That post is about measuring whether named refusal paths execute at all. This incident is narrower: one executed policy check treated an attacker-chosen missing trust root as permission to proceed.
Look for this shape outside security tooling too. An authorization rule that skips when a role is absent. A deployment gate that reads a missing scan report as zero findings. A schema validator that makes a field optional without deciding whether the operation should be allowed without it. “Nothing to validate” is never a neutral answer until you have decided who benefits from it.
Test your own security gates
You can find this class of bug without adopting Ranex. Start at a check that decides whether work can proceed and ask what every escape route means.
- List every early
return,continue, fallback, and empty collection in the check. - For each required input, test absent, unreadable, malformed, redirected, and present-but-wrong states separately.
- Ask whether the caller, the operator, or the measured party controls the file name or field. A safe default for one can be an exploit for another.
- Ask the source of truth about the name supplied, before resolution can turn that name into a different object.
- Keep one ordinary success test beside the refusal tests. A fix that refuses every path is not a security control; it is an outage.
That last test matters. Security tests often prove only that bad inputs fail. A control that makes every input fail will look excellent until somebody tries to use it.
The fix and its limit
Ranex now refuses a trust-root path the evaluated commit does not carry. It compares the bytes stored under the supplied repository name with the bytes it reads, then passes those committed bytes to the loader rather than reopening a path. The same rule applies to the producer keyring used by run.
The decision record says the ordinary committed catalog and keyring must still reach PASS. Its security test covers five refusal cases and one normal path; every refusal exits with no verdict. The record also reports that the check-and-reopen window was removed by parsing the committed bytes, and that the observed file opens fell from three and two to one each. Those are useful measurements, not a claim that review can judge whether a committed gate asks for enough.
That limit remains. A reviewed catalog can require too little, and this control cannot tell. The point is not that files in git become wise. The point is that the bytes whose policy you reviewed are the bytes that decide the verdict.
Ranex is pre-release. This is one repaired control in a kernel with a working verdict path, not a finished security product.
Questions people actually ask
Use these answers when a required security input is missing rather than merely incorrect.
What is a trust root in a security system?
A trust root is the committed file whose bytes decide whether later evidence counts. In Ranex, the gate catalog and producer keyring are trust roots because they decide every verdict.
What does fail closed mean for a missing security input?
Fail closed means Ranex refuses when a required trust-root path is absent instead of continuing with unchecked bytes. A missing input is a named refusal, not an empty success.
How did Ranex find the trust-root bug?
Ranex found the bug while auditing SLICE-003 after SLICE-002 had closed: a path absent from the evaluated commit made the trust-root check return without comparing bytes.
Is Ranex ready to use today?
No. Ranex is pre-release; its README describes a working verdict path with much of the surrounding product still unbuilt.
Make absence loud
Pick one security check you own. Delete or redirect the input it relies on in a disposable test. Then make the expected refusal specific: name the input, stop the operation, and preserve a normal success case.
Do not settle for a check that catches the wrong value. Make it tell you what happens when there is no value to check.
Try it. Break it. Tell me what broke.
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.
Keep reading

What Ed25519 Signing Actually Proves About a Test Result
What Ranex Ed25519 evidence signatures prove, what they cannot prove about approval or truth, and how the committed public keyring works.

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.

Inside the Append-Only, Hash-Chained Journal
How the Ranex journal blocks ordinary rewrites, detects edited rows, and where its rollback and truncation limit remains open.
