Skip to content

security-adversarial: detect unpinned npx supply-chain risk in evaluator entrypoints - #19

Draft
ruvnet wants to merge 2 commits into
mainfrom
dream/2026-08-18-security-adversarial
Draft

security-adversarial: detect unpinned npx supply-chain risk in evaluator entrypoints#19
ruvnet wants to merge 2 commits into
mainfrom
dream/2026-08-18-security-adversarial

Conversation

@ruvnet

@ruvnet ruvnet commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Nightly Dream Cycle, 2026-08-18. DEEP=security-adversarial, SCAN=redblue,supply-chain. Full report: docs/dream-cycle/2026-08-18-security-adversarial-report.md. Issue: #18.

Hypothesis

Given a compiled nightly routine prompt whose evaluatorEntrypoints/controlPlaneProbes embed literal shell commands invoking npx <pkg> with no pinned version, when a pure, deterministic detector classifies each such command as PINNED vs UNPINNED and is wired into the compile step to emit a visible warning for every unpinned entrypoint, then every future compiled nightly prompt should surface this known supply-chain exposure automatically, before an autonomous agent executes it — subject to: no change to any existing test, no change to the actual entrypoint commands executed tonight (detection only, not remediation), and the detector must be false-positive/false-negative-free against this repo's real dream.config.json. Frozen before implementation.

Candidate

+478/-3 across 5 files (2 new source files, 1 new test file, 1 committed report, 2 small wiring edits). One conceptual change: packages/compile/src/supplychain.ts (pure detector: findUnpinnedNpxInvocations) + wiring into step6to9Candidate in packages/compile/src/index.ts.

Evaluation Receipt

Real evaluator: npm test (vitest, this repo's own bench entrypoint).

Baseline (parent 8ce3857) Candidate
Tests 96 113 (+17, 0 removed/modified)
Result 96 passed 113 passed

Live end-to-end receipt: recompiling this repo's real dream.config.json with the candidate adds exactly a 3-line **Supply-chain warning** block naming evaluatorEntrypoints.darwin (its real, unpinned npx @metaharness/darwin entrypoint) — verified with diff against the pre-candidate compiled output.

Baseline

Parent commit 8ce385786faa5e63cc0e7105cc6e96f663a51f07, 96/96 tests passing, clean build.

Darwin Lineage

Not run — DARWIN=not-applicable. Probed live tonight (npx @metaharness/darwin evolve --sandbox mock → real leaderboard, LIVE), but no evolvable population exists for a single, already-minimal pure detector function — same rationale as 2026-08-13/ADR-0002.

Evidence

Reproduced live tonight (grade A, first-hand): npm view @metaharness/darwin shows version 0.9.2, 4 published versions (0.8.30.9.2), last published 2026-08-15 (3 days before tonight), single maintainer ruvnet, only a latest dist-tag exists. Grade A/B research: OWASP's official NPM Security Cheat Sheet explicitly warns npx bypasses the lockfile and to pin npx pkg@<version>, not npx pkg@latest; the Shai-Hulud/Miasma npm worm lineage has repeatedly compromised maintainer accounts and self-propagated via fresh malicious publishes since late 2025, most recently 2026-08-04 against a ~127M-weekly-download package family. Full evidence trail with grades in the committed report.

Reward-Hack Check

Independent critic (separate agent, not this candidate's author) reviewed the diff firsthand (git diff, file reads, its own npx vitest run) and reported FLAGGED with real correctness bugs, CLEAR of reward-hacking:

  1. False positive: npx --package=@metaharness/darwin@0.9.2 darwin-evolve (pinned) was incorrectly flagged — the original code checked the bin-name token instead of the --package= value.
  2. False negative (serious): npx --package=@metaharness/darwin run@1.0 (genuinely unpinned) produced zero findings, same root cause.
  3. Scope gap: npm exec @metaharness/darwin (npx's own alias, same risk) was never scanned.
  4. Misleading message: npx ./scripts/tool.js (local file, no registry involved) was flagged with text claiming it "resolves the registry latest dist-tag".

No gold/threshold/gate/automerge-protected path was touched; the golden snapshot test is untouched (the existing fixture has no npx entrypoint, so the new code path is exercised only by new, honestly-separate test configs). All 4 bugs fixed in packages/compile/src/supplychain.ts (extractPackageSpec now honors --package=/-p/--package <spec>; isLocalOrRemoteSpec excludes local/git/URL specs; the match now also accepts npm exec) and independently re-verified by re-running the critic's exact 4 repro strings directly against the rebuilt code — all four now resolve correctly. 6 new regression tests lock in the fixes.

Security Review

No prompt injection surface (no LLM calls in this candidate). No credential exposure, no new filesystem/network I/O — the detector is a pure string-only function over already-loaded config fields. No change to io.exec/verify-entrypoint's actual shell-exec path; this candidate only adds text to the generated prompt, not to what gets executed.

Regression Analysis

0 pre-existing tests modified or removed. All 96 baseline tests still pass unchanged; 17 new tests added (14 pure-detector incl. 6 critic-bug regressions, 3 compiler-wiring).

ADR

None. This is a compile-time detection/warning addition, not a new architectural decision — it extends ADR-0002's "classify, don't silently trust or silently fix" precedent within the existing architecture rather than introducing a new invariant or gate.

Gist

No gh gist create available this session (no gist tool, no gh CLI in this environment, same as 2026-08-13). Report committed at docs/dream-cycle/2026-08-18-security-adversarial-report.md instead. GIST=LOCAL.

Issue

#18

Witness

report_sha256 : 37e582b0fd9bb93bece762e8bd4095a33a317e5a2d6b17922b1c862d6b0daf37
session_commit: 8ce385786faa5e63cc0e7105cc6e96f663a51f07
witness       : e64046c951e7ef5fa7b72177bd9a6328df730610e5cca62397866168678ae123

Verify: sha256sum docs/dream-cycle/2026-08-18-security-adversarial-report.md, then printf '%s%s' "<that hash>" "8ce385786faa5e63cc0e7105cc6e96f663a51f07" | sha256sum must equal the witness above. Confirmed tonight via dream-machine witness verify (✓ VALID).

Merge Policy

Draft — human review required. This PR does not carry the automerge-safe label: it adds a new compile-time behavior (the compiler now emits an extra warning block whenever a future config has an unpinned entrypoint), which is a judgment call about tonight's own scan finding, not touched by the repo's protected-path list, but still substantive enough to warrant a human read rather than an automatic merge. The session never merges and never applies that label itself.


Generated by Claude Code

claude added 2 commits August 18, 2026 09:21
…tor entrypoints

Nightly Dream Cycle, 2026-08-18. DEEP=security-adversarial, SCAN=redblue,supply-chain.

dream.config.json's own evaluatorEntrypoints.darwin invokes `npx @metaharness/darwin
evolve --sandbox mock` with no version pin; npx resolves the registry's `latest`
dist-tag fresh on every call and isn't governed by this repo's lockfile. Adds a pure
detector (findUnpinnedNpxInvocations) wired into the prompt compiler so every future
compiled nightly routine surfaces this before an autonomous agent executes it.
Detection only, not remediation (matches ADR-0002's precedent).

An independent critic review found real correctness bugs in the first pass
(--package= mishandling causing both a false positive and a false negative, a
missed npm exec alias, a misleading message on local-path invocations) — fixed
and re-verified independently before this commit. Full report:
docs/dream-cycle/2026-08-18-security-adversarial-report.md

Evaluator: npm test. Baseline (parent 8ce3857) 96/96 passed. Candidate 113/113
passed, 0 regressions, clean build.
#19)

ACCEPT — unpinned npx supply-chain detector, npm test 96->113, 0 regressions.
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.

2 participants