security-adversarial: automerge-guard PROTECTED package enumeration drift (issue #99) - #100
security-adversarial: automerge-guard PROTECTED package enumeration drift (issue #99)#100ruvnet wants to merge 2 commits into
Conversation
…rift (issue #99) scripts/automerge-guard.mjs's PROTECTED path list enumerated 5 of 8 workspace packages by name instead of blanket-protecting packages/. cli, edge-contracts and edge-sim were never added after those packages shipped, so a PR touching only packages/cli/src/bin.ts (the CLI binary this repo's own tooling executes for ledger append/witness stamp/verify-entrypoints) was classified eligible:true, identical to a harmless docs PR. Replaced the five-name enumeration with a blanket packages/[^/]+ match so every current and future package is protected by construction, plus a regression test covering the three real gaps and one synthetic future-package case. npm test: 616 vitest + 82 governance (was 81), 0 regressions. lint/build clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UAKxJ4kWuXUqMsWFTsqysV
… PR #100) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UAKxJ4kWuXUqMsWFTsqysV
|
Self-review correction (STEP 26), disclosed rather than silently left inaccurate:
Neither changes the evaluation receipt (real Generated by Claude Code |
ruvnet
left a comment
There was a problem hiding this comment.
Dream cycle exact-head review.
Frozen review hypothesis: replacing the enumerated workspace package paths with a single segment-safe packages/<name> rule will make every current and future package subtree ineligible for automerge without changing non-package eligibility.
Evidence: the live CLI reproduction changes a package candidate from eligible: true to protected-path; guard coverage rises from 14 to 15 cases and explicitly includes a synthetic future package; the full suite remains 616/616 with 82/82 governance tests, build and lint clean. Exact-head CI run 34209362093 and CodeQL run 34209362154 both passed. The rule is anchored and segment-bounded, so it does not overmatch packages-other. Autocomplete remains disabled in repository state, so this is dormant hardening rather than evidence of a live merge.
Process caveat: the PR's own hypothesis was documented after implementation. This review hypothesis was frozen independently before inspecting the candidate result; future implementation work should preregister before coding.
ACCEPT
Hypothesis
Candidate
scripts/automerge-guard.mjs: one regex line,packages/(?:compile|ledger|witness|memory|schedule)→packages/[^/]+(+ explanatory comment).scripts/automerge-guard.test.mjs: one new test asserting the three real previously-unprotected paths (packages/cli/src/{bin,index,entrypoint,tui}.ts,packages/edge-contracts/src/index.ts,packages/edge-sim/src/index.ts) plus one synthetic never-invented package (packages/anything-not-yet-invented/src/index.ts) are all nowprotectedPath === true. Total diff: 24 lines, one conceptual change.Evaluation Receipt
Real evaluator:
npm test(vitest run && npm run test:governance), identical commands on baseline then candidate.3edd426automerge-guard.test.mjsalonetsc -b×8)eslint packages)Live repro, pre-fix:
evaluateGuard(...)on a synthetic PR touching onlypackages/cli/src/bin.tsreturned{"eligible":true,"reason":"separate-human-review-required"}. Post-fix, identical input:{"eligible":false,"reason":"protected-path"}.Baseline
3edd426f6c9c4b1e80235f7447dc863e749345cc(session start,maintip). 616 vitest + 81 governance tests green; lint/build clean.Darwin Lineage
DARWIN=not-applicable. One regex line + one test — no meaningful mutable population for bounded generations×children search (ADR-0002 precedent). Also declined to invoke thedarwinevaluator entrypoint itself tonight (unpinnednpx @metaharness/darwin, already-flagged supply-chain risk) — running it would contradict this candidate's own reasoning.Evidence
node -erepro at baseline3edd426):protectedPath('packages/cli/src/bin.ts') === false,evaluateGuard(...).eligible === truefor a synthetic bin.ts-only PR.list_pull_requests/search_issues): zero existing open PR or issue names this finding.npxdarwin-entrypoint finding (4 prior independent implementations: security-adversarial: detect unpinned npx supply-chain risk in evaluator entrypoints #19/fix(config): darwin evaluatorEntrypoint missing required <repo> positional + verify-entrypoints #40/security-adversarial: detect unpinned npx supply-chain risk in evaluator entrypoints (supersedes #19) #84 + a 2026-09-03 local-only cross-check — ≥3-repeat learning-signal threshold crossed) or thezeroMergeStreak/mergedPrNumbersmeasurement-quality finding (developer-experience: wire real merge state intoledger signals(zeroMergeStreak always-true bug) #9/ledger-signals: derive zeroMergeStreak from Prior-night fates tokens #15/developer-experience: thread real merge state into zero-merge learning signal (cli, tui) #27/ledger-signals: wire mergedPrNumbers into ledger signals + tui (supersedes #9, #15, #27) #89 — 4 prior implementations) — both rejected to avoid a 5th duplicate direction per STEP 1.1.Reward-Hack Check
New test includes a synthetic never-real package name (
packages/anything-not-yet-invented) specifically so the fix can't be verified by a candidate that only special-cases the three known-bad paths by name — guards against a narrow re-patch reintroducing the same drift class.Security Review
Widened regex is a strict superset — no path protected before this change becomes unprotected.
dream.config.json'sautoMerge: falsemeans this logic is fully inert today (config-disabled short-circuit, unchanged, still tested); this hardens the guard for if/when a human ever flips that flag..github/workflows/automerge.ymlreverified read-only (contents: read,pull-requests: read, base-only checkout, no merge/label authority). No prompt-injection surface, no credential exposure in this diff. Adjacent operational note (not a repo defect, not shipped as code): this session's own shell command briefly echoed the liveOPENROUTER_API_KEYvalue into its own transcript via an unquoted${VAR:-default}bash idiom — recorded as evidence in the attached report; grepped this repo's own scripts/workflows for the same idiom near credential-sounding names and found none.Regression Analysis
0 pre-existing tests modified or removed. 616 vitest unchanged; 81→82 governance (additive only). Lint and build clean, identical to baseline.
ADR
None. Bug fix to an existing governance guard's path-matching logic — closing a coverage gap, not introducing a new invariant, schema, or promotion-policy decision (matches #9/#15/#27's and 2026-09-06's precedent for non-architectural CLI/tooling changes).
Gist
No
ghCLI, no Gist-creation MCP tool this session.GIST=LOCAL— full report committed atdocs/dream-cycle/2026-09-08-security-adversarial-report.md.Issue
#99
Witness
(computed via
node packages/cli/dist/bin.js witness stamp <report> <commit>, on pre-Witness-section content, same convention as prior nights — see report's own Witness section for the reproduce-it verification chain)Merge Policy
Human review required. This session never self-merges and never autonomously promotes candidate state.
automerge-safelabel intentionally not applied (repo'sautoMergeconfig isfalse; this PR touchesscripts/which is itself a protected path under the very guard it modifies, so it would never be auto-merge-eligible regardless).🤖 Generated with Claude Code
https://claude.ai/code/session_01UAKxJ4kWuXUqMsWFTsqysV
Generated by Claude Code