fix(approval): require exact ./.. segments in the root-collapse hardline token (#56179)

Follow-up to #56236: the broadened root token /[/.]*\** treats any run of
dots after the root slash as a collapse spelling, so a literal root-level
directory named '...' (rm -rf /...) was unconditionally hardline-blocked
with no approval path. Tighten the token to /(?:(?:\.\.?)?/)*(?:\.\.?)?\**
so each inter-slash segment must be exactly '.' or '..' — all real collapse
spellings (//, /., /./, /.., //*, ///, /../..) stay on the hardline floor
while literal dot-run dirs fall through to the softer DANGEROUS_PATTERNS
rules like every other real path.
This commit is contained in:
Teknium 2026-07-05 02:24:00 -07:00 committed by GitHub
parent cb6c47af08
commit ebfc49c4d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 10 deletions

View file

@ -150,6 +150,16 @@ _HARDLINE_ALLOW = [
"rm -rf $HOME/tmp",
"rm foo.txt",
"rm -rf some/path",
# Literal root-level directories that only LOOK like root-collapse
# spellings. Each inter-slash segment must be exactly "." or ".." to
# count as a collapse back to "/" — "/..." is a dir literally named
# "..." and "/.foo" is an ordinary root dotfile. These must NOT be
# swept into the "recursive delete of root filesystem" hardline rule
# (regression guard for the collapse-spelling tightening).
"rm -rf /...",
"rm -rf /....",
"rm -rf /.foo",
"rm -rf /.config/foo",
# A dangerous-looking command embedded as a quoted *argument* to another
# command must not trip the floor: the path is immediately followed by a
# closing quote with no matching opening quote of its own, so the
@ -401,7 +411,8 @@ def test_root_collapse_pattern_leaves_real_paths_alone(clean_session):
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"]:
"rm -rf /.config", "rm -rf ./build", "rm -rf /opt/foo",
"rm -rf /...", "rm -rf /....", "rm -rf /.foo"]:
is_hl, _ = detect_hardline_command(cmd)
assert not is_hl, f"{cmd!r} must not be hardline-blocked (over-match)"

View file

@ -370,15 +370,16 @@ HARDLINE_PATTERNS = [
#
# The path token matches any root-anchored path whose components collapse
# back to "/" in the shell: a bare "/", repeated slashes ("//"), and
# "."/".." current/parent segments ("/.", "/./", "/..") all resolve to
# root, optionally followed by a trailing glob ("/*", "//*"). The earlier
# "/|/\*|/ \*" form only caught the literal "/" / "/*" spellings, so
# `rm -rf //`, `rm -rf /.`, `rm -rf /./`, `rm -rf /..` and `rm -rf //*`
# silently slipped the hardline floor and executed under --yolo /
# approvals.mode=off / cron approve-mode. A trailing real segment
# (e.g. "/tmp", "/home", "/.ssh") still fails to match here and stays
# with the softer DANGEROUS_PATTERNS / system-directory rules.
(_RM_FLAG_PREFIX + _hardline_rm_path(r'/[/.]*\**'), "recursive delete of root filesystem"),
# "."/".." current/parent segments ("/.", "/./", "/..", "/../..") all
# resolve to root, optionally followed by a trailing glob ("/*", "//*").
# Each inter-slash segment must be exactly "." or "..", so a longer dot
# run or any real name is a literal directory, NOT root — "/tmp", "/home",
# "/.ssh", "/.config" and even "/..." (a dir literally named "...") fall
# through to the softer DANGEROUS_PATTERNS / system-directory rules
# instead of being unconditionally hardline-blocked. The explicit "/ \*"
# alt preserves the slash-space-glob spelling (`rm -rf / *`, which the
# shell sees as two args: "/" plus the "*" glob).
(_RM_FLAG_PREFIX + _hardline_rm_path(r'/(?:(?:\.\.?)?/)*(?:\.\.?)?\**|/ \*'), "recursive delete of root filesystem"),
(_RM_FLAG_PREFIX + _hardline_rm_path(_HARDLINE_SYSTEM_DIRS), "recursive delete of system directory"),
(_RM_FLAG_PREFIX + _hardline_rm_path(r'(?:~|\$\{?HOME\}?)(?:/?|/\*)?'), "recursive delete of home directory"),
# Filesystem format