T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/queue-message.js:81
- Finding
- Queued private messages are stored with insufficient permission and retention controls## Vulnerability Details **File Location**: `scripts/queue-message.js:81-91`; `scripts/digest.js:85-88` **Vulnerability Type**: Plaintext sensitive-data storage and indefinite retention **Risk Level**: Medium ### Vulnerable Code `scripts/queue-message.js:81-91`: ```js const Database = require('better-sqlite3'); const db = new Database(DB_FILE); const insert = db.prepare(` INSERT INTO queue (provider, sender_id, sender_name, message, received_at) VALUES (?, ?, ?, ?, datetime('now')) `); const result = insert.run(provider, senderId, senderName || senderId, message); db.close(); ``` `scripts/digest.js:85-88`: ```js // Mark all as delivered const ids = rows.map(r => r.id); db.prepare(`UPDATE queue SET delivered = 1 WHERE id IN (${ids.join(',')})`).run(); db.close(); ``` ### Technical Analysis The Skill stores complete private-message bodies, provider identifiers, sender identifiers, and sender names in a local SQLite database. The database and its parent data directory are created without explicit owner-only permissions, so their effective permissions depend on the user's process umask and existing directory permissions. Digest processing only sets `delivered = 1`; it does not delete delivered records or impose a retention period. Consequently, `queue.db` becomes a persistent archive of message history. This conflicts with the declared behavior in `SKILL.md`, which states that the queue is cleared after digest delivery. SQLite parameter binding is correctly used, so the insertion itself is not vulnerable to SQL injection. The issue is the confidentiality and retention of the stored content. ### Attack Path 1. The Skill receives non-urgent messages during a sleep window. 2. `queue-message.js` writes the complete message content and sender metadata to `queue.db`. 3. Morning digest processing marks records as delivered but leaves them in the database. 4. A local account or compromised p ...[truncated 788 chars]
- Remediation
- ## Remediation Suggestions 1. Create the data directory with mode `0700` and verify its existing permissions: ```js fs.mkdirSync(DATA_DIR, { recursive: true, mode: 0o700 }); fs.chmodSync(DATA_DIR, 0o700); ``` 2. Create and maintain `queue.db`, `state.json`, and `vip-contacts.json` with mode `0600`. 3. Delete queue records only after confirmed digest delivery instead of retaining them with a delivered flag. 4. If delivery history is required, make retention explicit and configurable, and purge records after a short documented period. 5. Consider encrypting message content at rest when the host platform provides a suitable user-bound secret or operating-system keystore. 6. Update the privacy documentation to disclose the actual retention behavior.
