Skip to content

Commit f722bfb

Browse files
test(mcpverbscheck): pin the exact advertised MCP verb roster — a constant-count swap must not slip a verb in (#1)
Adds (6b)/(6c) to the L4 section. L4_COUNT==31 fails closed on a verb ADDED and the per-name arms pin the L4/field-notes verbs individually, but a rename or swap that keeps the count at 31 and touches a verb no arm names (analyze, grep, the edit trio, ...) passes every existing arm — CONTRIBUTING §2 shape 7 ('true but narrower'). (6b) pins the full sorted advertised roster so ANY add/remove/rename reddens until a human signs for it; (6c) is the mutation control proving (6b) can see a new verb, plus a named tripwire asserting no shell/exec-shaped verb is advertised (--run-trace stays CLI-only). Context: security-audit follow-up. Both audit findings closed clean — M1 (report XSS) not-a-bug, verified end-to-end: jsonesc::escapeHtml \uXXXX-escapes <>& at emission and all 8 innerHTML sinks escape repo-derived values or interpolate integers/constexpr vocab. L1 (--run-trace reachable from MCP) was never a vuln: no such verb exists. This arm is invariant-hardening so that stays true, not a vuln fix. Verified on a real build: planted lego->run_trace in kMcpVerbTable + the tools/list stanza (constant-count swap) — L4_COUNT==31 stayed green, (6b) reddened with the diff, (6c) tripwire reddened; reverted, gate ALL PASS. New arm in an existing gate file — no regression.sh or gate-count ride-along. Co-authored-by: usehoplite[bot] <288093033+usehoplite[bot]@users.noreply.github.com>
1 parent f45087a commit f722bfb

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

test/mcpverbscheck.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,76 @@ if [ "$L4_EDITCHK" = "True" ]; then ok "tools/list includes 'edit_check'"; else
302302
[ "$L4_PACKTASK" = "False" ] && ok "'pack_task' is NOT separately advertised in tools/list (dispatch-only alias)" \
303303
|| no "'pack_task' unexpectedly appears in tools/list"
304304

305+
# ── (6b) THE EXACT ADVERTISED ROSTER — a constant-count swap must not slip a verb in ─────────────
306+
# WHY THIS ARM. The L4_COUNT==31 assertion above fails closed on a verb ADDED; the per-name checks pin
307+
# the L4/field-notes verbs individually. Neither catches a RENAME or SWAP that keeps the count at 31 and
308+
# touches a verb no arm names (analyze, grep, the edit trio, …) — CONTRIBUTING §2 shape 7 ("true but
309+
# narrower"): "same count + these names present" is strictly weaker than "the roster is EXACTLY this set".
310+
# A verb that reaches a subprocess (a hypothetical run_trace MCP twin of the CLI-only --run-trace) could
311+
# replace an unnamed read verb at count 31 with every arm above still green. Pinning the FULL sorted
312+
# roster reddens on ANY add/remove/rename until a human updates this list — the point being that a new
313+
# MCP verb, above all one that reaches an exec, is signed for, never a silent drift.
314+
# The set is the ADVERTISED roster (kMcpVerbTable, mcp.h); pack_task stays out (dispatch-only alias,
315+
# already asserted absent above). Sorted so the diff reads name-by-name.
316+
EXPECTED_VERBS="analyze
317+
batch
318+
cochange
319+
connect
320+
doc_drift
321+
edit_check
322+
exemplar
323+
explore
324+
fetch_body
325+
find_referencing_symbols
326+
find_symbol
327+
flags
328+
for
329+
from_trace
330+
grep
331+
impact
332+
insert_after_symbol
333+
insert_before_symbol
334+
lego
335+
memory_recall
336+
mentions
337+
owners
338+
path_between
339+
quality_baseline
340+
quality_delta
341+
replace_symbol_body
342+
situational_awareness
343+
slice
344+
stray_content
345+
uses
346+
whereis"
347+
348+
LIVE_VERBS_SORTED="$( l4_field 'chr(10).join(sorted(names))' )"
349+
EXPECTED_SORTED="$( printf '%s\n' "$EXPECTED_VERBS" | sort )"
350+
351+
if [ "$LIVE_VERBS_SORTED" = "$EXPECTED_SORTED" ]; then
352+
ok "(6b) advertised roster matches the pinned set exactly ($L4_COUNT verbs; no unpinned add/rename/swap)"
353+
else
354+
no "(6b) advertised roster DRIFTED from the pinned set — a verb was added, removed, or renamed; review it (a subprocess-reaching verb must never join silently), then update EXPECTED_VERBS consciously:"
355+
diff <(printf '%s\n' "$EXPECTED_SORTED") <(printf '%s\n' "$LIVE_VERBS_SORTED") | sed 's/^/ /'
356+
fi
357+
358+
# ── (6c) LIVENESS of (6b) + the named shell-exec tripwire (CONTRIBUTING §2: prove the arm can fail) ─
359+
# (6b) is only as live as its ability to SEE a new verb. Prove it on a mutated copy of the live list:
360+
# inject a synthetic run_trace and assert the SAME comparison reddens. Guards shape 3 (empty==empty) if
361+
# a future edit ever broke the extraction. Computed here, run every time — never a fixture of the server.
362+
MUT_LIVE="$( printf '%s\nrun_trace\n' "$LIVE_VERBS_SORTED" | sort )"
363+
if [ "$MUT_LIVE" != "$EXPECTED_SORTED" ]; then
364+
ok "(6c) mutation control: a synthetic 'run_trace' verb is correctly seen as roster drift"
365+
else
366+
no "(6c) mutation control VACUOUS: injecting 'run_trace' did not disturb the comparison — (6b) cannot fail"
367+
fi
368+
# The invariant, named for the reader who greps for it: no shell/exec-shaped verb is advertised.
369+
if printf '%s\n' "$LIVE_VERBS_SORTED" | grep -qxE 'run_trace|run|shell|exec'; then
370+
no "(6c) an MCP verb named like a shell/exec entry point is advertised — --run-trace must stay CLI-only (runtracecheck.sh)"
371+
else
372+
ok "(6c) no shell/exec-shaped verb advertised (MCP surface reaches no subprocess exec; --run-trace stays CLI-only)"
373+
fi
374+
305375
# ── explore round-trip: a pack-task-shaped bundle (same shape as CLI --pack-task) ────────────────
306376
EXPLORE_MSGS=(
307377
'{"jsonrpc":"2.0","id":1,"method":"initialize"}'

0 commit comments

Comments
 (0)