Files
ruflo/docs
rUvandClaude e7fb8edc19 dream(security): #3138 opt-in MCP governance policy enforcement (evaluated, ACCEPT) (#3139)
* dream(security): #3138 opt-in MCP governance policy enforcement (evaluated, ACCEPT)

.harness/mcp-policy.json declared auditLog/maxToolCallsPerTurn/dangerousPatterns
but was only ever read by the offline metaharness mcp-scan linter -- the actual
running claude-flow MCP server (mcp-server.ts) never consulted it, so any
connected client could call every tool with no audit trail and no call budget.

Adds an opt-in PolicyEnforcer (RUFLO_MCP_ENFORCE_POLICY=1, default off) wiring
auditLog + maxToolCallsPerTurn into the real tools/call dispatch path. Default
behavior is unconditionally unchanged. 16/16 new tests, 39/43 broader MCP
regression sweep (4 failures traced to pre-existing unbuilt sibling packages,
unrelated to this change). Independent adversarial critique found one real,
disclosed limitation (maxToolCallsPerTurn is session-lifetime cumulative, not
true per-turn) -- documented in the module, gist, and issue; not a blocker for
landing opt-in/default-off.

Full research, hypothesis, evaluation receipt, and witness: issue #3138 and
docs/dream-cycle/dream-gist-2026-08-31.md.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01QqhXjtDofNNJPX3CCVQyrk

* dream(security): fail-closed on policy load/audit-log failure (review round 1)

Addresses ruvnet's PR review on #3139: an enforcement feature that
silently falls back to unrestricted execution when its own config is
missing/malformed, or when a mandatory audit-log write fails, isn't
real enforcement.

- loadMcpPolicy() returning null (missing/malformed .harness/mcp-policy.json)
  now denies the call instead of falling through unconstrained.
- A failed appendAuditLog() write, when policy.auditLog is true, now denies
  the call instead of silently proceeding unaudited.
- New evaluateToolCall() is the single enforcement entry point combining
  fail-closed policy loading + budget + fail-closed audit logging;
  mcp-server.ts's tools/call wiring simplified to call it directly.
- 8 new tests: fail-closed on null policy, fail-closed on malformed JSON
  on disk (server-integration level), fail-closed on unwritable audit log
  (server-integration level + unit level), concurrent-call budget
  accounting. 24/24 passing.

Two of the review's five findings remain open by design, not silently
dropped: the maxToolCallsPerTurn session-lifetime-vs-per-turn semantic gap
(needs a real TTL/reset design) and the stdio-only enforcement scope (HTTP/
WS transport lives in a separate @claude-flow/mcp package) — both already
documented in this module's own doc comment and flagged as follow-up in
the PR/issue, not attempted here to keep this round's diff reviewable.
Getting the repo's other currently-broken workflows (CI/CD Pipeline, CVE
Audit Gate, Cross-Agent Integration Tests, V3 CI, Verification Pipeline)
green is out of scope for this PR — confirmed pre-existing and base-branch-
wide (see PR comment), unrelated to this diff.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01QqhXjtDofNNJPX3CCVQyrk

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-14 22:07:29 +00:00
..