Skip to content

Swarm-review follow-ups for the #76 execution layer: security hardening, Windows shims, correctness nits, named test gaps #88

Description

@robertelee78

Summary

Consolidated follow-ups from the six-leg swarm review of the #76 final head (6347d1a) — full report + per-finding dispositions on #76. Related: #67#84 (delivery), PR #85 + #86 + #87 (already-remediated findings), #71 (the capability-registry direction several of these feed).

Security

  • S1 (HIGH) — repo-weaponization threat model is undocumented and the opencode permission-abort boundary is event-driven, not policy-enforced. An owned opencode serve runs with cwd = target repo + full process.env; opencode merges the project's opencode.json, so a malicious repo can ship {"permission":{"bash":"allow"}} (no permission.updated event ever fires → the adapter's abort never trips) and hostile mcp entries, while AGENTS.md content flows into the worker prompt. claude/codex workers analogously inherit the repo's .claude/settings.json under the user's trust posture. Options: pin a serve-time restrictive permission policy for owned servers (e.g. --permission scoped to edit-only within the cwd), or document the "ak run only in repos you trust with full user privileges" contract in ADR-0018 + run help. (src/lib/execution/opencode.mjs:158,258-262,285-287)
  • S2 (MED) — port-reservation TOCTOU can hand a rogue local process the per-run Basic credential, the full worker prompt, and the ability to fabricate terminal SSE evidence. Probe-bind-close-rebind (defaultReservePort) races under maxConcurrent. Harden: watch child.exitCode during waitForHealth, or serve --port 0 and parse the bound port from child stdout. (src/lib/execution/opencode.mjs:21-30,122-133,255-258)
  • S3 (LOW) — SSE per-line buffer is unbounded (memory-DoS amplifier; owned-server context). Cap like the 256 KB subprocess cap. (opencode.mjs:167-169)

Execution correctness

  • Windows shim-awareness gap in the execution layer (two reviewers converged): resolveShim (PATHEXT-aware) is used by have() but never exported or applied at launch; bare spawnFn('claude'|'codex'|'opencode') and 'codex' missing from CMD_SHIMS. Windows: readiness passes, launch ENOENTs. Fix: export + apply in subprocess/opencode launch paths, add codex to CMD_SHIMS, add a win32 spawn assertion. (src/lib/exec.mjs:21-38, subprocess.mjs:99, opencode.mjs:258)
  • maxTurns is silently dropped by ak run — templates carry per-node caps (6–15) that claude-flow-codex dual honored; no execution adapter reads them. Translate to --max-turns in claude/codex argumentsFor (opencode has no equivalent — document) or drop the field. (routing.mjs:398-424,452)
  • $-pattern substitution corrupts the opencode worker prompttemplate.replace('{{task}}', worker.prompt) treats $& $` $' as metachars. Use replacer functions. (opencode.mjs:72)
  • --timeout has no upper bound — values >2³¹−1 ms are clamped by Node to ~1 ms, turning a huge timeout into an instant timeout for every worker. Cap in positiveInt. (run.mjs:37-41)
  • Persisted dualRouting is never validated at load — a hand-edited kit.json with {model:123} crashes plan printing far from the cause. Schema-check in buildRunPlan. (run.mjs, routing.mjs)
  • cancel/cleanup HTTP is unbounded/abort + /instance/dispose have no timeout (a wedged server hangs the runner's timeout branch). Same class as court B4 (launch-phase fetches); fix together. (opencode.mjs:299,307)
  • No construction invariant tying canRouteActivities hosts to EXECUTION_ADAPTERS keys — a routable host with no execution adapter loads, validates, and materializes, then every worker fails cli_unavailable at runtime. Same failure class as court A3, one axis over; exactly Architecture: capability-driven host, provider, binding, and observability adapters #71's trap. (execution/adapters.mjs:7-11)

Test coverage gaps (verified — each names the missing assertion)

  • Session-isolation: foreign sessionID SSE events must be ignored (currently untested — deleting the filter terminates on any session's idle). (opencode-execution.test.mjs)
  • Plan-validation guards: duplicate id / unknown dep / self-dep / bad maxConcurrent / dependency-cycle throw. (execution-runner.test.mjs)
  • Materialization capability gate: "cannot materialize… requires canRouteActivities". (routing.test.mjs)
  • Readiness-false → cli_unavailable path. (runner suites)
  • KILL-fallback signal assertion (['SIGTERM','SIGKILL'], not just the orphan verdict). (opencode-execution.test.mjs:120-137)
  • Smaller: auth_required/cancelled categories, session.error mapping, SSE multi-chunk splits, run.mjs exit-1/2 paths, schema type rejections + frozen immutability.

Docs/migration notes (LOW)

Acceptance

  • S1 disposition: policy pinned for owned servers, or the trust contract documented (ADR-0018 + run help).
  • S2/S3 hardening landed.
  • Windows shim path fixed + asserted.
  • maxTurns / $-prompt / timeout-clamp / dualRouting-load-validation / cancel-cleanup bounds fixed.
  • Execution-adapter construction invariant added.
  • Named test gaps filled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions