T09 · Insecure Skill Coding Practices
- Location
scripts/save_image.py:110- Finding
Stored Terminal-Control Sequence Injection Through Image Metadata
- Content
View full analysis
Vulnerability Details
File Locations:
scripts/save_image.py:110-112scripts/search_image.py:68-71scripts/list_images.py:88-91
Vulnerability Type: Stored terminal-control sequence injection
Risk Level: LowVulnerable Code
scripts/save_image.py:110-112stores user-controlled metadata without validation:python "tags": tags or [], "description": description, "saved_at": datetime.now().isoformat(),scripts/search_image.py:68-71subsequently prints that metadata directly to the terminal:python tags_str = ', '.join(img.get('tags', [])) print(f" 📷 {img['id']}") print(f" 类别: {img['category']} | 标签: {tags_str}") print(f" 描述: {img.get('description', '无')}")scripts/list_images.py:88-91also prints stored metadata without terminal-safe encoding:python tags_str = ', '.join(img.get('tags', [])) desc = img.get('description', '')[:40] print(f" [{img['category']}] {img['id']}") print(f" {tags_str} | {desc}")Technical Analysis
The
--tagsand--descriptioncommand-line parameters are accepted as arbitrary strings and persisted inmedia/index.json. When the search or listing commands are later invoked, these stored values are written directly to an interactive terminal.No filtering or escaping is applied to control characters such as ESC (
\x1b) or to ANSI and OSC terminal sequences. Consequently, metadata can contain sequences interpreted by the terminal rather than displayed as ordinary text. Truncating a description to 40 characters does not neutralize such sequences because a complete control sequence may fit within that limit.Depending on the terminal emulator and its configuration, crafted metadata could manipulate colors and cursor placement, erase or replace visible output, create deceptive hyperlinks, modify a terminal title, or invoke terminal-specific operations such as OSC clipboard handling. The affected code does not itself pass the data to a shell, so this is not dir ...[truncated 1448 chars]
- Remediation
View remediation
Remediation Suggestions
- Introduce a shared terminal-safe rendering function and use it for every value read from
index.jsonbefore printing it in human-readable mode. - Remove or visibly escape C0 and C1 control characters, particularly ESC (
\x1b), BEL (\x07), carriage return, and other non-printable characters. - Preserve newline and tab characters only when explicitly required; otherwise encode them as visible representations such as
\nand\t. - Validate metadata when it is saved, imposing reasonable length limits and rejecting control characters. Output sanitization must still be retained as defense in depth because
index.jsoncan be modified independently. - Keep machine-readable JSON output separate from terminal presentation. If raw metadata must remain available in JSON, document that downstream consumers must treat it as untrusted data.
- Add regression tests using ANSI CSI sequences, OSC hyperlinks, OSC clipboard sequences, carriage returns, and embedded newlines to verify that they are rendered harmlessly.
- Consider a sanitizer similar to:
python import re CONTROL_CHARS = re.compile( r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]' ) def terminal_safe(value): value = str(value) value = value.replace('\x1b', r'\x1b') value = value.replace('\r', r'\r') value = value.replace('\n', r'\n') return CONTROL_CHARS.sub( lambda match: f'\\x{ord(match.group()):02x}', value )Apply this function to identifiers, categories, tags, descriptions, paths, and timestamps before human-readable terminal output.
- Introduce a shared terminal-safe rendering function and use it for every value read from
