mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-05 08:58:34 +00:00
105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from nanobot.resource_links import ensure_resource_view
|
|
from nanobot.security.workspace_policy import WorkspaceBoundaryError, resolve_allowed_path
|
|
|
|
|
|
@pytest.fixture
|
|
def resource_targets(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
|
|
data_dir = tmp_path / "data"
|
|
workspace = tmp_path / "agent"
|
|
package = tmp_path / "package" / "nanobot"
|
|
project = tmp_path / "project"
|
|
(workspace / "skills" / "custom").mkdir(parents=True)
|
|
(workspace / "memory").mkdir()
|
|
(package / "skills" / "builtin").mkdir(parents=True)
|
|
(package / "templates").mkdir()
|
|
project.mkdir()
|
|
(workspace / "skills" / "custom" / "SKILL.md").write_text("custom", encoding="utf-8")
|
|
(workspace / "memory" / "history.jsonl").write_text("{}\n", encoding="utf-8")
|
|
(package / "skills" / "builtin" / "SKILL.md").write_text("builtin", encoding="utf-8")
|
|
(package / "templates" / "identity.md").write_text("identity", encoding="utf-8")
|
|
return data_dir, workspace, package, project
|
|
|
|
|
|
def _view_for(targets: tuple[Path, Path, Path, Path]):
|
|
data_dir, workspace, package, _ = targets
|
|
view = ensure_resource_view(
|
|
data_dir=data_dir,
|
|
config_path=data_dir / "config.json",
|
|
agent_workspace=workspace,
|
|
package_root=package,
|
|
)
|
|
if view.agent is None or view.media is None or view.package is None:
|
|
pytest.skip(f"directory links unavailable: {view.warnings}")
|
|
return view
|
|
|
|
|
|
def test_restricted_access_follows_resource_alias_targets(
|
|
resource_targets: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, workspace, package, project = resource_targets
|
|
view = _view_for(resource_targets)
|
|
|
|
custom_skill = resolve_allowed_path(
|
|
view.agent / "skills" / "custom" / "SKILL.md",
|
|
workspace=project,
|
|
allowed_root=project,
|
|
extra_allowed_roots=[workspace / "skills", package / "skills"],
|
|
strict=True,
|
|
)
|
|
builtin_skill = resolve_allowed_path(
|
|
view.package / "skills" / "builtin" / "SKILL.md",
|
|
workspace=project,
|
|
allowed_root=project,
|
|
extra_allowed_roots=[workspace / "skills", package / "skills"],
|
|
strict=True,
|
|
)
|
|
media_root = resolve_allowed_path(
|
|
view.media,
|
|
workspace=project,
|
|
allowed_root=project,
|
|
extra_allowed_roots=[resource_targets[0] / "media"],
|
|
strict=True,
|
|
)
|
|
|
|
assert custom_skill == (workspace / "skills" / "custom" / "SKILL.md").resolve()
|
|
assert builtin_skill == (package / "skills" / "builtin" / "SKILL.md").resolve()
|
|
assert media_root == (resource_targets[0] / "media").resolve()
|
|
|
|
|
|
def test_alias_does_not_expand_restricted_package_or_agent_access(
|
|
resource_targets: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, workspace, _, project = resource_targets
|
|
view = _view_for(resource_targets)
|
|
|
|
with pytest.raises(WorkspaceBoundaryError):
|
|
resolve_allowed_path(
|
|
view.package / "templates" / "identity.md",
|
|
workspace=project,
|
|
allowed_root=project,
|
|
extra_allowed_roots=[workspace / "skills"],
|
|
strict=True,
|
|
)
|
|
|
|
history = workspace / "memory" / "history.jsonl"
|
|
with pytest.raises(WorkspaceBoundaryError):
|
|
resolve_allowed_path(
|
|
view.agent / "memory" / "history.jsonl",
|
|
workspace=project,
|
|
allowed_root=project,
|
|
extra_allowed_files=[history],
|
|
strict=True,
|
|
)
|
|
|
|
assert resolve_allowed_path(
|
|
history,
|
|
workspace=project,
|
|
allowed_root=project,
|
|
extra_allowed_files=[history],
|
|
strict=True,
|
|
) == history.resolve()
|