fix(lib): hardening and edge-case bugfixes (FW-12, FW-16 round)

- Restored .bak generation to maintain P0-B backup invariants
- Fixed stale NFS warning message to reflect SQLite DELETE fallback
- Replaced vulnerable yaml.replace with os.path.splitext globally
- Ensured YAML dump occurs after conn.commit() to prevent partial syncs
- Re-applied chmod 0600 on SQLite -wal and -shm files
This commit is contained in:
2026-06-21 08:43:06 +00:00
parent 9b797a5c8c
commit 478be56679
3 changed files with 28 additions and 18 deletions
+26 -16
View File
@@ -112,7 +112,7 @@ resolve_tmux_server() {
import os, sys, sqlite3, json, yaml
name = os.environ['SESSION_NAME']
yaml_path = os.environ['YAML_PATH']
db_path = yaml_path.replace('.yaml', '.db')
db_path = os.path.splitext(yaml_path)[0] + '.db'
d = {}
try:
if os.path.exists(db_path):
@@ -124,13 +124,13 @@ try:
with open(yaml_path) as f:
d = yaml.safe_load(f) or {}
for s in d.get('tmux_sessions', []):
if s.get('name') == name:
server = s.get('tmux_server')
if server:
print(server)
sys.exit(0)
except Exception:
pass
if s.get('name') == name:
server = s.get('tmux_server')
if server:
print(server)
sys.exit(0)
except Exception:
pass
# Fallback
print(os.environ.get('TMUX_SERVER_NAME', 'default'))
PYEOF
@@ -208,7 +208,7 @@ _atomic_dump_yaml_check_nfs() {
if mount | grep -q "$mountpoint.*nfs\|$mountpoint.*cifs\|$mountpoint.*fuse.sshfs"; then
echo "WARNING: $mountpoint appears to be a network filesystem (NFS/CIFS/SSHFS)." >&2
echo "WARNING: fcntl.flock-based atomic writes are unreliable on network filesystems." >&2
echo "WARNING: Consider migrating to SQLite WAL for registry storage (see FUTURE_WORKS.md FW-02)." >&2
echo "WARNING: SQLite journal_mode automatically falls back to DELETE." >&2
fi
}
@@ -228,7 +228,7 @@ from datetime import datetime, timezone
import yaml
yaml_path = os.environ['YAML_PATH']
db_path = yaml_path.replace('.yaml', '.db')
db_path = os.path.splitext(yaml_path)[0] + '.db'
def _validate(d):
if not isinstance(d, dict):
@@ -305,14 +305,16 @@ try:
new_terminals = get_terminal_set(d)
conn.commit()
# Write to YAML ONLY when a session transitions to a finished state
# (Moved after conn.commit() per Claude's feedback)
if new_terminals != old_terminals:
if os.path.exists(yaml_path):
try:
shutil.copy2(yaml_path, yaml_path + '.bak')
except Exception:
pass
dir_ = os.path.dirname(yaml_path) or '.'
fd, tmp = tempfile.mkstemp(dir=dir_, prefix='.agent-sessions.', suffix='.tmp')
try:
@@ -324,9 +326,7 @@ try:
if os.path.exists(tmp):
os.remove(tmp)
raise
conn.commit()
if new_terminals != old_terminals:
try:
conn.execute('PRAGMA wal_checkpoint(TRUNCATE)')
except Exception:
@@ -336,6 +336,16 @@ except Exception:
raise
finally:
conn.close()
# H3: Re-apply chmod 0600 after close to cover newly created -wal / -shm files
try:
os.chmod(db_path, 0o600)
wal = db_path + '-wal'
if os.path.exists(wal): os.chmod(wal, 0o600)
shm = db_path + '-shm'
if os.path.exists(shm): os.chmod(shm, 0o600)
except Exception:
pass
PYEOF
}
@@ -364,7 +374,7 @@ ws = os.environ['WS_ABS']
agent = os.environ['AGENT']
home = os.environ['HOME_DIR']
yaml_path = os.environ['YAML_PATH']
db_path = yaml_path.replace('.yaml', '.db')
db_path = os.path.splitext(yaml_path)[0] + '.db'
claude_project_dir = os.environ.get('CLAUDE_PROJECT_DIR', f"{home}/.claude/projects")
d = {}
@@ -483,7 +493,7 @@ is_already_stopped() {
import os, yaml, sqlite3, json
name = os.environ['SESSION_NAME']
yaml_path = os.environ['YAML_PATH']
db_path = yaml_path.replace('.yaml', '.db')
db_path = os.path.splitext(yaml_path)[0] + '.db'
d = {}
try:
if os.path.exists(db_path):
@@ -238,7 +238,7 @@ try:
d
except NameError:
import sqlite3
db_path = yaml_path.replace('.yaml', '.db')
db_path = os.path.splitext(yaml_path)[0] + '.db'
d = {}
try:
if os.path.exists(db_path):
@@ -37,7 +37,7 @@ home = os.environ['HOME_DIR']
claude_project_dir = os.environ.get('CLAUDE_PROJECT_DIR', f"{home}/.claude/projects")
drift = json.loads(os.environ['DRIFT_JSON'])
db_path = yaml_path.replace('.yaml', '.db')
db_path = os.path.splitext(yaml_path)[0] + '.db'
d = {}
import sqlite3
try: