T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/refine_image.py:28
- Finding
- Plaintext Persistence of Sensitive Conversation and Image Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/refine_image.py:28-36`, `scripts/refine_image.py:61-67`, and `scripts/refine_image.py:82-89` **Vulnerability Type**: Plaintext storage of potentially sensitive data **Risk Level**: Medium ### Vulnerable Code ```python def load_conversation(history_file): """Load conversation history from JSON file.""" if not os.path.exists(history_file): return [] with open(history_file, "r") as f: return json.load(f) def save_conversation(history_file, contents): """Save conversation history to JSON file.""" with open(history_file, "w") as f: json.dump(contents, f, indent=2) ``` The history receives the user prompt: ```python # Load existing conversation contents = load_conversation(history_file) # Add new user prompt contents.append({ "role": "user", "parts": [{"text": prompt}] }) ``` The complete model response is then persisted: ```python # Add model response to conversation contents.append({ "role": "model", "parts": model_parts }) # Save updated conversation save_conversation(history_file, contents) ``` ### Technical Analysis The refinement script stores the complete conversation in an unencrypted JSON file. This includes user prompts and the API's complete `model_parts` response. Because generated images may be returned as base64-encoded `inlineData`, the history can contain both textual information and full image content. The file is created using the process's default permissions and umask. The script does not explicitly restrict access to the owner, encrypt sensitive content, redact inline image data, enforce a retention policy, or limit history size. Consequently, the history may be readable by other local users in permissively configured environments or exposed through backups, synchronization services, shared directories, or accidental source-control commits. Repeated refinement also appends API responses to the history without a si ...[truncated 1363 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create history files with owner-only permissions, such as mode `0600`, rather than relying solely on the process umask. 2. Use an atomic write procedure: create a protected temporary file in the destination directory, flush and synchronize it, and atomically replace the old history. 3. Reject symbolic-link destinations or otherwise ensure that the selected history path cannot redirect writes to an unintended file. 4. Do not store base64-encoded `inlineData` in conversation history unless it is strictly required. Store only the minimum metadata needed for refinement, or place image data in separately protected files. 5. Make persistent history opt-in and provide an ephemeral mode that keeps conversation data only in memory. 6. Add configurable retention, turn-count, and total-size limits. 7. Warn users that the history contains sensitive prompts and potentially complete images, and recommend excluding it from version control, backups, and shared directories. 8. If persistent sensitive histories are required, encrypt them at rest using a key managed separately from the history file. ]]>
