T09 · Insecure Skill Coding Practices
- Location
scripts/notify_closed_bottles.py:126- Finding
Malformed notifier state or ledger truncation can replay previously delivered messages
- Content
View full analysis
dict: p = state_file() p.parent.mkdir(parents=True, exist_ok=True) if p.exists(): try: return json.loads(p.read_text(encoding="utf-8")) except Exception: pass return {"enabled": True, "last_processed_line": 0} ``` The processing path also automatically rewinds an out-of-range cursor: ```python lines = load_all_lines() total = len(lines) cursor = state.get("last_processed_line", 0) if cursor > total: cursor = 0 ``` ### Technical Analysis The notifier treats a missing state file and an existing but malformed state file identically. If JSON parsing fails, the broad exception handler silently discards the failure and returns a fresh state with the notifier enabled and its cursor set to zero. This also discards the persisted `delivered_ids` deduplication list. The same replay behavior occurs when the saved cursor exceeds the current number of non-empty ledger lines. Instead of failing closed and requiring operator review, the code resets the cursor to zero. The observer implementation in `miab_observer.py` already demonstrates the safer approach by rejecting malformed state rather than rewinding. The closed-bottle notifier does not apply that protection even though its output is externally delivered to Discord. ### Attack Path 1. An attacker or faulty local process with write access corrupts or truncates `closed_bottle_state.json`, causing JSON parsing to fail. Alternatively, the attacker truncates or replaces the ledger so that the stored cursor exceeds its current line count. 2. A scheduled job subsequently invokes `notify_closed_bottles.py process`. 3. `load_state()` sil ...[truncated 819 chars]- Remediation
View remediation
