feat: secure_mkdir and secure_create helpers for restrictive file permissions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jon Saad-Falcon
2026-03-28 20:57:25 -07:00
co-authored by Claude Sonnet 4.6
parent 472938cc2b
commit 9909dde780
2 changed files with 108 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
"""Secure file and directory creation helpers.
All OpenJarvis data files under ``~/.openjarvis/`` should be created
through these helpers to ensure consistent, restrictive permissions.
"""
from __future__ import annotations
import os
from pathlib import Path
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
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
+74
View File
@@ -0,0 +1,74 @@
"""Tests for secure file creation helpers (Section 4)."""
from __future__ import annotations
import os
import stat
import tempfile
from pathlib import Path
class TestSecureMkdir:
"""secure_mkdir should create directories with 0o700."""
def test_creates_directory_with_700(self) -> None:
from openjarvis.security.file_utils import secure_mkdir
with tempfile.TemporaryDirectory() as tmp:
target = Path(tmp) / "secure_dir"
result = secure_mkdir(target)
assert result.is_dir()
mode = stat.S_IMODE(os.stat(target).st_mode)
assert mode == 0o700
def test_creates_parent_directories(self) -> None:
from openjarvis.security.file_utils import secure_mkdir
with tempfile.TemporaryDirectory() as tmp:
target = Path(tmp) / "a" / "b" / "c"
result = secure_mkdir(target)
assert result.is_dir()
def test_existing_directory_gets_permission_fix(self) -> None:
from openjarvis.security.file_utils import secure_mkdir
with tempfile.TemporaryDirectory() as tmp:
target = Path(tmp) / "existing"
target.mkdir(mode=0o755)
secure_mkdir(target)
mode = stat.S_IMODE(os.stat(target).st_mode)
assert mode == 0o700
class TestSecureCreate:
"""secure_create should create files with 0o600."""
def test_creates_file_with_600(self) -> None:
from openjarvis.security.file_utils import secure_create
with tempfile.TemporaryDirectory() as tmp:
target = Path(tmp) / "secure_file.db"
result = secure_create(target)
assert result.exists()
mode = stat.S_IMODE(os.stat(target).st_mode)
assert mode == 0o600
def test_existing_file_gets_permission_fix(self) -> None:
from openjarvis.security.file_utils import secure_create
with tempfile.TemporaryDirectory() as tmp:
target = Path(tmp) / "existing.db"
target.write_text("data")
os.chmod(target, 0o644)
secure_create(target)
mode = stat.S_IMODE(os.stat(target).st_mode)
assert mode == 0o600
def test_creates_parent_directory_with_700(self) -> None:
from openjarvis.security.file_utils import secure_create
with tempfile.TemporaryDirectory() as tmp:
target = Path(tmp) / "sub" / "file.db"
secure_create(target)
parent_mode = stat.S_IMODE(os.stat(target.parent).st_mode)
assert parent_mode == 0o700