T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/ima_image_create.py:747
- Finding
- Sensitive generation data is persisted in operational logs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ima_image_create.py:747-753`, `scripts/ima_image_create.py:1368-1381`, `scripts/ima_image_create.py:1438-1445`, and `scripts/ima_image_create.py:266-269` **Vulnerability Type**: Sensitive information exposure through persistent logging **Risk Level**: Medium ### Vulnerable Code ```python if code not in (0, 200): logger.error(f"Task create failed: code={code}, msg={data.get('message')}, " f"attribute_id={attribute_id}, credit={credit}") raise RuntimeError( f"Create task failed — code={code} " f"message={data.get('message')} " f"request={json.dumps(payload, ensure_ascii=False)}" ) ``` The resulting exception is incorporated into persistent retry diagnostics: ```python except Exception as e: error_info = extract_error_info(e) attempt_log.append({ "attempt": attempt, "result": "failed", "params": current_params.copy(), "error": error_info }) logger.error(f"❌ Attempt {attempt} failed: {error_info['type']} - {error_info['message']}") ``` After all attempts fail, the accumulated data is written to the operational log: ```python logger.error( "Attempt log (debug only): %s", json.dumps(attempt_log, ensure_ascii=False), ) ``` Local input paths are also logged without redaction: ```python with open(source, "rb") as f: image_bytes = f.read() content_type = mimetypes.guess_type(source)[0] or "image/jpeg" logger.info(f"Read local file: {source} ({len(image_bytes)} bytes)") ``` The log sink is enabled by default in `scripts/ima_logger.py:61-74`: ```python file_handler = RotatingFileHandler( log_file, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8' ) file_handler.setLevel(log_level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) ``` ### Technical Analysis The task-creation payload contains the complete user prompt and all input-image URLs ...[truncated 2746 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the serialized request payload from exceptions: ```python raise RuntimeError( f"Create task failed — code={code}, " f"message={sanitize_error_message(data.get('message'))}" ) ``` 2. Log only the minimum diagnostic fields required for troubleshooting, such as: - Error code. - Task type. - Model ID. - Attribute ID. - Correlation or task identifier. - Retry count. 3. Never persist raw prompts, local paths, complete media URLs, URL query strings, or request bodies. Introduce centralized redaction before any value reaches the logger: ```python from urllib.parse import urlsplit, urlunsplit def redact_url(value: str) -> str: parts = urlsplit(value) return urlunsplit((parts.scheme, parts.netloc, parts.path, "", "")) def redact_path(value: str) -> str: return os.path.basename(value) ``` 4. Ensure `extract_error_info()` and retry diagnostics retain structured, sanitized error fields rather than the original exception string. 5. Remove `error_info["message"]` from `attempt_log`, or replace it with a bounded and redacted error category. 6. Make verbose diagnostic logging opt-in rather than enabled by default. Keep production logs at a minimal level. 7. Create the log directory and files with restrictive permissions, such as directory mode `0700` and file mode `0600`, and verify permissions after handler creation. 8. Reduce retention where practical and provide a documented mechanism to disable file logging or immediately delete logs containing user content. 9. Add automated tests asserting that representative secrets, prompts, query parameters, local paths, and image URLs never appear in log output during failed requests and exhausted retries. ]]>
