Skip to content

security-adversarial: score.ts/oia-manifest.ts share threat-model.ts's pre-#276 narrow MCP-detection bug #280

Description

@ruvnet

Found by the independent Round 2 critic while re-verifying PR #276 (2026-09-03 Dream Cycle, dream/2026-09-03-security-adversarial).

Finding

packages/create-agent-harness/src/threat-model.ts had a narrow mcpInUse check that missed .claude/settings.json's mcpServers key (only recognized .harness/mcp-policy.json and .mcp.json) — fixed in #276 by routing detection through mcp-scan.ts's scanMcp().mcpEnabled, which now ORs all three surfaces.

Two other call sites have the identical bug and were not touched by #276, since they don't call scanMcp() at all:

  • packages/create-agent-harness/src/score.tsscoreMcpSafety()'s hasMcp = policy != null || fileExists(dir, '.mcp.json')
  • packages/create-agent-harness/src/oia-manifest.tsreadHarnessProfile()'s hasMcp = mcpPolicy != null || existsSync('.mcp.json')

Reproduced live by the critic against a .claude/settings.json-mcpServers-only fixture (real built CLI, not mocks):

  • harness score --jsonmcpRisk: "None" (scored as the safest possible posture — false negative)
  • harness oia-manifest --jsonadjacentStandards.mcp.mode: "off", security.implementation: "ADR-022 (MCP off)" (also a false negative)

Both are security-relevant: score.ts feeds publish-readiness scoring, oia-manifest.ts feeds the ADR-034 cross-cutting manifest layer.

Suggested fix

Same mechanical pattern as #276: swap each hasMcp/mcpPolicy != null || existsSync('.mcp.json') expression for scanMcp(dir).mcpEnabled. Neither file imports mcp-scan.ts today, so there's no circular-import obstacle. Small, one-conceptual-change diff — good candidate for a future security-adversarial night, following #276's own precedent (2026-08-28's PR #243 flagged the original threat-model.ts gap; #276 closed it two nights later).

Also noted (lower priority, likely separate scope)

ADR-022 (docs/adrs/ADR-022-mcp-primitive.md) names additional per-host MCP registration surfaces (.codex/config.toml, .openclaw/openclaw.json, Hermes optional-mcps/<name>.json) that scanMcp()/threat-model.ts don't check at all — a pre-existing, architecturally larger gap (different permission-string formats per host), not introduced or regressed by #276.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions