What is the problem?
agent.sandbox_allow_unsandboxed_exec is a single boolean that conflates two very different decisions:
- "let Kiro Crew's own managed MCP servers run" —
kirocrew-core, kirocrew-cron, kirocrew-computer. First-party binaries, argv fully controlled by Kiro Crew, no agent- or repo-supplied input in the command line.
- "unconfine the
mode="strict" hostile-input paths" — dashboard/handlers/worktree.py (a repo-controlled include.path could make git read ~/.aws/credentials) and the Papyrus latex.py / gitops.py chokepoints (a crafted .tex could typeset a secret into a PDF).
On a host with no sandbox backend — every Windows host, and any Linux kernel that refuses user namespaces — detect_backend() returns "none", so wrap_argv() fail-closes for both classes. Getting (1) working therefore currently requires granting (2) as well.
Why this issue matters to the user
A Windows user who just wants working MCP tooling has to accept unconfined execution for the paths that actually take hostile input. #1527 made that choice informed and consented (a kirocrew setup prompt naming ~/.aws / ~/.ssh, default no, SEL-audited) rather than implicit, which is the right posture — but the granularity problem is unchanged: there is no way to say yes to (1) and no to (2).
Both blocking reviewers on that PR objected specifically on the (2) paths, which is the evidence that the distinction is real and worth encoding.
Proposed direction (not yet designed)
Add a caller-class axis to wrap_argv() distinguishing who chose the argv from what the sandbox hides (the existing mode). First-party fixed-argv spawns could proceed on a backend-less host while agent/repo-controlled callers keep fail-closing, making the current single opt-in unnecessary for case (1).
Constraints that make this its own change rather than a rider:
wrap_argv() has ~30 call sites; every one needs classifying, and a mistake here is a security regression, not a bug.
- "First-party" has to be enforced structurally, not by trusting a keyword argument a future caller can copy-paste. A spawn-audit ratchet (
test_every_spawn_is_routed_or_allowlisted already exists in this repo) is the natural enforcement point.
- Governance interaction:
_clamp_sandbox_mode() lets a policy require a minimum tier. A carve-out must not become a way to duck that floor.
- The SEL audit story needs deciding: today a refusal is
denied and a nested passthrough is allowed; a first-party unconfined spawn is a third case.
Blocking relationship
Per the Design Review on #1527, this granularity should land before the same consent pattern is replicated in the dashboard surface — otherwise the coarse boolean gets a second, more prominent front door.
Related
What is the problem?
agent.sandbox_allow_unsandboxed_execis a single boolean that conflates two very different decisions:kirocrew-core,kirocrew-cron,kirocrew-computer. First-party binaries, argv fully controlled by Kiro Crew, no agent- or repo-supplied input in the command line.mode="strict"hostile-input paths" —dashboard/handlers/worktree.py(a repo-controlledinclude.pathcould makegitread~/.aws/credentials) and the Papyruslatex.py/gitops.pychokepoints (a crafted.texcould typeset a secret into a PDF).On a host with no sandbox backend — every Windows host, and any Linux kernel that refuses user namespaces —
detect_backend()returns"none", sowrap_argv()fail-closes for both classes. Getting (1) working therefore currently requires granting (2) as well.Why this issue matters to the user
A Windows user who just wants working MCP tooling has to accept unconfined execution for the paths that actually take hostile input. #1527 made that choice informed and consented (a
kirocrew setupprompt naming~/.aws/~/.ssh, default no, SEL-audited) rather than implicit, which is the right posture — but the granularity problem is unchanged: there is no way to say yes to (1) and no to (2).Both blocking reviewers on that PR objected specifically on the (2) paths, which is the evidence that the distinction is real and worth encoding.
Proposed direction (not yet designed)
Add a caller-class axis to
wrap_argv()distinguishing who chose the argv from what the sandbox hides (the existingmode). First-party fixed-argv spawns could proceed on a backend-less host while agent/repo-controlled callers keep fail-closing, making the current single opt-in unnecessary for case (1).Constraints that make this its own change rather than a rider:
wrap_argv()has ~30 call sites; every one needs classifying, and a mistake here is a security regression, not a bug.test_every_spawn_is_routed_or_allowlistedalready exists in this repo) is the natural enforcement point._clamp_sandbox_mode()lets a policy require a minimum tier. A carve-out must not become a way to duck that floor.deniedand a nested passthrough isallowed; a first-party unconfined spawn is a third case.Blocking relationship
Per the Design Review on #1527, this granularity should land before the same consent pattern is replicated in the dashboard surface — otherwise the coarse boolean gets a second, more prominent front door.
Related