Skip to content

mount_security

mount_security

Mount validation and security for container sandboxes.

Port of NanoClaw's mount-security.ts — validates bind mounts against an allowlist and blocks paths containing sensitive files.

Classes

AllowedRoot dataclass

AllowedRoot(path: str, read_only: bool = True)

An allowed mount root with optional read-only constraint.

MountAllowlist dataclass

MountAllowlist(roots: List[AllowedRoot] = list(), blocked_patterns: List[str] = (lambda: list(DEFAULT_BLOCKED_PATTERNS))())

Allowlist for container mounts.

Functions

load_mount_allowlist

load_mount_allowlist(path: str) -> MountAllowlist

Load a mount allowlist from a JSON file.

Expected format::

{
    "roots": [
        {"path": "/home/user/projects", "read_only": false},
        {"path": "/data", "read_only": true}
    ],
    "blocked_patterns": [".ssh", ".env", "*.pem"]
}

If blocked_patterns is omitted, :data:DEFAULT_BLOCKED_PATTERNS is used.

Source code in src/openjarvis/sandbox/mount_security.py
def load_mount_allowlist(path: str) -> MountAllowlist:
    """Load a mount allowlist from a JSON file.

    Expected format::

        {
            "roots": [
                {"path": "/home/user/projects", "read_only": false},
                {"path": "/data", "read_only": true}
            ],
            "blocked_patterns": [".ssh", ".env", "*.pem"]
        }

    If ``blocked_patterns`` is omitted, :data:`DEFAULT_BLOCKED_PATTERNS`
    is used.
    """
    raw = Path(path).read_text()
    data = json.loads(raw)

    roots = [
        AllowedRoot(
            path=r["path"],
            read_only=r.get("read_only", True),
        )
        for r in data.get("roots", [])
    ]

    blocked = data.get("blocked_patterns", list(DEFAULT_BLOCKED_PATTERNS))
    return MountAllowlist(roots=roots, blocked_patterns=blocked)

validate_mount

validate_mount(mount_path: str, allowlist: MountAllowlist) -> bool

Validate a single mount path against the allowlist.

Returns True if the mount is allowed, False otherwise.

Source code in src/openjarvis/sandbox/mount_security.py
def validate_mount(
    mount_path: str,
    allowlist: MountAllowlist,
) -> bool:
    """Validate a single mount path against the allowlist.

    Returns ``True`` if the mount is allowed, ``False`` otherwise.
    """
    # Resolve symlinks and normalize
    try:
        resolved = str(Path(mount_path).resolve())
    except (OSError, ValueError):
        return False

    # Check blocked patterns
    if _is_blocked(resolved, allowlist.blocked_patterns):
        logger.debug("Mount blocked by pattern: %s", mount_path)
        return False

    # Check allowed roots
    if not _is_under_allowed_root(resolved, allowlist.roots):
        logger.debug("Mount not under any allowed root: %s", mount_path)
        return False

    return True

validate_mounts

validate_mounts(mounts: List[str], allowlist: MountAllowlist) -> List[str]

Validate a list of mount paths. Returns only valid mounts.

Raises :class:ValueError for any blocked mount.

Source code in src/openjarvis/sandbox/mount_security.py
def validate_mounts(
    mounts: List[str],
    allowlist: MountAllowlist,
) -> List[str]:
    """Validate a list of mount paths. Returns only valid mounts.

    Raises :class:`ValueError` for any blocked mount.
    """
    valid: List[str] = []
    for mount in mounts:
        if _is_blocked(mount, allowlist.blocked_patterns):
            raise ValueError(
                f"Mount path blocked by security policy: {mount}"
            )
        if _is_under_allowed_root(mount, allowlist.roots):
            valid.append(mount)
        else:
            raise ValueError(
                f"Mount path not under any allowed root: {mount}"
            )
    return valid