diff --git a/tests/tools/test_approval.py b/tests/tools/test_approval.py index a752b68d7..476fd0d32 100644 --- a/tests/tools/test_approval.py +++ b/tests/tools/test_approval.py @@ -460,6 +460,24 @@ class TestProjectSensitiveCopyPattern: assert key is not None assert "project env/config" in desc.lower() + def test_cp_absolute_path_to_dotenv_requires_approval(self): + # Regression: the real-world bug report was `cp /opt/data/.env.local /opt/data/.env`. + # The regex must cover absolute paths, not just `./` / bare relative paths. + dangerous, key, desc = detect_dangerous_command( + "cp /opt/data/.env.local /opt/data/.env" + ) + assert dangerous is True + assert key is not None + assert "project env/config" in desc.lower() + + def test_redirect_absolute_path_to_dotenv_requires_approval(self): + dangerous, key, desc = detect_dangerous_command( + "cat /opt/data/.env.local > /opt/data/.env" + ) + assert dangerous is True + assert key is not None + assert "project env/config" in desc.lower() + def test_mv_to_nested_config_yaml_requires_approval(self): dangerous, key, desc = detect_dangerous_command("mv tmp/generated.yaml config/config.yaml") assert dangerous is True diff --git a/tools/approval.py b/tools/approval.py index d88b3e877..258f66b6e 100644 --- a/tools/approval.py +++ b/tools/approval.py @@ -63,8 +63,8 @@ _HERMES_ENV_PATH = ( r'(?:\$hermes_home|\$\{hermes_home\})/)' r'\.env\b' ) -_PROJECT_ENV_PATH = r'(?:(?:\.{1,2}/)?(?:[^\s/"\'`]+/)*\.env(?:\.[^/\s"\'`]+)*)' -_PROJECT_CONFIG_PATH = r'(?:(?:\.{1,2}/)?(?:[^\s/"\'`]+/)*config\.yaml)' +_PROJECT_ENV_PATH = r'(?:(?:/|\.{1,2}/)?(?:[^\s/"\'`]+/)*\.env(?:\.[^/\s"\'`]+)*)' +_PROJECT_CONFIG_PATH = r'(?:(?:/|\.{1,2}/)?(?:[^\s/"\'`]+/)*config\.yaml)' _SENSITIVE_WRITE_TARGET = ( r'(?:/etc/|/dev/sd|' rf'{_SSH_SENSITIVE_PATH}|'