Approval regex over-flags absolute paths as dangerous deletes
Cluster 18083 · agent. Data as of
2 open items. Fix merged in #63120 on 12 Jul 2026. Check it covers your case.
The short answer
- Known?
- Yes. 9 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #63120. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PR #63120. That is separate from the fix event. - Still open
- 1 issue and 1 PR.
Next step: read #63120, then check whether your report is the one still open.
Read #63120Pick up a Lab taskA DANGEROUS_PATTERNS regex incorrectly matches ordinary absolute paths as root-delete operations, prompting several fixes to tighten the pattern to genuine system-root targets and carve out exceptions for verifier and Hermes-owned temp file cleanup.
Open issues1
- Issue#18083Over-broad DANGEROUS_PATTERNS regex flags every absolute path as 'delete in root path'
Open PRs1
- PR#39629fix(approval): avoid flagging ordinary absolute paths as root deletes (#18083)
Fixes on record
-
#63120 fix(approval): allow verifier temp cleanup
files:
agent/verification_stop.py,scripts/release.py,tests/agent/test_verification_stop.py,tests/tools/test_approval.py,tools/approval.pyShow the diff
-
agent/verification_stop.py@@ -289,7 +289,7 @@ def build_verify_on_stop_nudge( + "), read any failure, repair the code, and summarize what passed." ) else: - temp_dir = tempfile.gettempdir() + temp_dir = os.path.realpath(tempfile.gettempdir()) command_instruction = ( "No canonical test/lint/build command was detected. Create a focused " f"temporary verification script under `{temp_dir}` using an OS-safe " -
scripts/release.py@@ -45,6 +45,7 @@ # Auto-extracted from noreply emails + manual overrides AUTHOR_MAP = { + "[email protected]": "ansel-f", # PR #62388 salvage (approval: allow exact verifier temp cleanup without broadening rm safety boundary) "[redacted:email]": "professorpalmer", # PR #48591 salvage (sessions: CLI workspace filter + restore-cwd-on-resume) "[redacted:email]": "Supersynergy", # PR #59241 salvage (desktop: workspace path status-bar action) "[redacted:email]": "esthonjr", # PR #61950 salvage (desktop: legacy non-git workspace grouping + Windows path identity) -
tests/agent/test_verification_stop.py@@ -260,6 +260,27 @@ def test_no_suite_nudge_requests_temp_script(tmp_path, monkeypatch): assert "creative UI/visual work" in nudge +def test_no_suite_nudge_uses_canonical_temp_dir(tmp_path, monkeypatch): + monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes")) + project = tmp_path / "project" + project.mkdir() + (project / "package.json").write_text("{}", encoding="utf-8") + real_temp = tmp_path / "real-temp" + real_temp.mkdir() + linked_temp = tmp_path / "linked-temp" + linked_temp.symlink_to(real_temp, target_is_directory=True) + monkeypatch.setattr(tempfile, "gettempdir", lambda: str(linked_temp)) + + nudge = build_verify_on_stop_nudge( + session_id="s1", + changed_paths=[str(project / "src" / "app.ts")], + ) + + assert nudge is not None + assert str(real_temp) in nudge + assert str(linked_temp) not in nudge + + def test_verify_guidance_can_be_disabled(tmp_path, monkeypatch): monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes")) _node_project(tmp_path) -
tests/tools/test_approval.py@@ -2,6 +2,7 @@ import ast import os +import tempfile import threading import time from pathlib import Path @@ -116,6 +117,53 @@ def test_rm_recursive_long_flag(self): assert key is not None assert "delete" in desc.lower() + def test_nonrecursive_verification_artifact_cleanup_is_not_dangerous(self): + with mock_patch("tempfile.gettempdir", return_value="/tmp"): + for prefix in ("hermes-verify-", "hermes-ad-hoc-"): + assert detect_dangerous_command(f"rm -f /tmp/{prefix}example.py") == ( + False, + None, + None, + ) + + def test_symlinked_temp_dir_only_exempts_canonical_target(self, tmp_path): + real_temp = tmp_path / "real-temp" + real_temp.mkdir() + linked_temp = tmp_path / "linked-temp" + linked_temp.symlink_to(real_temp, target_is_directory=True) + basename = "hermes-verify-example.py" + + with mock_patch("tempfile.gettempdir", return_value=str(linked_temp)): + assert detect_dangerous_command(f"rm -f {linked_temp / basename}")[0] is True + assert detect_dangerous_command(f"rm -f {real_temp / basename}") == ( + False, + None, + None, + ) + + def test_verification_cleanup_exemption_rejects_broader_deletions(self): + commands = ( + "rm -rf /tmp/hermes-verify-example.py", + "rm -f /tmp/hermes-verify-example.py /tmp/other.py", + "rm -f /tmp/nested/hermes-verify-example.py", + "rm -f /tmp/nested/../hermes-verify-example.py", + "rm -f /tmp/./hermes-verify-example.py", + "rm -f /tmp//hermes-verify-example.py", + "rm -f /tmp/a/../../tmp/hermes-verify-example.py", + "rm -f /var/tmp/hermes-verify-example.py", + "rm -f /tmp/unrelated.py", + "rm -f /tmp/hermes-verify-*", + "rm -f /tmp/hermes-verify-$(touch>/tmp/pwned).py", + "rm -f /tmp/hermes-ad-hoc-`touch>/tmp/pwned`.py", + "rm -f /tmp/hermes-verify-example.py; touch /tmp/pwned", + ) + with mock_patch("tempfile.gettempdir", return_value="/tmp"): + for command in commands: + is_dangerous, key, desc = detect_dangerous_command(command) + assert is_dangerous is True, command + assert key is not None, command + assert "delete" in desc.lower(), command + class TestWindowsShellDestructiveCommands: def test_cmd_del_requires_approval(self): -
tools/approval.py@@ -17,6 +17,7 @@ import re import shlex import sys +import tempfile import threading import time import unicodedata @@ -1383,12 +1384,36 @@ def _command_detection_variants(command: str): yield variant +def _is_verification_artifact_cleanup(command: str) -> bool: + """Return whether *command* only removes one Hermes ad-hoc temp script.""" + try: + argv = shlex.split(command, posix=True) + except ValueError: + return False + if len(argv) != 3 or argv[0] != "rm" or argv[1] != "-f": + return False + + operand = argv[2] + temp_dir = os.path.realpath(tempfile.gettempdir()) + basename = os.path.basename(operand) + if operand != os.path.join(temp_dir, basename): + return False + + target = os.path.realpath(operand) + if os.path.dirname(target) != temp_dir: + return False + return re.fullmatch(r"hermes-(?:verify|ad-hoc)-[A-Za-z0-9_.-]+", basename) is not None + + def detect_dangerous_command(command: str) -> tuple: """Check if a command matches any dangerous patterns. Returns: (is_dangerous, pattern_key, description) or (False, None, None) """ + if _is_verification_artifact_cleanup(command): + return (False, None, None) + for command_variant in _command_detection_variants(command): command_lower = command_variant.lower() for pattern_re, description in DANGEROUS_PATTERNS_COMPILED:
-
5 PRs closed without merging
From the close comments: done elsewhere 2 · PR volume cleanup 1 · merged via another change 1 · superseded by another PR 1
-
#18089 fix(approval): tighten 'delete in root path' regex to system-root targets only · PR volume cleanup →
#26829,#18083 -
#18533 fix(approval): tighten 'delete in root path' regex to system roots only · superseded by another PR →
#18089,#12584,#12592 -
#62379 fix(tools): allow cleanup of Hermes-owned temp files without approval · done elsewhere →
#63120,#62388 -
#62388 fix(approval): allow verifier temp cleanup · merged via another change →
#63120 -
#62400 fix(agent): instruct background review fork to call skill_view before patching · done elsewhere →
#60331,#62400,#62377
Closed issues · 1
- Closed#62377Verify-on-stop mandates temp cleanup that dangerous-command approval blocks
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.20: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- agent
- Merged PRs
- #63120
- Closed issues
- 1
- Attempts
- 5 PRs closed without merging
- Fix link checked
- 12 Jul 2026