T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/md_to_html.py:166
- Finding
- Stored HTML Injection in Generated Documents<![CDATA[ ## Vulnerability Details **File Location**: `scripts/md_to_html.py`, lines 166–178, with unsafe data reaching HTML output at lines 239–253, 288–290, 309, and 377–380 **Vulnerability Type**: Stored HTML and JavaScript injection **Risk Level**: High ### Vulnerable Code ```python def parse_inline(text): """Convert inline Markdown: bold, italic, code, links, images.""" # images text = re.sub(r'!\[([^\]]*)\]\(([^)]+)\)', r'<img src="\2" alt="\1" style="max-width:100%">', text) # links text = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'<a href="\2" target="_blank">\1</a>', text) # bold text = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', text) # italic text = re.sub(r'\*(.+?)\*', r'<em>\1</em>', text) # inline code text = re.sub(r'`([^`]+)`', r'<code>\1</code>', text) return text ``` The unescaped result is inserted into tables and document content: ```python for cell in table_rows[0]: html.append(f'<th>{parse_inline(cell)}</th>') for row in table_rows[1:]: html.append('<tr>') for cell in row: html.append(f'<td>{parse_inline(cell)}</td>') html.append('</tr>') ``` ```python output.append(f'<p>{parse_inline(line.strip())}</p>') ``` The Markdown title is also inserted into multiple HTML contexts without escaping: ```python title_match = re.search(r'^#\s+(.+)$', md_text, re.MULTILINE) title = title_match.group(1) if title_match else os.path.splitext(os.path.basename(input_path))[0] body = convert_md_to_html(md_text) html = HTML_TEMPLATE.format(title=title, content=body) ``` ### Technical Analysis The converter treats attacker-controlled Markdown as trusted HTML. `parse_inline()` performs regular-expression substitutions but never HTML-escapes ordinary text before returning it. As a result, raw HTML elements and event-handler attributes supplied in headings, paragraphs, list items, blockquotes, or table cells remain active in the generated document. Link destinations and image sour ...[truncated 2031 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. HTML-escape all untrusted plain text before inserting Markdown formatting: ```python import html safe_text = html.escape(text, quote=True) ``` 2. Avoid implementing Markdown parsing through regular-expression substitutions over untrusted text. Use a maintained Markdown library with raw HTML disabled and an explicit sanitization policy. 3. Escape values according to their output context: - Escape text placed inside HTML elements. - Escape quotes and metacharacters in attribute values. - Escape the title separately before inserting it into `<title>` and `<h1>`. 4. Validate link and image URL schemes using a URL parser. Permit only explicitly required schemes, such as `https`, `http`, and optionally `mailto`. Reject `javascript:`, unexpected `data:` URLs, embedded control characters, and obfuscated variants. 5. If limited raw HTML support is required, sanitize the rendered output with an allowlist-based HTML sanitizer. Permit only necessary elements and attributes, and remove scripts, event handlers, dangerous URLs, embedded frames, and active content. 6. Add automated regression tests covering: - Raw `<script>` and other active HTML in paragraphs and headings. - Quotes and angle brackets in link and image destinations. - Event-handler attributes. - Dangerous and mixed-case URL schemes. - Malicious content in titles, tables, lists, and blockquotes. ]]>
