Skip to content

file_utils

file_utils

Secure file and directory creation helpers.

All OpenJarvis data files under ~/.openjarvis/ should be created through these helpers to ensure consistent, restrictive permissions.

Functions

secure_mkdir

secure_mkdir(path: Path, mode: int = 448) -> Path

Create a directory with restrictive permissions.

Creates parent directories as needed, then sets mode on the target directory (even if it already exists).

Source code in src/openjarvis/security/file_utils.py
def secure_mkdir(path: Path, mode: int = 0o700) -> Path:
    """Create a directory with restrictive permissions.

    Creates parent directories as needed, then sets *mode* on the
    target directory (even if it already exists).
    """
    path.mkdir(parents=True, exist_ok=True)
    os.chmod(path, mode)
    return path

secure_create

secure_create(path: Path, mode: int = 384) -> Path

Ensure a file exists with restrictive permissions.

Creates the parent directory with 0o700 if needed, touches the file if it doesn't exist, and sets mode on it.

Source code in src/openjarvis/security/file_utils.py
def secure_create(path: Path, mode: int = 0o600) -> Path:
    """Ensure a file exists with restrictive permissions.

    Creates the parent directory with ``0o700`` if needed, touches the
    file if it doesn't exist, and sets *mode* on it.
    """
    secure_mkdir(path.parent, mode=0o700)
    if not path.exists():
        path.touch()
    os.chmod(path, mode)
    return path

secure_write_text

secure_write_text(path: Path, content: str, *, mode: int = 384, encoding: str = 'utf-8') -> Path

Atomically replace path with owner-only text content.

The temporary file is created in the destination directory so :func:os.replace remains atomic. Permissions are restricted before secret bytes are written, then re-applied after replacement for platforms whose replace semantics preserve the destination's prior mode.

Source code in src/openjarvis/security/file_utils.py
def secure_write_text(
    path: Path,
    content: str,
    *,
    mode: int = 0o600,
    encoding: str = "utf-8",
) -> Path:
    """Atomically replace *path* with owner-only text content.

    The temporary file is created in the destination directory so
    :func:`os.replace` remains atomic. Permissions are restricted before
    secret bytes are written, then re-applied after replacement for platforms
    whose replace semantics preserve the destination's prior mode.
    """
    secure_mkdir(path.parent, mode=0o700)
    fd, temporary_name = tempfile.mkstemp(
        prefix=f".{path.name}.",
        suffix=".tmp",
        dir=str(path.parent),
    )
    temporary_path = Path(temporary_name)
    try:
        if hasattr(os, "fchmod"):
            os.fchmod(fd, mode)
        with os.fdopen(fd, "w", encoding=encoding) as handle:
            handle.write(content)
            handle.flush()
            os.fsync(handle.fileno())
        os.replace(temporary_path, path)
        os.chmod(path, mode)
    except Exception:
        try:
            os.close(fd)
        except OSError:
            pass
        temporary_path.unlink(missing_ok=True)
        raise
    return path

secure_write_json

secure_write_json(path: Path, payload: Any, *, mode: int = 384) -> Path

Atomically persist JSON with restrictive permissions.

Source code in src/openjarvis/security/file_utils.py
def secure_write_json(path: Path, payload: Any, *, mode: int = 0o600) -> Path:
    """Atomically persist JSON with restrictive permissions."""
    return secure_write_text(
        path,
        json.dumps(payload, indent=2),
        mode=mode,
        encoding="utf-8",
    )