security-adversarial: classify evaluator entrypoint liveness (redblue silent no-op) - #7
Merged
Merged
Conversation
… silent no-op)
Reproduced a real, 100%-reliable silent failure in this repo's own SCAN=redblue
evaluator entrypoint (dream.config.json): `npx @metaharness/redblue <anything>`
exits 0 with zero bytes on both stdout and stderr, for every subcommand tried.
Root cause: the package's CLI guards its dispatch with the classic
`import.meta.url === file://${process.argv[1]}` idiom, which fails once the
executable is reached through the symlink npm/npx create for a package's bin
entry, so the dispatch body never runs. Exit 0 + silence is indistinguishable
from "ran clean, zero findings" to anything that only checks the exit code.
Adds classifyEntrypointResult (packages/cli/src/entrypoint.ts) and a
`dream-machine verify-entrypoint` CLI command so this pipeline can separate
live / blocked / suspicious-silent before ever recording EVALUATED=yes.
Includes two fixes from an independent adversarial critique of this diff:
unquoted multi-word --cmd used to silently truncate and produce a false
"live" verdict (fixed with a hard usage-error guard); child_process.exec's
error.code can be a non-number on maxBuffer overflow (fixed with explicit
buffer size + defensive coercion).
npm test: 85 -> 96 (all new, 0 removed/modified). ADR-0002 documents the
entrypoint-trust convention. Full evidence in
docs/dream-cycle/2026-08-13-security-adversarial-report.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UAJ2FdMooTevrJ34FW3mpr
ACCEPT — redblue evaluator entrypoint liveness classifier. Issue #6, PR #7. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UAJ2FdMooTevrJ34FW3mpr
- LEDGER.md: remove the fake future-dated demo seed rows (#3-#18, 2026-08-14..19) that were never real GitHub state; keep only the real 2026-08-13 night. The nightly correctly flagged these as unverifiable (issue #6). - dream.config.json: drop the broken evaluator entrypoints the nightly proved don't work — 'npx @metaharness/redblue' (silent no-op via the bin-symlink isMain footgun) and 'npx @metaharness/flywheel' (no bin field); keep the working 'npm test' and correct darwin to 'npx @metaharness/darwin evolve'. Merges main (v0.1.x packaging, 4D site, GHA dream) into the PR branch. 96 tests green. Co-Authored-By: claude-flow <ruv@ruv.net>
ruvnet
marked this pull request as ready for review
August 13, 2026 22:04
This was referenced Aug 13, 2026
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.
Nightly Dream Cycle, 2026-08-13. DEEP=security-adversarial, SCAN=redblue,supply-chain. Full report:
docs/dream-cycle/2026-08-13-security-adversarial-report.md. Issue: #6.Hypothesis
Given a nightly pipeline that shells out to third-party evaluator entrypoints and trusts their exit code as a pass/fail signal, when a deterministic
classifyEntrypointResultfunction is interposed between the raw exec result and the pipeline'sEVALUATED=yes/no/blockeddecision, then it should correctly separate LIVE (real output, exit 0) from BLOCKED (nonzero exit, has a reason) from SUSPICIOUS-SILENT (exit 0, zero output on both streams) — a distinction the raw exit code alone cannot make. Frozen before implementation.Candidate
+584/-0 across 8 files (2 new source files, 1 new test file, 1 new ADR, 1 new report, 3 small edits). One conceptual change:
packages/cli/src/entrypoint.ts(pure classifier) +verify-entrypointCLI command wired inindex.ts/bin.ts.Evaluation Receipt
Real evaluator:
npm test(vitest, this repo's ownbenchentrypoint).Baseline
Parent commit
3de01079abe64e4a1f2d3fe3b758523705b3bf47, 85/85 tests passing, clean build.Darwin Lineage
Not run —
DARWIN=not-applicable. No evolvable population for a single pure classification function with an exhaustive 3-branch structure.Evidence
Reproduced live tonight (grade A, first-hand):
npx -y @metaharness/redblue <any subcommand>→ 0-byte stdout, 0-byte stderr, exit 0, every time. Root-caused to the installed package'sisMainguard (import.meta.url === \file://${process.argv[1]}`), a known npm-bin-symlink ESM footgun (nodejs/node#57616, grade B; native fiximport.meta.mainshipped, grade A). This repo's owndream.config.json:17documents the broken invocation as the live SCAN=redblue evaluator entrypoint. Live receipt against all four realevaluatorEntrypointsin the report:bench→live,flywheel→blocked,redblue→suspicious-silent,darwin --version`→blocked.Reward-Hack Check
Independent critic (separate agent): CLEAR of reward-hacking — only intended files changed, all additive, no test/gold/threshold tampering. Found + fixed before finalizing: (1) an unquoted multi-word
--cmdsilently truncated and produced a falseliveverdict — the exact failure class this tool exists to catch — fixed with a hard usage-error guard on stray positionals, regression test added; (2)error.codecan be non-numeric onmaxBufferoverflow with no override — fixed with explicit 10MiB buffer + defensive coercion. One risk flagged and correctly not fixed tonight (out of scope):io.execshells an unsanitized string; safe today (human-typed CLI only), but automatingevaluatorEntrypoints→verify-entrypointlater needs an allowlist orexecFilefirst — tracked in issue #6.Security Review
child_process.execinbin.tsruns via/bin/sh -c;cmdtoday only comes from a trusted local CLI flag, no new remote attack surface, no credentials, no LLM calls.IO.execis optional — no existing call site gains new capability.Regression Analysis
0 pre-existing tests modified or removed. All 85 baseline tests still pass unchanged; 11 new tests added (5 pure-classifier, 6 CLI-wiring incl. the post-critique regression test).
ADR
ADR-0002 (Proposed) — extends ADR-0001 §2.3/§2.4's "evaluators stay optional" principle to "never silently trusted either."
Gist
No
gh gist createavailable this session (no gist tool, noghCLI in this environment). Report committed atdocs/dream-cycle/2026-08-13-security-adversarial-report.mdinstead.GIST=LOCAL.Issue
#6
Witness
Verify:
sha256sum docs/dream-cycle/2026-08-13-security-adversarial-report.md, thenprintf '%s%s' "<hash>" "3de01079abe64e4a1f2d3fe3b758523705b3bf47" | sha256summust equal the witness above. Confirmed tonight viadream-machine witness verify(✓ VALID).Merge Policy
Draft — human review required. This PR does not carry the
automerge-safelabel: it adds new CLI surface and a shell-exec wiring path, which per this repo's own guarded-auto-merge policy (.github/workflows/automerge.yml) is exactly the kind of change that stays human-review-only. The session never merges and never applies that label itself.Generated by Claude Code