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:
+25
-15
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user