T09 · Insecure Skill Coding Practices
Warning
- Location
- src/model_selector.py:108
- Finding
- Plaintext Persistence of Complete User Prompts Without Data Minimization<![CDATA[ ## Vulnerability Details **File Location**: `hooks/smart_model_selector.py:35-48`; `src/model_selector.py:61-77`; `src/model_selector.py:108-128`; `src/model_selector.py:374-382`; `src/model_selector.py:402-407` **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Vulnerable Code ```python # hooks/smart_model_selector.py:35-48 # Get the user's first message, if present first_message = bootstrap_info.get('first_message', '') if not first_message: return {'action': 'continue'} # Select the optimal model model, reason = selector.select_model(first_message) # Start tracking the task selector.start_task(first_message, model) ``` ```python # src/model_selector.py:61-77 c.execute(''' CREATE TABLE IF NOT EXISTS task_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_hash TEXT UNIQUE NOT NULL, task_text TEXT NOT NULL, selected_model TEXT NOT NULL, dialogue_rounds INTEGER DEFAULT 1, user_rating INTEGER, duration_seconds REAL DEFAULT 0, token_consumption INTEGER DEFAULT 0, is_completed INTEGER DEFAULT 0, score REAL DEFAULT 0, created_at TEXT, updated_at TEXT ) ''') ``` ```python # src/model_selector.py:108-128 c.execute(''' INSERT OR REPLACE INTO task_records (task_hash, task_text, selected_model, dialogue_rounds, user_rating, duration_seconds, token_consumption, is_completed, score, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( record.task_hash, record.task_text, record.selected_model, record.dialogue_rounds, record.user_rating, record.duration_seconds, record.token_consumption, 1 if record.is_completed else 0, self._calculate_score(record), record.created_at, record.updated_at )) conn.commit() conn.close() ``` ```python # src/model_selector.py:374-382 def start_task(self, task_text: str, selected_model: str = None): ...[truncated 3988 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Do not persist complete prompts by default** - Store only the selected model, derived task category, quality score, message-length bucket, and other non-content metadata needed for routing. - Remove the `task_text` column or replace it with minimized derived features. 2. **Use a non-reversible identifier** - If duplicate detection is required, use an HMAC with a locally generated secret instead of retaining the original text. - Do not treat unsalted MD5 as a privacy mechanism; predictable prompts can be recovered through dictionary guessing. 3. **Require explicit opt-in consent** - Disable learning-related persistence by default. - Clearly disclose which fields are stored, where they are stored, what event triggers persistence, and how long they are retained. 4. **Redact sensitive information** - Before any optional persistence, remove common credentials, authorization headers, private keys, connection strings, email addresses, and other sensitive patterns. - Warn users that automated redaction cannot guarantee removal of all confidential information. 5. **Protect retained data** - If storing prompt text is strictly necessary, encrypt the database or sensitive columns using a key held outside the database. - Create the data directory and database with owner-only permissions, such as mode `0700` for the directory and `0600` for the database, where supported. 6. **Implement retention and deletion controls** - Add configurable expiry and automatic deletion of old records. - Provide a documented command that securely removes all stored learning data. - Avoid retaining abandoned in-memory tasks longer than the active session. 7. **Align documentation with actual behavior** - Explain that `/model-rate` currently triggers persistence of the complete first message. - Update privacy claims to describe plaintext local storage accurately, or change the implementation to satisfy strong ...[truncated 26 chars]
