diff --git a/studio/backend/core/inference/tools.py b/studio/backend/core/inference/tools.py index 4af8b1552a..6d506eefa2 100644 --- a/studio/backend/core/inference/tools.py +++ b/studio/backend/core/inference/tools.py @@ -552,9 +552,13 @@ def _build_brace_re(prefix_alt: str, names: tuple[str, ...]) -> "re.Pattern[str] non-word -- so it cannot anchor here).""" return re.compile( _PATH_TOKEN_START - + r"(?:" + prefix_alt + r")" + + r"(?:" + + prefix_alt + + r")" + r"[^\s'\";&|`$]*?" - + r"\{[^{}]*?(?<=[,{/])(?:" + "|".join(names) + r")(?=,|\}|/)[^{}]*\}", + + r"\{[^{}]*?(?<=[,{/])(?:" + + "|".join(names) + + r")(?=,|\}|/)[^{}]*\}", re.IGNORECASE, ) @@ -568,16 +572,12 @@ _HOME_BRACE_PREFIX_ALT = ( + r"|%USERPROFILE%/+" + r"|%HOMEDRIVE%%HOMEPATH%/+" ) -_HOME_BRACE_RE = _build_brace_re( - _HOME_BRACE_PREFIX_ALT, _HOME_BRACE_SENSITIVE_NAMES -) +_HOME_BRACE_RE = _build_brace_re(_HOME_BRACE_PREFIX_ALT, _HOME_BRACE_SENSITIVE_NAMES) _ETC_BRACE_RE = _build_brace_re(r"/etc/+", _ETC_BRACE_SENSITIVE_NAMES) _PROC_BRACE_RE = _build_brace_re( r"/proc/(?:self|thread-self|\d+)/+", _PROC_BRACE_SENSITIVE_NAMES ) -_VAR_SPOOL_BRACE_RE = _build_brace_re( - r"/var/spool/cron/+", (r"[\w.-]+",) -) +_VAR_SPOOL_BRACE_RE = _build_brace_re(r"/var/spool/cron/+", (r"[\w.-]+",)) def _expand_brace_projections(text: str, limit: int = 1024) -> set[str]: @@ -3248,10 +3248,7 @@ def _check_signal_escape_patterns(code: str): ): recv = node.value.value.id attr = node.value.attr - if ( - recv in self.pathlib_aliases - and attr in _PATHLIB_PATH_CLASSES - ): + if recv in self.pathlib_aliases and attr in _PATHLIB_PATH_CLASSES: for tgt in node.targets: if isinstance(tgt, ast.Name): self.path_aliases.add(tgt.id) diff --git a/studio/backend/tests/test_sandbox_hardening.py b/studio/backend/tests/test_sandbox_hardening.py index c38e2c3613..286becd68c 100644 --- a/studio/backend/tests/test_sandbox_hardening.py +++ b/studio/backend/tests/test_sandbox_hardening.py @@ -2258,7 +2258,9 @@ class TestR7_DeepPathTraversal: ], ) def test_deep_traversal_bash_blocked(self, cmd): - assert _find_sensitive_paths(cmd), f"deep ~/foo/../../etc traversal leaked: {cmd!r}" + assert _find_sensitive_paths( + cmd + ), f"deep ~/foo/../../etc traversal leaked: {cmd!r}" @pytest.mark.parametrize( "code", @@ -2280,7 +2282,9 @@ class TestR7_DeepPathTraversal: ], ) def test_in_home_traversal_allowed(self, cmd): - assert not _find_sensitive_paths(cmd), f"legit in-home ../ traversal blocked: {cmd!r}" + assert not _find_sensitive_paths( + cmd + ), f"legit in-home ../ traversal blocked: {cmd!r}" class TestR7_BraceFalsePositive: @@ -2301,7 +2305,9 @@ class TestR7_BraceFalsePositive: ], ) def test_user_data_brace_allowed(self, cmd): - assert not _find_sensitive_paths(cmd), f"user-data brace falsely blocked: {cmd!r}" + assert not _find_sensitive_paths( + cmd + ), f"user-data brace falsely blocked: {cmd!r}" @pytest.mark.parametrize( "cmd", @@ -2324,7 +2330,9 @@ class TestR7_BraceFalsePositive: ], ) def test_home_credential_brace_blocked(self, cmd): - assert _find_sensitive_paths(cmd), f"home-credential brace listing leaked: {cmd!r}" + assert _find_sensitive_paths( + cmd + ), f"home-credential brace listing leaked: {cmd!r}" class TestR7_BinOpAddDepthCap: @@ -2344,7 +2352,9 @@ class TestR7_BinOpAddDepthCap: parts = ["''"] * pad + [repr(c) for c in target] expr = " + ".join(parts) code = f"open({expr}).read()" - assert _is_blocked(code), f"long {n_parts}-operand concat leaked: open({expr!r})" + assert _is_blocked( + code + ), f"long {n_parts}-operand concat leaked: open({expr!r})" def test_long_concat_legit_allowed(self): # A long concatenation that resolves to a benign path must