fix: detect and audit tier downgrades in nested-sandbox passthrough (#691) - #2406
Conversation
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsThe changed logic is sound: dict lookups are all guarded ( No findings. [OPUS-REVIEWED] 3478fd7 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSAdvisory design-level review of Design-Verdict: PASS A real audit-visibility gap closed with a proportionate mitigation: tier recorded, downgrade audited/warned, partial scrub enforced, fail-closed correctly rejected with cited evidence. Suggestions
[DESIGN-REVIEWED] 3478fd7 |
6789888 to
743789d
Compare
…691) The nested-sandbox passthrough in wrap_argv() returned argv unchanged regardless of the tier the caller requested, and the in-sandbox marker recorded only that a sandbox was active, not which tier built it. An in-sandbox caller requesting strict under a standard outer sandbox therefore silently ran at standard, invisible in logs and in the SEL passthrough audit event. Both launcher sites now export KIROCREW_SANDBOX_LEVEL beside the ACTIVE marker, with the same non-droppable placement (after the Linux launcher env-scrub loop; after the macOS env -u flags). The passthrough resolves the requested mode through a shared _mode_to_level() helper, compares it against the active tier (standard < cc < strict; absent/unrecognized reads as unknown, which proves nothing and never crashes), and: - records requested_tier, active_tier, and tier_downgrade in the SEL nested_sandbox_passthrough metadata so a downgrade is visible in the audit log rather than inferred - emits a per-call SECURITY warning naming both tiers and the executable on a proven downgrade - prefixes the passthrough argv with the requested tier's env-scrub delta (env -u agent-denied credential keys) — the one slice of the stricter tier enforceable without a nested wrap The passthrough itself stays: a nested re-wrap is impossible by design (Linux seccomp denies unshare; macOS Seatbelt refuses sandbox_apply with EPERM), and refusing the downgrade is known to break Dev Fleet Sync/Provision. Closes #691
743789d to
3478fd7
Compare
…irodotdev#691) (kirodotdev#2406) The nested-sandbox passthrough in wrap_argv() returned argv unchanged regardless of the tier the caller requested, and the in-sandbox marker recorded only that a sandbox was active, not which tier built it. An in-sandbox caller requesting strict under a standard outer sandbox therefore silently ran at standard, invisible in logs and in the SEL passthrough audit event. Both launcher sites now export KIROCREW_SANDBOX_LEVEL beside the ACTIVE marker, with the same non-droppable placement (after the Linux launcher env-scrub loop; after the macOS env -u flags). The passthrough resolves the requested mode through a shared _mode_to_level() helper, compares it against the active tier (standard < cc < strict; absent/unrecognized reads as unknown, which proves nothing and never crashes), and: - records requested_tier, active_tier, and tier_downgrade in the SEL nested_sandbox_passthrough metadata so a downgrade is visible in the audit log rather than inferred - emits a per-call SECURITY warning naming both tiers and the executable on a proven downgrade - prefixes the passthrough argv with the requested tier's env-scrub delta (env -u agent-denied credential keys) — the one slice of the stricter tier enforceable without a nested wrap The passthrough itself stays: a nested re-wrap is impossible by design (Linux seccomp denies unshare; macOS Seatbelt refuses sandbox_apply with EPERM), and refusing the downgrade is known to break Dev Fleet Sync/Provision. Closes kirodotdev#691 Co-authored-by: bolichen97 <bolichen97@users.noreply.github.com>
Summary
The nested-sandbox passthrough in
wrap_argv()was tier-blind: theKIROCREW_SANDBOX_ACTIVEmarker proves a Kiro Crew sandbox is active but not which tier built it, so an in-sandbox caller requestingstrictunder astandardouter sandbox silently ran atstandard— invisible in logs and in the SEL passthrough audit (which recorded onlymode). Closes #691.What changed
KIROCREW_SANDBOX_LEVEL=<standard|cc|strict>beside the ACTIVE marker, with the same non-droppable placement: the Linux launcher sets it immediately after its env-scrub loop; the macOSenvprefix carries it after the-uflags.cli.main()drops an inherited copy at the same site where it drops the marker itself (a stale ancestor value would corrupt a descendant's downgrade audit)._mode_to_level()helper (also replacing the inline mapping at the backend-wrap site) resolves the requested tier; ordinalsstandard(1) < cc(2) < strict(3). Absent/unrecognized level (older-build outer tree) reads asunknown— no ordinal claim, passthrough unaffected, never crashes.nested_sandbox_passthroughmetadata now carriesrequested_tier,active_tier,tier_known, andtier_downgrade, so a downgrade is visible in the audit log rather than inferred (tier_knownseparates proven no downgrade from unprovable).SECURITY:warning names both tiers and argv[0], and the passthrough argv is prefixed with the requested tier'senv -uscrub — the one part ofstrictenforceable without a nested wrap (agent-denied credential keys astandardlauncher never scrubbed). Theenvbinary is resolved only at a trusted absolute path (_unset_env_argv); if none exists the scrub is skipped with a warning rather than resolvingenvthrough a caller-influenced PATH.Tests
test/test_sandbox_nested_tier.py(new, 19 tests): launcher templates the level per tier; level export sits after the scrub loop; macOS env assignment positioned after-uflags; downgrade detected + audited + warned + scrubbed via absolute env path; no-downgrade / same-tier / unknown / unrecognized-value / SEL-failure / no-trusted-env-binary paths.test/test_cli.py: marker-drop test extended to pin the LEVEL pop.test/test_terminal_commands.py: probe env allowlist learns the new sandbox-injected name.docs/system-specs/modules/security.mdpassthrough section updated in the same commit.Verification
envPATH lookup — fixed via trusted absolute resolver), 1 Blocking (inherited LEVEL not dropped incli.main()— fixed), 4 advisories applied. One advisory (apply scrub onunknowntier) declined: the issue scope specifies the passthrough must be unaffected when no downgrade can be proven;tier_knownmetadata added instead.