Repository navigation
fix(gate): a gate pattern cannot hang a hook or fail it open (CH-2) - #308
Merged
Merged
Conversation
A content_regex or test_integrity pattern a backtracking engine runs in exponential time (`^(a+)+$` on a 60-character line) hung `git commit` on every platform, burned the client's hook timeout in the agent, where a timeout fails open, and wedged `chock check --history` on Windows, where the SIGALRM budget silently did nothing. `re` holds the GIL and no platform can interrupt it in-process, so a worker thread with a timeout cannot bound it either. Every gate pattern now runs in a child interpreter (`python -I -c`, stdlib only) that the engine kills at a deadline: budget.PATTERN_SECONDS, 10 s per judgement. The git hook exits 2 naming the gate, the in-agent write path and the tool_call path refuse naming it, and the history scan errors naming it, the same way on every platform. The judge lives in chock.gate.budget, which gate.py now vendors whole (assemble.budget_fragment) and the shared runtime already carried; the runner's kinds gather their texts and judge them in one or two batches per gate run (measured on the git path, 20 staged files, one gate: 0.206 s -> 0.239 s median). `chock validate` and the compile step refuse a pattern whose nested repeats can run into each other (chock.gate.pattern_safety: a linear walk of the parse tree; lookaheads and `$` count as constraints), naming the param and the pattern. The 52 patterns chock and chock-catalog install all pass; overlapping alternation and a separator the inner repeat absorbs are left to the deadline and pinned as such. .chock/bin and tests/fixtures/runtime_goldens regenerated (chock sync, CHOCK_REGEN_GOLDENS=1). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
The runner prints a pattern deadline refusal through one plain line, with the repo's suppression for the clear-text-logging query: the reason names a time or the judge's own last stderr line, never the judged text, and stderr is the runner's one channel to the person. pattern_safety imports re's error through `from` only, so the module is not imported two ways. .chock/bin/gate.py regenerated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
…(CH-2) The inline form on the print line did not suppress the alert; the repo's working form is the comment on its own line before the print. .chock/bin/gate.py regenerated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
…secret (CH-2) CodeQL read the test helper `_secret_gate` as a source of secret data and traced its return value, a gate path, through the runner to the new deadline refusal, which prints the gate's policy id. The helper builds a gate for an AWS AKIA key pattern; `_akia_gate` says so. The non-working suppression comment in verdict.py is dropped. .chock/bin/gate.py regenerated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
jothimani-rajendran
marked this pull request as ready for review
October 19, 2026 12:06
…dline Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
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.
What
CH-2 of
plan/prerelease-fixes.md(chock M7, whole-system M-3). Acontent_regexortest_integritypattern a backtracking engine runs in exponential time (^(a+)+$on a 60-character line) hunggit commiton every platform, burned the client's hook timeout in the agent (which fails open) and wedgedchock check --historyon Windows, where the SIGALRM budget silently did nothing.reholds the GIL and nothing can interrupt it in-process, so a worker thread withfuture.result(timeout)(the plan's suggested mechanism) cannot bound it either: the main thread never gets the GIL back to time out. Every gate pattern now runs in a child interpreter (python -I -c <stdlib program>, JSON lines over pipes) that the engine kills at a deadline,budget.PATTERN_SECONDS(10 s per judgement), the same way on every platform:gate: <policy>: the patterns gave no verdict within 10s (a catastrophic pattern?) -- refusing rather than allowing what it never judged._tool_call_regex, which ranre.searchin the hook process): deny naming the gate, within what is left of the engine budget.HistoryErrornaming the gate;scan.BLOB_SECONDSis nowPATTERN_SECONDSandscan.pyno longer importssignal, sotest_catastrophic_gate_pattern_times_out_naming_the_gateholds on Windows too.The judge lives in
chock.gate.budget(functions only, as the shared-runtime extractor requires);gate.pynow vendors that module whole (assemble.budget_fragment). The runner kinds gather their texts first and judge every pattern of one gate in one or two batches, so a commit costs one child per gate, not one per line. Match ordering and the waiver rules are unchanged; the existing gate tests pass unmodified. Measured on the git path (one scan-secrets gate run, 20 staged files, median of 10): 0.206 s before, 0.239 s after. The child is not started when there is nothing to judge.chock validateand the compile step (build_gate_json, sochock syncrefuses with a message and exit 1) refuse a pattern whose nested repeats can run into each other, naming the param and the pattern.chock.gate.pattern_safety.catastrophicwalks the parse tree once: a variable repeat that can run into the next iteration of an enclosing loop ((a+)+,(\w+\s?)+,(?:\.[\w.-]+)+,(a{2,3})+,(x+x+)+y).$counts as a barrier and a positive lookahead as the constraint on the next character, which is what the catalog's large patterns rely on. All 52 regex params installed in chock and in chock-catalog main a263aa9 pass. Two shapes it does not see are pinned in the tests ((a|aa)+,(.*a)+); the deadline is their backstop.Out of scope and unchanged: the mcp-gateway evaluator (
gateway/gates.py) still searches in-process; it judges the same compiled patterns validate now screens. Under the frozen binary the scan uses the firstpython3/pythonon PATH for the child and refuses to scan without one (not exercised by the binary smoke test)..chock/binandtests/fixtures/runtime_goldensare regenerated (chock sync --repo .,CHOCK_REGEN_GOLDENS=1);.chock/compiled,chock.lockand the registry are unchanged.CodeQL: the new deadline refusal prints the policy id; CodeQL traced two test helpers named
_secret_gate(their returned gate path) into that print as "secret" data. The helpers build a gate for an AWS AKIA key pattern and are renamed_akia_gate. The repo's# codeql[...]suppression comment did not take effect on that line.Definition of done
chock check→ 0 errors, 0 warnings, 0 infos (one pre-existing INFO on injection-defense's own eval suite, unchanged)chock check --only matrixpasses; matrix updated in this PR if behavior changed (no row changed: the surfaces and their grades are the same)chock sync --repo . --checkclean (compiled artifacts match their manifests)chock check --only verifyclean (lockfile matches packs and compiled artifacts)pytest -qgreen; new checks have attack + ordinary-data tests (local full run at 67e8d43: 4722 passed, 97 skipped, 1 failed, the failure beingtest_repo_standards::test_no_file_exceeds_line_budgeton an untracked.chock/state/<session>.jsonlthe agent hooks wrote in that environment while the suite ran, not a repository file; new:tests/test_pattern_safety.py,tests/test_pattern_deadline.py)pytest acceptance/ -c acceptance/pytest.ini --rootdir=acceptancegreen if packaging,init,addor hook installation changed (not changed)changelog.d/<slug>.mdfragment (no manifest touched;changelog.d/gate-pattern-deadline.mdadded)ruff check .andruff format --check .cleanClaims
spec/gate-dsl.mdgains the deadline and the validate rule oncontent_pattern.🤖 Generated with Claude Code