Skip to content

Commit d2c0fc1

Browse files
committed
fix(security): resolve F-03 normalize cross-platform traversal paths
1 parent a1f22cb commit d2c0fc1

2 files changed

Lines changed: 9 additions & 4 deletions

File tree

maf_starter/tools.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import json
44
import subprocess
5-
from pathlib import Path
5+
from pathlib import Path, PureWindowsPath
66

77
from agent_framework import tool
88

@@ -18,7 +18,10 @@
1818

1919

2020
def _resolve_candidate(repo_root: Path, raw_path: str) -> Path:
21-
candidate = Path(raw_path)
21+
normalized_raw = raw_path.replace("\\", "/")
22+
if PureWindowsPath(raw_path).is_absolute() and not Path(normalized_raw).is_absolute():
23+
raise ValueError(f"Path escapes the configured repo root: {raw_path}")
24+
candidate = Path(normalized_raw)
2225
if not candidate.is_absolute():
2326
candidate = (repo_root / candidate).resolve()
2427
else:

tests/test_maf_setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,10 @@ def test_resolve_repo_path_blocks_escape(self) -> None:
112112
allowed = root / "notes.txt"
113113
allowed.write_text("ok", encoding="utf-8")
114114
self.assertEqual(resolve_repo_path(root, "notes.txt"), allowed)
115-
with self.assertRaises(ValueError):
116-
resolve_repo_path(root, "..\\outside.txt")
115+
for unsafe_path in ("../outside.txt", "..\\outside.txt", "C:\\outside.txt"):
116+
with self.subTest(path=unsafe_path):
117+
with self.assertRaises(ValueError):
118+
resolve_repo_path(root, unsafe_path)
117119

118120
def test_build_repo_tools_contains_expected_tool_names(self) -> None:
119121
tools = build_repo_tools(self.make_scratch_dir())

0 commit comments

Comments
 (0)