fix(security): block shell-collapse rm -rf / spellings at the hardline floor

rm -rf //, /., /./, /.. and //* all resolve to / in the shell but slipped
past the root-filesystem hardline pattern, whose target group only matched
the literal / and /* tokens. They fell to the softer DANGEROUS_PATTERNS
'delete in root path' rule, which --yolo / approvals.mode=off / cron
approve-mode are designed to bypass — leaving the one unconditional floor
open to a full root wipe under yolo.

Broaden the root token from '/|/\\*|/ \\*' to '/[/.]*\\**' inside
_hardline_rm_path so any root-anchored path whose components collapse back
to / (repeated slashes plus ./.. segments) with an optional trailing glob
is caught. A trailing real segment (/tmp, /home, /.ssh) still fails to
match and stays with the softer rules.

Co-authored-by: kernel-t1 <214165399+kernel-t1@users.noreply.github.com>
This commit is contained in:
teknium1 2026-07-01 02:29:09 -07:00 committed by Teknium
parent d15a288812
commit 51feecc2b1
2 changed files with 55 additions and 1 deletions

View file

@ -31,6 +31,18 @@ _HARDLINE_BLOCK = [
# rm -rf targeting root / system dirs / home
"rm -rf /",
"rm -rf /*",
# Shell-equivalent spellings of "rm -rf /": repeated slashes and
# current/parent-dir segments all collapse back to root, so they must
# hit the hardline floor too (regression: these used to slip through the
# root pattern's target group and fall to the softer DANGEROUS_PATTERNS
# rule, which --yolo / approvals.mode=off / cron approve-mode bypass).
"rm -rf //",
"rm -rf /.",
"rm -rf /./",
"rm -rf /..",
"rm -rf //*",
"rm -fr /./",
"ls && rm -rf //",
"rm -rf /home",
"rm -rf /home/*",
"rm -rf /etc",
@ -329,6 +341,37 @@ def test_yolo_env_var_cannot_bypass_hardline(clean_session, monkeypatch):
assert r2.get("hardline") is True
def test_root_collapse_forms_cannot_bypass_hardline(clean_session, monkeypatch):
"""Shell-equivalent spellings of "rm -rf /" stay blocked under yolo.
"//", "/.", "/./", "/..", "//*" all collapse to the root filesystem in
the shell. They previously matched only the softer DANGEROUS_PATTERNS
rule, which yolo bypasses leaving the hardline floor open to a full
root wipe under --yolo / approvals.mode=off / cron approve-mode.
"""
monkeypatch.setenv("HERMES_YOLO_MODE", "1")
for cmd in ["rm -rf //", "rm -rf /.", "rm -rf /./", "rm -rf /..", "rm -rf //*"]:
is_hl, _ = detect_hardline_command(cmd)
assert is_hl, f"{cmd!r} should be hardline-blocked"
result = check_all_command_guards(cmd, "local")
assert result["approved"] is False, f"yolo leaked hardline on {cmd!r}"
assert result.get("hardline") is True
def test_root_collapse_pattern_leaves_real_paths_alone(clean_session):
"""The broadened root token must not over-match real trailing segments.
A path with a real component after the root-collapse prefix (/tmp,
/home/user/x, /.ssh, ./build) is recoverable-or-legitimate and must NOT
be pulled onto the hardline floor by the "collapse to /" broadening.
"""
for cmd in ["rm -rf /tmp", "rm -rf /home/user/x", "rm -rf /.ssh",
"rm -rf /.config", "rm -rf ./build", "rm -rf /opt/foo"]:
is_hl, _ = detect_hardline_command(cmd)
assert not is_hl, f"{cmd!r} must not be hardline-blocked (over-match)"
def test_line_continuation_root_wipe_cannot_bypass_hardline(clean_session, monkeypatch):
"""A line-continuation root wipe must stay blocked even under yolo.