diff --git a/tests/tools/test_approval.py b/tests/tools/test_approval.py index 1ebb3efe6d98d..29687ccb89059 100644 --- a/tests/tools/test_approval.py +++ b/tests/tools/test_approval.py @@ -128,6 +128,32 @@ def test_nonrecursive_verification_artifact_cleanup_is_not_dangerous(self): None, ) + def test_top_level_temp_dir_alias_is_exempted_via_raw_path(self, tmp_path): + """A top-level tempdir alias (e.g. macOS ``/tmp`` -> ``/private/tmp``) + must be exempted using the raw, unresolved path, since that's the + literal text a real cleanup command carries — realpath() alone would + never match it.""" + real_root_temp = tmp_path / "private_tmp" + real_root_temp.mkdir() + basename = "hermes-verify-example.py" + orig_realpath = os.path.realpath + + def fake_realpath(path): + text = str(path) + if text == "/tmp": + return str(real_root_temp) + if text.startswith("/tmp/"): + return str(real_root_temp / text[len("/tmp/"):]) + return orig_realpath(text) + + with mock_patch("tempfile.gettempdir", return_value="/tmp"), \ + mock_patch("os.path.realpath", side_effect=fake_realpath): + assert detect_dangerous_command(f"rm -f /tmp/{basename}") == ( + False, + None, + None, + ) + def test_symlinked_temp_dir_only_exempts_canonical_target(self, tmp_path): real_temp = tmp_path / "real-temp" real_temp.mkdir() diff --git a/tools/approval.py b/tools/approval.py index 9962bf851d8bf..ad0a93f27b544 100644 --- a/tools/approval.py +++ b/tools/approval.py @@ -1974,9 +1974,14 @@ def _is_verification_artifact_cleanup(command: str) -> bool: return False operand = argv[2] - temp_dir = os.path.realpath(tempfile.gettempdir()) + raw_temp_dir = os.path.abspath(tempfile.gettempdir()) + temp_dir = os.path.realpath(raw_temp_dir) basename = os.path.basename(operand) - if operand != os.path.join(temp_dir, basename): + canonical_operand = os.path.join(temp_dir, basename) + raw_operand = os.path.join(raw_temp_dir, basename) + if operand != canonical_operand and not ( + operand == raw_operand and os.path.dirname(raw_temp_dir) == os.path.abspath(os.sep) + ): return False target = os.path.realpath(operand)