mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-28 05:12:26 +00:00
171 lines
5.3 KiB
Python
171 lines
5.3 KiB
Python
"""Tests for mount_security module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from openjarvis.sandbox.mount_security import (
|
|
DEFAULT_BLOCKED_PATTERNS,
|
|
AllowedRoot,
|
|
MountAllowlist,
|
|
load_mount_allowlist,
|
|
validate_mount,
|
|
validate_mounts,
|
|
)
|
|
|
|
|
|
class TestDefaultBlockedPatterns:
|
|
def test_contains_ssh(self):
|
|
assert ".ssh" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
def test_contains_env(self):
|
|
assert ".env" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
def test_contains_pem(self):
|
|
assert "*.pem" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
def test_contains_key(self):
|
|
assert "*.key" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
def test_contains_gnupg(self):
|
|
assert ".gnupg" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
def test_contains_aws(self):
|
|
assert ".aws" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
def test_contains_credentials(self):
|
|
assert "credentials" in DEFAULT_BLOCKED_PATTERNS
|
|
|
|
|
|
class TestValidateMount:
|
|
def test_allows_valid_path(self, tmp_path):
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
target = tmp_path / "data"
|
|
target.mkdir()
|
|
assert validate_mount(str(target), allowlist) is True
|
|
|
|
def test_blocks_ssh_dir(self, tmp_path):
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
target = tmp_path / ".ssh"
|
|
target.mkdir()
|
|
assert validate_mount(str(target), allowlist) is False
|
|
|
|
def test_blocks_env_file(self, tmp_path):
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
target = tmp_path / ".env"
|
|
target.touch()
|
|
assert validate_mount(str(target), allowlist) is False
|
|
|
|
def test_blocks_pem_file(self, tmp_path):
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
target = tmp_path / "server.pem"
|
|
target.touch()
|
|
assert validate_mount(str(target), allowlist) is False
|
|
|
|
def test_blocks_key_file(self, tmp_path):
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
target = tmp_path / "private.key"
|
|
target.touch()
|
|
assert validate_mount(str(target), allowlist) is False
|
|
|
|
def test_rejects_outside_root(self, tmp_path):
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path / "allowed"))],
|
|
)
|
|
target = tmp_path / "not_allowed" / "data"
|
|
target.mkdir(parents=True)
|
|
assert validate_mount(str(target), allowlist) is False
|
|
|
|
def test_no_roots_allows_non_blocked(self, tmp_path):
|
|
allowlist = MountAllowlist(roots=[])
|
|
target = tmp_path / "safe_dir"
|
|
target.mkdir()
|
|
assert validate_mount(str(target), allowlist) is True
|
|
|
|
def test_traversal_prevention(self, tmp_path):
|
|
"""Paths with .. are resolved before checking."""
|
|
allowed = tmp_path / "allowed"
|
|
allowed.mkdir()
|
|
# This resolves to tmp_path (outside allowed)
|
|
traversal = str(allowed / ".." / "secret")
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(allowed))],
|
|
blocked_patterns=["secret"],
|
|
)
|
|
assert validate_mount(traversal, allowlist) is False
|
|
|
|
|
|
class TestValidateMounts:
|
|
def test_returns_valid_mounts(self, tmp_path):
|
|
d1 = tmp_path / "data1"
|
|
d2 = tmp_path / "data2"
|
|
d1.mkdir()
|
|
d2.mkdir()
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
result = validate_mounts([str(d1), str(d2)], allowlist)
|
|
assert len(result) == 2
|
|
|
|
def test_raises_for_blocked(self, tmp_path):
|
|
target = tmp_path / ".ssh"
|
|
target.mkdir()
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path))],
|
|
)
|
|
with pytest.raises(ValueError, match="blocked"):
|
|
validate_mounts([str(target)], allowlist)
|
|
|
|
def test_raises_for_outside_root(self, tmp_path):
|
|
target = tmp_path / "outside"
|
|
target.mkdir()
|
|
allowlist = MountAllowlist(
|
|
roots=[AllowedRoot(path=str(tmp_path / "inside"))],
|
|
)
|
|
with pytest.raises(ValueError, match="not under"):
|
|
validate_mounts([str(target)], allowlist)
|
|
|
|
def test_empty_list(self):
|
|
allowlist = MountAllowlist()
|
|
assert validate_mounts([], allowlist) == []
|
|
|
|
|
|
class TestLoadMountAllowlist:
|
|
def test_loads_from_json(self, tmp_path):
|
|
config = {
|
|
"roots": [
|
|
{"path": "/home/user/projects", "read_only": False},
|
|
{"path": "/data"},
|
|
],
|
|
"blocked_patterns": [".ssh", "*.pem"],
|
|
}
|
|
f = tmp_path / "allowlist.json"
|
|
f.write_text(json.dumps(config))
|
|
|
|
al = load_mount_allowlist(str(f))
|
|
assert len(al.roots) == 2
|
|
assert al.roots[0].path == "/home/user/projects"
|
|
assert al.roots[0].read_only is False
|
|
assert al.roots[1].read_only is True
|
|
assert al.blocked_patterns == [".ssh", "*.pem"]
|
|
|
|
def test_default_blocked_patterns(self, tmp_path):
|
|
config = {"roots": []}
|
|
f = tmp_path / "allowlist.json"
|
|
f.write_text(json.dumps(config))
|
|
|
|
al = load_mount_allowlist(str(f))
|
|
assert al.blocked_patterns == DEFAULT_BLOCKED_PATTERNS
|