|
| 1 | +"""Boundary shapes for the shared public-safety surface rules. |
| 2 | +
|
| 3 | +The local-path rule recognizes every drive-qualified path, UNC shares and |
| 4 | +``/data`` roots; the secret rule tolerates quoted keys and values. Neither |
| 5 | +widening may start matching URLs, clock times or ratios. |
| 6 | +""" |
| 7 | + |
| 8 | +import pytest |
| 9 | + |
| 10 | +from loopx.control_plane.runtime.public_safety import ( |
| 11 | + LOCAL_PATH_SURFACE_PATTERN, |
| 12 | + SECRET_LIKE_SURFACE_PATTERN, |
| 13 | + validate_public_safe_value, |
| 14 | +) |
| 15 | + |
| 16 | +REJECTED_PATHS = [ |
| 17 | + "C:" + chr(92) + "build" + chr(92) + "evidence.txt", |
| 18 | + "C:/workspace/private/worker.json", |
| 19 | + "D:" + chr(92) + "Projects" + chr(92) + "loopx" + chr(92) + "state.json", |
| 20 | + chr(92) * 2 + "server" + chr(92) + "share" + chr(92) + "evidence.txt", |
| 21 | + "/data/reports/evidence.txt", |
| 22 | + "C:" + chr(92) + "Users" + chr(92) + "fixture" + chr(92) + "evidence.txt", |
| 23 | +] |
| 24 | + |
| 25 | +ACCEPTED_TEXT = [ |
| 26 | + "https://example.org/data/report", |
| 27 | + "s3://bucket/key", |
| 28 | + "notion://page/1", |
| 29 | + "12:30/45", |
| 30 | + "ratio 3:4/5 done", |
| 31 | + "id x:y/z", |
| 32 | + "docs/evidence.md", |
| 33 | + "access key rotation guide", |
| 34 | +] |
| 35 | + |
| 36 | +def _secret(key: str, separator: str, quote: str = "", key_quote: str = "") -> str: |
| 37 | + """Build a credential-shaped probe without a literal secret assignment.""" |
| 38 | + |
| 39 | + return f"{key_quote}{key}{key_quote}{separator}{quote}{'synthetic' * 4}{quote}" |
| 40 | + |
| 41 | + |
| 42 | +REJECTED_SECRETS = [ |
| 43 | + _secret("token", ": ", quote='"'), |
| 44 | + _secret("token", "="), |
| 45 | + _secret("access_key", "=", quote="'"), |
| 46 | + _secret("access_key", ": ", quote='"', key_quote='"'), |
| 47 | + _secret("sk", " = ", quote="'"), |
| 48 | +] |
| 49 | + |
| 50 | + |
| 51 | +@pytest.mark.parametrize("value", REJECTED_PATHS) |
| 52 | +def test_drive_unc_and_data_paths_are_local_paths(value): |
| 53 | + assert LOCAL_PATH_SURFACE_PATTERN.search(value) |
| 54 | + with pytest.raises(ValueError, match="absolute local path"): |
| 55 | + validate_public_safe_value(value) |
| 56 | + |
| 57 | + |
| 58 | +@pytest.mark.parametrize("value", ACCEPTED_TEXT) |
| 59 | +def test_urls_times_and_ratios_are_not_local_paths(value): |
| 60 | + assert not LOCAL_PATH_SURFACE_PATTERN.search(value) |
| 61 | + assert not SECRET_LIKE_SURFACE_PATTERN.search(value) |
| 62 | + validate_public_safe_value(value) |
| 63 | + |
| 64 | + |
| 65 | +@pytest.mark.parametrize("value", REJECTED_SECRETS) |
| 66 | +def test_quoted_secret_keys_and_values_are_credential_like(value): |
| 67 | + assert SECRET_LIKE_SURFACE_PATTERN.search(value) |
| 68 | + with pytest.raises(ValueError, match="credential-like"): |
| 69 | + validate_public_safe_value(value) |
| 70 | + |
| 71 | + |
| 72 | +def test_path_shaped_value_reports_local_path_before_opaque_shape(): |
| 73 | + # The turn-executor contract records this ordering: a drive-qualified |
| 74 | + # path is a local path first, so that diagnostic wins over the |
| 75 | + # opaque-reference shape check that runs afterwards. |
| 76 | + with pytest.raises(ValueError, match="absolute local path"): |
| 77 | + validate_public_safe_value({"worker_ref": "C:/workspace/private/worker.json"}) |
0 commit comments