From 66d73338bf41e3513fb54063ea9f139f3c450a1d Mon Sep 17 00:00:00 2001 From: Gracy769 <169199616+Gracy769@users.noreply.github.com> Date: Mon, 24 Aug 2026 19:14:36 +0530 Subject: [PATCH] fix(security): path containment checks for Windows environments - Fixed an issue in extract_path_candidates where shlex.split(posix=True) would strip backslashes from Windows paths, mangling UNC paths (e.g. \\server\share) before they could be evaluated by _is_windows_absolute. - Fixed a bypass in validate_path where Windows absolute paths bypassed glob expansion and symlink resolution. On Windows, they now fall through to the standard Path logic, allowing glob expansion and strict resolution while still properly checking containment. --- src/path_scope.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/path_scope.py b/src/path_scope.py index 31828a6a..ba84229a 100644 --- a/src/path_scope.py +++ b/src/path_scope.py @@ -60,7 +60,12 @@ class WorkspacePathScope: def validate_path(self, candidate: str | Path, cwd: str | Path | None = None) -> PathScopeDecision: raw = os.path.expandvars(os.path.expanduser(str(candidate))) if _is_windows_absolute(raw): - return self._validate_windows_path(raw) + if os.name != 'nt': + return self._validate_windows_path(raw) + elif not any(_is_windows_absolute(str(root)) for root in self.roots): + # Even on Windows, deny if no roots are Windows absolute paths (edge case) + return PathScopeDecision(False, 'windows absolute path is outside workspace scope', str(candidate), raw) + base = Path(cwd).expanduser().resolve(strict=False) if cwd else self.roots[0] path = Path(raw) if not path.is_absolute(): @@ -116,7 +121,7 @@ def extract_path_candidates(payload: str) -> tuple[str, ...]: tokens = payload.split() raw_tokens = payload.split() candidates: list[str] = [] - for token in (*tokens, *raw_tokens): + for token in (*raw_tokens, *tokens): if not token or token.startswith('-') or _ENV_ASSIGNMENT_RE.match(token): continue token = _strip_redirection_operator(token)