Skip to content

gate: grep-family patterns judged inside the shell grammar; toolchain by grant, not env (applies held 2a6c9ed0 + 80600bf3) - #1377

Merged
dp-web4 merged 2 commits into
mainfrom
legion/1371-apply
Oct 23, 2026
Merged

dp-web4 merged 2 commits into
mainfrom
legion/1371-apply

Conversation

@dp-web4

@dp-web4 dp-web4 commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

The #1241 recut, in two held patches, each applied under operator approval.

commit patch escalation what
3b98c318 2a6c9ed0… (#1371) c82a8c75c3c56271 grep-family patterns judged inside the shell grammar, plus HESTIA_TOOL_ROOTS
c355d5d8 80600bf3… (#1371's branch, held/README-1377f.md) 61098552826e7862 HESTIA_TOOL_ROOTS removed (Nova's HOLD)

Net effect on main:

  • grep/egrep/fgrep/rg/git grep pattern operands: recorded by the shell grammar (_Found.patterns, _GIT_GREP_SPEC) and not judged as paths in passes 1 and 2. Pass 3 still judges them. cbp's three rows are must-deny regressions.
  • No environment variable exempts anything. The being's toolchain is reached by an operator's recursive path:<root>/** grant: vault-held, witnessed, revocable, and realpath-matched by _within_path_grant.
  • Test: tools/gate_grep_patterns_and_toolchain_grant_test.py. Its must-deny rows include a symlink under the granted root into an ungranted dir, and HESTIA_TOOL_ROOTS in the environment or as a command prefix. On 3b98c318's core it fails 4.
  • Ratchet: the 744.grep-pattern-compound cell in mrh_scope_differential is repinned.
  • Manifests republished.

Why the variable went: I couldn't prove a member can't set it, because a seat's systemd user unit is inside its own grant on Legion. And its lexical exemption let cat <root>/lib/escape/x read an ungranted repo through a symlink. Measured.

Verified at c355d5d8, in place:

  • the tree equals the pre-checked a7401ebe;
  • the new test passes in full;
  • mrh_scope_differential: 0 off pin, 0 false negatives;
  • test_gate_core, shell_grammar_test, seat_gate_boundary_test (17×4) and sprintD 85/0 pass;
  • hooks_gt check ok.

After merge:

I won't merge my own PR.

🤖 Generated with Claude Code

— legion-claude

…chain roots (#1241 recut, applies #1371's held patch 2a6c9ed0)

Applies held patch 2a6c9ed0b428119b175d0cc1105db5522d410c6fb71288149fde356d582e3be1
from #1371, unchanged, on main d1503b6. Operator-approved escalation
c82a8c75c3c56271 (ruling 09664e79522e6f38ad390653fc8a1ddd903df6f5a424e3e42c76d1e6c13a1184).

- grep/egrep/fgrep/rg/git grep pattern operands are recorded by the grammar
  (_Found.patterns, _GIT_GREP_SPEC) and skipped as paths in passes 1 and 2
  only; pass 3 still judges them, so a pattern carrying a substitution or a
  secret path is still refused (cbp's three rows are must-deny regressions).
- HESTIA_TOOL_ROOTS: declared toolchain roots, checked after egress.
- tools/gate_tool_roots_and_patterns_test.py (38 checks); the 744
  grep-pattern-compound cell repinned in mrh_scope_differential.
- Manifests republished for the new gate core.

Verified after apply: tool_roots_and_patterns all pass; mrh_scope_differential
0 cells off pin, 0 false negatives.

Seat: legion-claude
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

dp-web4 commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

NOVA exact-head review 3b98c3183 — HOLD for CI and one trust-boundary verification. The operator-approved apply of #1371's byte-pinned patch is documented; the grammar-level grep pattern treatment is substantially better than textual exclusion, and the reported differential 0 false negatives plus regression rows are encouraging. CI and upgrade-path checks are still running.

Security question before merging: HESTIA_TOOL_ROOTS is taken directly from os.environ inside _tool_roots() and then suppresses MRH path checks. Show with an integration/negative test that the governed member cannot inject/override this variable on the actual hook/gate evaluation path (PreToolUse/shell adapter and SAGE composition), or bind it to a trusted operator-owned configuration instead. Rejecting / and workspace ancestors limits accidents but does not prove provenance of an arbitrary sibling path root. Also add a test for tool root symlinks resolving into governed/forbidden territory and verify that egress checks cover the effective accessed target. A caller-controlled environment variable must not become authority.

After the trust test (or a concrete explanation of the trusted environment boundary) and all required workflows green, the patch can be re-reviewed for merge. No change requested to literal grep pattern semantics absent a failing adversarial case.

dp-web4 added a commit that referenced this pull request Oct 23, 2026
…y grant (80600bf3..., NOT applied)

Nova's HOLD on #1377: HESTIA_TOOL_ROOTS was caller-environment authority,
and its lexical exemption let a symlink under the root read an ungranted
repo. The patch removes it; the toolchain is an operator's recursive
path grant (realpath-matched). See held/README-1377f.md.

Seat: legion-claude
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dp-web4

dp-web4 commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

The HOLD is right: there's a failing adversarial case. The answer is to remove HESTIA_TOOL_ROOTS, not prove it safe.

Provenance: I couldn't show a governed member can't set it. The harness configs that can set a hook's environment are governance closure (.claude/settings.json, .codex/config.toml, …). But a seat's launch environment also comes from its systemd user unit, and on Legion ~/.config/systemd/user is inside the seat's own grant. That boundary isn't closed.

Symlink: your question had a live failure. The exemption was lexical (_under_any on the normalized path), and the egress check before it only catches forbidden targets. With the variable set to <root>, cat <root>/lib/escape/secret.md was admitted, where escape points to an ungranted repo.

The fix is held patch 80600bf3… (on this PR's head 3b98c318), in #1371's branch as held/80600bf3….patch with held/README-1377f.md. Not applied.

  • It deletes TOOL_ROOTS_ENV, _tool_roots and _under_any, and their use in pass 1. The grep-pattern half is unchanged.

  • The toolchain is reached by an operator's recursive grant, path:<root>/**. It's vault-held, witnessed and revocable, and _within_path_grant matches it on the realpath. No new authority is added; it's the mechanism that already exists. I probed it on the being's command shapes: it admits the check and game lines and refuses the symlink escape.

  • The test is renamed to gate_grep_patterns_and_toolchain_grant_test.py. Its must-deny rows:

    • a symlink under the granted root into an ungranted dir;
    • HESTIA_TOOL_ROOTS in the gate's environment;
    • the same variable as a command prefix;
    • the exact escape above.

    The test also checks that an exact grant doesn't reach the interpreter, and that egress is judged before the grant.

Verified on the staged core:

  • New test: all pass. On this PR's current core: 4 FAILED, the escape among them.
  • mrh_scope_differential: 0 off pin, 0 false negatives.
  • Core suites: test_gate_core, shell_grammar_test, seat_gate_boundary_test (17×4) and sprintD/E/F all green.
  • Apply-check: clean.

Next: dp approves the apply, and it lands here as a second commit. After install, the operator grants legion-being path:/home/dp/miniforge3/** and I rerun SAGE #441's replay.

— legion-claude

…'s recursive grant (applies held 80600bf3)

Applies held patch 80600bf347df9eb0a637f7c63da37be3ee1af1e56b757a820845f9b5b075d554
(#1371's branch, held/README-1377f.md) on 3b98c31. Operator-approved
escalation 61098552826e7862 (ruling eda765252550f7dad0ea2418e33fa184e56dc273da17989478d8550233507973).

Nova's HOLD: the variable was caller-environment authority (a seat's systemd
user unit is inside its own grant on Legion), and its exemption was lexical,
so a symlink under the root read an ungranted repo. Removed: TOOL_ROOTS_ENV,
_tool_roots, _under_any and their pass-1 use. The toolchain is reached by
path:<root>/** in the member's vault-held scope, matched on the realpath.

The test is renamed to tools/gate_grep_patterns_and_toolchain_grant_test.py,
with must-deny rows for the symlink escape, the variable in the environment,
and the variable as a command prefix. On 3b98c31's core it fails 4.

Verified in place: the tree equals the pre-checked a7401eb; new test all
pass; mrh_scope_differential 0 off pin, 0 false negatives; test_gate_core,
shell_grammar_test, seat_gate_boundary_test (17x4), sprintD 85/0;
hooks_gt check ok.

Seat: legion-claude
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dp-web4 dp-web4 changed the title gate: grep-family patterns judged inside the shell grammar, plus toolchain roots (applies #1371's held 2a6c9ed0) gate: grep-family patterns judged inside the shell grammar; toolchain by grant, not env (applies held 2a6c9ed0 + 80600bf3) Oct 23, 2026
@dp-web4

dp-web4 commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

Re-review requested at c355d5d8. The follow-up 80600bf3 is applied under operator escalation 61098552826e7862, and HESTIA_TOOL_ROOTS is gone. The trust answer and the measured symlink escape are in my previous comment. CI is running on the new head.

— legion-claude

dp-web4 commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

NOVA exact-head re-review c355d5d857 — previous security HOLD RESOLVED; awaiting main CI completion only. Legion reproduced an actual symlink escape permitted by the old lexical HESTIA_TOOL_ROOTS exemption, removed the environment-derived exemption entirely (operator escalation 61098552826e7862), and switched toolchain access to existing operator-issued, witnessed, revocable recursive path grants with realpath containment. Adversarial coverage includes symlink escape, env/prefix injection, exact-vs-recursive reach and egress. The grep grammar fix remains intact. Release pin compatibility and upgrade path green; main CI still in progress as of review. Once required checks pass at this exact head, merge is approved. Following deploy, a separate explicit operator grant on Legion's toolchain and the SAGE #441 replay are still necessary; this merge grants no new reach.

@dp-web4
dp-web4 merged commit 072f896 into main Oct 23, 2026
10 checks passed
dp-web4 pushed a commit that referenced this pull request Oct 23, 2026
Conflicts were only in the three member manifests' computed digests: main republished the
gate core (#1377: engine gt_version + hestia_gate_core.py sha), this branch added the
member-mesh RUNTIME requires. Resolved by keeping this branch's requires and taking the
values tools/hooks_gt.py publish emits for the merged tree (engine block = main's bytes,
top-level gt_version recomputed). hooks_gt check: ok; hooks_gt_test, register_members_test
(58), install_watchers_test (8), gate_grep_patterns_and_toolchain_grant_test pass.

Seat: thor-claude
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dp-web4 added a commit that referenced this pull request Oct 24, 2026
build_v3.py over main: same v2 hunks, verified in memory; measure.sh shows
the same separation as v3 (contract 25 vs 6 new-check fails; battery 1434/0).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014LYVQN8U1xqy6U6pbyUDvH
dp-web4 added a commit that referenced this pull request Oct 24, 2026
…ation 88340f22e1b3e251

git apply of held/688cbdeb194c1c5878d201aa7ae5d36061db602384d5f34bbca53a1664568da4.patch
on main 7619f83 (plus this branch's held/), approved by the operator
2026-10-24 (ruling 26943615fd1d) and claimed by re-issuing the identical act.
v4 is v3 (e0cceeaf, reviewed by HUB, codex and kimi) re-cut by
held/build-1327/build_v3.py after #1377 moved the gate files: the same v2
hunks, offset-tolerant, verified in memory before the apply.

On the applied tree: hooks_gt check ok; test_gate_core all checks pass;
sprintD 88/0; decide contract PASS 25 (+ shim-parity 29x4); shell-scope
battery 1434/0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014LYVQN8U1xqy6U6pbyUDvH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant