Repository navigation
Conversation
Sensitive Change ReviewSensitive files changed:
A human reviewer with write access must approve the CURRENT head and |
GPT 6.1 Review — ✅ no blocking findingsGPT 6.1 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 (Opus 5.5) — ✅ PASSDesign-level review of Design-Verdict: PASS Confirm CI ran [DESIGN-REVIEWED] 4392cc5 Description read[DESCRIPTION-READ] 90efab491052bb7e0a95d33de8390f5cd463ccc3277bc849fc41a029a3fe000e That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
First Principles Review (Opus 5.5) — ✅ PASSPremise-level review of First-Principles-Verdict: PASS
What this change shipsInventory (7 items) — 7 justifiedIntent: give
[FIRST-PRINCIPLES-REVIEWED] 4392cc5 Description read[DESCRIPTION-READ] a24ecea742e5c941f54de90d46a8216d21e2e8e31425dc2c43336440740757e0 That sha256 names the PR description this verdict read. Recompute it with |
Opus 5.5 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
Security Scope Review -- ✅ PASSWhich legitimate operations does Verdict basis: adjudicated, zero confirmed regressions. Security scope review -- adjudicated candidates
No candidate flipped from allowed to refused. Scope-Verdict: PASS TIGHTENING: Nothing gets stricter. The ssh-to-self sandbox-escape floor ( ADJUDICATED: 24 candidates, 0 confirmed newly-refused PLATFORM COVERAGE:
The diff itself adds no new platform assumption. It moves code that already handled the Windows hosts file and adapter-address paths, and both host spellings are probed above. No assumed platform is left uncovered. [SCOPE-REVIEWED] 4392cc5 |
argv_floor.py was at 4,454 lines against the 4,500-line per-module cap, so every security change that grew it had to relocate some function first. The sandbox-escape floor (_is_ssh_to_self, the own-host name and address caches, the hosts-file verdict cache and the DNS verdict layer) was the largest cohesive cluster in it, about half the file. It now lives in security/ssh_self_floor.py, byte-identical apart from two reads of argv-floor helpers that go through the module attribute. argv_floor never imports the new module, and a liveness test pins that one-way edge. The facade routes the 24 moved names to the new owner. Tests and the boot-smoke slot table patch the new module, and the hosts-file and netlink docstrings and the security spec name it. argv_floor.py: 4,454 -> 2,210 lines. ssh_self_floor.py: 2,279 lines. Refs #10283
0d0a756 to
4392cc5
Compare
|
Thanks for this refactor, @buluoray! Main has since moved the ssh self-target floor out of |
Problem / Motivation
Goal: Give
argv_floor.pyreal headroom under the 4,500-line per-module cap by moving its largest self-contained cluster, the ssh self-target floor, into its own module with no behaviour change.src/kiro_crew/security/argv_floor.pywas at 4,454 lines onmainagainst_MODULE_LINE_CAP = 4_500(test_security_gate_liveness.py::test_no_single_module_grows_back_into_a_monolith). With that little room, a security fix that adds code to the module has to move some unrelated function out first, picked because it is easy to move that day. The PR closing #10205 did exactly that with_backtick_closer.Why it matters
This module gets multi-round lexer hardening all the time. Without headroom, each of those PRs pays a forced-relocation toll and wears down the layer boundaries the cap exists to protect. #10284 is already waiting on this headroom.
Not a goal
argv_floor.py(self-kill, subcommand, credential-mint, git-publish floors). How to partition those is the owner's decision recorded on security: argv_floor.py rides the 4,500-line anti-monolith cap; plan a deliberate split #10283. This PR is the first slice and saysRefs, notCloses._self_token_frames,_static_substitution_output) down intoshell_normalizer.py.What changed (motivation → approach → change)
# ── Sandbox-escape floorsection (lines 1245–3449 onmain:_is_ssh_to_self, the own-host name/address cache, the hosts-file verdict cache, the DNS verdict layer and the operand walk) moves intosrc/kiro_crew/security/ssh_self_floor.py. I picked this cluster first because, measured onmain, none of its 89 top-level names are used anywhere else inargv_floor.py. It is also the cluster the earlierhosts_file.pyandhost_addresses.pysplits were already peeling. Finally, none of the four open PRs that editargv_floor.py(fix(security): consolidate the git-publish scan onto a bash-faithful word lexer #17442, fix: deny a git push whose subcommand hides behind a non-plain word #17374, fix(security): quote-aware argv windows for the mint and pkill self-protection floors #16496, fix(security): keep a quoted newline inside its word on the push path #15438) touch these lines.main, with three exceptions. Two calls now read_argv_floor._self_token_frames(...)and_argv_floor._static_substitution_output(...)through the module attribute, following the patch-visibility conventionargv_flooralready uses for the reader. Two comments sayssh devboxinstead of an internal host name, because the commit leak guard refuses that name in newly added lines.ssh_self_floorimportsargv_floor, andargv_floornever imports it back, so the edge runs one way._EXPORTSnow map tossh_self_floor, and theTYPE_CHECKINGimport block is split the same way.is_deniedreads_is_ssh_to_selffrom its new owner.dashboard/server.pyimportswarm_own_host_namesfrom the new module.hosts_file.pyandhost_addresses.py, the module list indocs/system-specs/modules/security.md, and two references intesting-conventions.mdnow name the new owner.argv_floor.pydrops from 4,454 to 2,210 lines, andssh_self_floor.pyis 2,279.Backwards compatibility
Compatible: every name stays reachable on the
kiro_crew.securityfacade, now routed to the module that owns it, and the floor's verdicts are unchanged.The diff removes the moved names from
argv_floor's module namespace. These are their readers onorigin/main@ 0d885e6:Reader: src/kiro_crew/security/init.py:is_denied -- chat -- reads
_is_ssh_to_selffrom_submodule("ssh_self_floor");test_denied_commands_security.py(1316 passed)Reader: src/kiro_crew/security/init.py:_EXPORTS -- chat -- the 24 names now map to
ssh_self_floor;test_security_facade.py::test_a_facade_name_is_the_owning_submodules_objectReader: src/kiro_crew/dashboard/server.py:warm_own_host_names -- release -- imports from
ssh_self_floorReader: test/integration/test_boot_smoke.py:_KNOWN_SECOND_BOOT_CHANGES -- release -- module path updated to
kiro_crew.security.ssh_self_floorReader: test/test_denied_commands_security.py:_ssh_self_floor -- release -- patch and read targets updated
Reader: test/test_security.py:ssh_self_floor -- release -- patch and read targets updated
Reader: test/test_deny_guidance.py:_ssh_hook_result -- release -- patch and read targets updated
Reader: test/test_argv_floor_inline_and_brace_scope.py:ssh_self_floor -- release -- patch and read targets updated
A search of
test/andsrc/for every remainingargv_floor.<name>andsetattr(argv_floor, "<name>")finds each name on its target module. The one exception is_perm_verb_mention_onlyin a docstring, a stale reference that is already onmain. This search caught twomonkeypatch.setattr(_argv_floor, "open", ..., raising=False)calls that would otherwise have silently instrumented nothing. Both now target the new module.Tests
test_security_gate_liveness.py::test_the_argv_floor_never_imports_the_ssh_self_floorpins the one-way edge. Mutation check: addingfrom .ssh_self_floor import _is_ssh_to_selftoargv_floor.pyturns it red._PACKAGE_LINE_BUDGETre-pinned from 28,572 to 28,612 (the measured total). The 40 lines are the new module's docstring and its own import block. The per-file cap is untouched.TestDataConsumerGuardIsChargedPerFrameNotPerTriggerToken(test_denied_commands_security.py) now patches_data_consumer_exemptonssh_self_floortoo, because one of its four frame loops moved there. Without that patch the instrument would no longer see that loop.PYTHONPATH=<worktree>/src,-n0):test_security_facade11,test_security_single_storage2846,test_security_gate_liveness11,test_argv_floor_inline_and_brace_scope187,test_deny_guidance132,test_denied_commands_security1316 (1 skipped),test_denied_commands_hooks117,test_inline_payload_completion_regressions48,test_inline_payload_scoped_regressions134,test_redaction_mirror_parity12,test_blocked_commands_disclosure7, all passed.test/integration/test_boot_smoke.pyskips locally.test/test_security.py. On this host, pytest is SIGKILLed during collection of that file. The same thing happens on an unmodifiedmaincheckout, so it is an environment problem and not this change. CI is the evidence for that file.Manual verification
N/A — this is a pure move with no runtime-visible change.
kiro_crew.security._is_ssh_to_self is kiro_crew.security.ssh_self_floor._is_ssh_to_selfholds, andis_denied("ssh localhost")still refuses withrule=sandbox-escape-ssh-self. Windows and macOS address paths are moved code only; they were not exercised on this Linux host.Related Issues
Refs #10283
Decision for the owner
#10283 asks how
argv_floor.pyshould be partitioned into submodules, and when that split should happen relative to the in-flight floor PRs. This draft does not commit to a partition for the rest of the module. It moves only the ssh self-target floor, which is about half the file, has no internal callers left inargv_floor, and is untouched by the open floor PRs, so the other submodule boundaries stay your call.Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)Contribution License Agreement
Contributions are accepted under the project's Apache-2.0 license; see the Licensing section of CONTRIBUTING.md.