T09 · Insecure Skill Coding Practices
- Location
scripts/convert.py:112- Finding
Stored HTML and JavaScript Injection in Generated Pages
- Content
View full analysis
Vulnerability Details
File Location:
scripts/convert.py:49-54, 63-68, 72-79, 101-103, 112-128
Vulnerability Type: Unsanitized Markdown content and attribute injection
Risk Level: HighThe converter places attacker-controlled Markdown content into an HTML document without first escaping or sanitizing it. Link destinations and code-fence language identifiers are also inserted into HTML attributes without contextual encoding.
Vulnerable Code
The ordinary paragraph path inserts the result of
process_inline_formatting()directly into HTML:python # Normal paragraph text = process_inline_formatting(line) html.append(f'<p>{text}</p>')Header and list content use the same unsafe formatting function:
python # Headers if line.startswith('#'): level = len(line) - len(line.lstrip('#')) text = line.lstrip('#').strip() text = process_inline_formatting(text) html.append(f'<h{level}>{text}</h{level}>') i += 1 continue # Unordered lists if line.strip().startswith(('- ', '* ', '+ ')): if not in_list: in_list = True list_items = [] item_text = re.sub(r'^[\s\-\*\+]+', '', line).strip() item_text = process_inline_formatting(item_text) list_items.append(f'<li>{item_text}</li>') i += 1 continueThe inline formatter performs substitutions without HTML-escaping the original text. It also places an unrestricted link destination directly into an
hrefattribute:python def process_inline_formatting(text): """Process inline markdown formatting""" # Bold text = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', text) text = re.sub(r'__(.+?)__', r'<strong>\1</strong>', text) # Italic text = re.sub(r'\*(.+?)\*', r'<em>\1</em>', text) text = re.sub(r'_(.+?)_', r'<em>\1</em>', text) ...[truncated 3374 chars]- Remediation
View remediation
Remediation Suggestions
- Replace the regular-expression converter with a maintained Markdown parser configured to disable raw HTML, or sanitize the parser output with a strict HTML allowlist.
- Escape all untrusted source text before constructing HTML. Apply encoding appropriate to the destination context, distinguishing element text from attribute values.
- Preserve Markdown formatting through parsed tokens rather than escaping text after HTML tags have already been introduced.
- Validate link schemes explicitly. Permit only required schemes such as
https,http, and optionallymailto; rejectjavascript:,data:,vbscript:, and unknown schemes. - HTML-escape both link labels and attribute values.
- Restrict code-fence language identifiers to a safe pattern such as
^[A-Za-z0-9_-]+$. Reject or discard values that do not match. - Add
rel="noopener noreferrer"to every link usingtarget="_blank". - Consider adding a restrictive Content Security Policy to the generated template, for example one that disallows inline scripts and external resources. This should be defense in depth rather than a replacement for sanitization.
- Add regression tests for raw script elements, event-handler attributes,
javascript:links, quotes in link destinations, malicious code-fence identifiers, and malformed Markdown. - Clearly document whether raw HTML is intentionally supported. If it is required, process it with an allowlist sanitizer before inserting it into the template.
