T09 · Insecure Skill Coding Practices
- Location
scripts/ledger_watcher.py:484- Finding
Sensitive ledger and checkpoint files rely on ambient umask permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/ledger_watcher.py:484-486, 528-529; sensitive fields defined inscripts/ledger_schema.sql:5-34
Vulnerability Type: Local sensitive-data exposure through insufficiently enforced file permissions
Risk Level: LowVulnerable Code
python def get_db() -> sqlite3.Connection: LEDGER_DB.parent.mkdir(parents=True, exist_ok=True) db = sqlite3.connect(str(LEDGER_DB)) db.execute("PRAGMA journal_mode=WAL")python def save_checkpoint(cp: dict): CHECKPOINT.write_text(json.dumps(cp, indent=2))The database contains sensitive operational metadata:
sql CREATE TABLE IF NOT EXISTS calls ( call_id TEXT PRIMARY KEY, session_key TEXT NOT NULL, turn_hint TEXT, ts TEXT NOT NULL, provider TEXT, model TEXT, model_raw TEXT, call_reason TEXT DEFAULT 'primary', input_tokens INTEGER DEFAULT 0, output_tokens INTEGER DEFAULT 0, cache_read_tokens INTEGER DEFAULT 0, cache_write_tokens INTEGER DEFAULT 0, cost_input REAL DEFAULT 0, cost_output REAL DEFAULT 0, cost_cache_read REAL DEFAULT 0, cost_cache_write REAL DEFAULT 0, cost_total REAL DEFAULT 0, cost_source TEXT DEFAULT 'unknown', channel TEXT, chat_id TEXT, thread_id TEXT, message_id TEXT, source_kind TEXT DEFAULT 'interactive', cron_job_id TEXT, price_version TEXT DEFAULT '2026-03-17-unified', usage_raw TEXT, created_at TEXT DEFAULT (datetime('now')) );Technical Analysis
The watcher creates
~/.openclaw,ledger.db, SQLite WAL/SHM files, and the main checkpoint without explicitly enforcing owner-only permissions. Their resulting modes therefore depend on the process umask an ...[truncated 1905 chars]- Remediation
View remediation
Remediation Suggestions
-
Create the data directory with mode
0700and verify its mode even when it already exists:python LEDGER_DB.parent.mkdir(parents=True, exist_ok=True, mode=0o700) LEDGER_DB.parent.chmod(0o700) -
Explicitly set
ledger.dbto mode0600immediately after opening or creating it:python db = sqlite3.connect(str(LEDGER_DB)) LEDGER_DB.chmod(0o600) -
Restrict SQLite sidecar files after enabling WAL mode, and consider using a temporary restrictive umask while opening the database:
python old_umask = os.umask(0o077) try: db = sqlite3.connect(str(LEDGER_DB)) db.execute("PRAGMA journal_mode=WAL") finally: os.umask(old_umask) -
Replace the main checkpoint atomically using the same pattern already used for the Spark checkpoint: create a temporary file in the protected directory, set it to
0600, flush it, and atomically replace the destination. -
On startup, validate ownership and permissions of the directory, database, WAL/SHM files, and checkpoints. Refuse to proceed or emit a prominent warning if they are writable or readable by unintended users.
-
Document that the ledger contains potentially sensitive communication and financial metadata, along with recommended retention and secure-deletion practices.
-
