Boundary terms by digest, so the term itself never has to be stated - #56
Merged
Conversation
BL-050 gap 2. The public blocklist matches by SHA-256 so the file stops naming what it protects, but the digests are themselves public and a digest of a short guessable phrase falls to a wordlist immediately. Obscurity, not secrecy -- as that file already says. MP_BOUNDARY_TERMS was the escape hatch, and it is not quite sufficient for a term that genuinely must not be recoverable: putting plaintext into a repository secret means typing it, into a shell that keeps history or a transcript that persists. The act of protecting the word states the word. MP_BOUNDARY_DIGESTS takes the hash instead. scripts/add_boundary_term.py reads the term with getpass -- no echo, no argv, and it refuses a command-line argument because a term in argv is in the shell history and the process list before the program starts -- hashes it in memory and prints only 64 hex characters. Those go in the repository secret. The term then exists in no file, no git history, no CI configuration, no shell history and no transcript. And because the digest is in a secret rather than in the public checker, it cannot be wordlisted either: guesses cannot be tested against a digest an attacker cannot see. That closes the honest limit documented in the checker, for the terms that need it closed. The helper refuses terms longer than the matcher's 3-word n-gram window rather than handing back a digest that silently blocks nothing, and malformed secret entries are ignored rather than crashing -- a boundary check that crashes is a boundary check that gets disabled. Both CI boundary steps receive the secret; an absent secret is an empty string and no extra terms, so nothing breaks for a fork or a clone. NOT DONE HERE, deliberately: the secret is not set. Setting it requires typing the term, and doing that through an agent would recreate the exact leak this prevents. Instructions are in the script docstring; it is a one-command owner action. 322 tests pass.
…ing them
A secret input makes this check unreproducible by a third party. A fork runs it
without the secret and can go green where main would go red. That is a real cost
of the digest mechanism and the answer is not to pretend it is absent.
The success line now reports how many vocabulary rules were in force and how many
of them came from outside the repository, and says plainly that the result is not
reproducible without them. A reader learns that an unaudited input existed and can
ask for it. They learn nothing about what it was.
3 shipped vocabulary rule(s).
3 shipped vocabulary rule(s) + 2 supplied at runtime and not present in this
repository, so this result is not reproducible without them.
The count is deliberately not bucketed or rounded. Rounding would be a second,
quieter piece of obscurity, and this line exists to reduce obscurity.
This does not touch any finding. The boundary checker is a publication guard, not
a measuring instrument: no result in LIN-000 or KL-001 depends on its output, and
deleting it entirely would leave every number unchanged. The reproducibility cost
is confined to CI's own verdict, which is exactly why it is worth stating there.
Also corrects the header comment, which still named the plaintext variable as the
answer for terms that must not be recoverable.
323 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BL-050 gap 2.
The public blocklist matches by SHA-256 so the file stops naming what it protects — but the digests are themselves public, and a digest of a short guessable phrase falls to a wordlist immediately. Obscurity, not secrecy, as that file already admits.
MP_BOUNDARY_TERMSwas the escape hatch and it is not quite sufficient:MP_BOUNDARY_DIGESTSTakes the hash instead.
scripts/add_boundary_term.pyreads the term withgetpass(no echo, no argv), hashes it in memory, prints only 64 hex characters. Those go in the repository secret.The term then exists in no file, no git history, no CI configuration, no shell history, no transcript. And because the digest lives in a secret rather than in the public checker, it cannot be wordlisted either — you cannot test guesses against a digest you cannot see.
That closes the honest limit documented in the checker, for the terms that need it closed.
Details
argvis in the shell history and process list before the program startsDeliberately not done here
The secret is not set. Setting it requires typing the term, and doing that through an agent would recreate the exact leak this prevents. One-command owner action; instructions in the script docstring.
322 tests pass.