Skip to content

Commit fef6c45

Browse files
committed
fix(workspace): keep state.db's umask permissions when building via mkstemp (#954)
mkstemp forces 0600 and os.replace preserves it, so the transactional-init change silently tightened state.db from the umask-derived mode sqlite used to create it (0644 on a stock 0022 umask). That is an unrequested behavior change that could break a shared-group deployment. Reproduce a normal file creation instead — permissions are not this change's business.
1 parent eb52358 commit fef6c45

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

codeframe/core/workspace.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,14 @@ def create_or_load_workspace(repo_path: Path, tech_stack: Optional[str] = None)
988988
os.close(fd)
989989
tmp_db = Path(tmp_name)
990990
try:
991+
# mkstemp forces 0600 and os.replace preserves it, which would silently
992+
# tighten state.db from the umask-derived mode sqlite used to create it.
993+
# Reproduce a normal file creation instead — permissions are not this
994+
# change's business.
995+
umask = os.umask(0)
996+
os.umask(umask)
997+
os.chmod(tmp_db, 0o666 & ~umask)
998+
991999
# mkstemp already created an empty file; sqlite is happy to build into it.
9921000
_init_database(tmp_db)
9931001

tests/core/test_atomic_writes_954.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,25 @@ def test_workspace_init_fsyncs_the_state_dir_after_the_rename(tmp_path, monkeypa
363363
assert str(repo / ".codeframe") in synced
364364

365365

366+
def test_state_db_keeps_normal_create_permissions(tmp_path):
367+
"""Building via mkstemp must not silently tighten state.db to 0600.
368+
369+
mkstemp forces 0600 and os.replace preserves it; sqlite used to create the
370+
file with the process umask. Permissions are not this change's business.
371+
"""
372+
from codeframe.core import workspace as ws
373+
374+
repo = tmp_path / "repo"
375+
repo.mkdir()
376+
ws.create_or_load_workspace(repo)
377+
378+
umask = os.umask(0)
379+
os.umask(umask)
380+
expected = 0o666 & ~umask
381+
actual = (repo / ".codeframe" / ws.STATE_DB_NAME).stat().st_mode & 0o777
382+
assert actual == expected, f"expected {oct(expected)}, got {oct(actual)}"
383+
384+
366385
def test_an_existing_workspace_is_still_loaded_not_rebuilt(tmp_path):
367386
from codeframe.core import workspace as ws
368387

0 commit comments

Comments
 (0)