fix: block exact-file allowlist link escapes
maintainer edit: compare exact-file allowlist entries using logical paths and require the resolved target to stay at that canonical path, so symlinks or junctions cannot redirect canonical memory files to an external write target.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -11,6 +13,24 @@ from nanobot.security.workspace_policy import (
|
||||
)
|
||||
|
||||
|
||||
def _make_directory_link(link: Path, target: Path) -> None:
|
||||
if os.name == "nt":
|
||||
completed = subprocess.run(
|
||||
["cmd", "/c", "mklink", "/J", str(link), str(target)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
pytest.skip(completed.stderr.strip() or completed.stdout.strip())
|
||||
return
|
||||
|
||||
try:
|
||||
link.symlink_to(target, target_is_directory=True)
|
||||
except OSError as exc:
|
||||
pytest.skip(f"symlink creation is unavailable: {exc}")
|
||||
|
||||
|
||||
def test_resolve_allowed_path_accepts_workspace_relative_path(tmp_path: Path) -> None:
|
||||
workspace = tmp_path / "workspace"
|
||||
workspace.mkdir()
|
||||
@@ -91,3 +111,24 @@ def test_resolve_allowed_path_allows_extra_file_only_exactly(tmp_path: Path) ->
|
||||
allowed_root=workspace,
|
||||
extra_allowed_files=[allowed],
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_allowed_path_extra_file_blocks_link_escape(tmp_path: Path) -> None:
|
||||
workspace = tmp_path / "workspace"
|
||||
workspace.mkdir()
|
||||
outside = tmp_path / "outside"
|
||||
outside.mkdir()
|
||||
outside_target = outside / "MEMORY.md"
|
||||
outside_target.write_text("secret", encoding="utf-8")
|
||||
|
||||
memory_link = workspace / "memory"
|
||||
_make_directory_link(memory_link, outside)
|
||||
logical_allowed = memory_link / "MEMORY.md"
|
||||
|
||||
with pytest.raises(WorkspaceBoundaryError, match="outside allowed directory"):
|
||||
resolve_allowed_path(
|
||||
"memory/MEMORY.md",
|
||||
workspace=workspace,
|
||||
allowed_root=workspace / "skills",
|
||||
extra_allowed_files=[logical_allowed],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user