T09 · Insecure Skill Coding Practices
- Location
scripts/report_generator.py:151- Finding
Unescaped Report Content Is Embedded into Active HTML During PDF Conversion
- Content
View full analysis
Vulnerability Details
File Location:
scripts/report_generator.py, lines 151–153
Vulnerability Type: HTML injection and unrestricted resource loading during document conversion
Risk Level: MediumVulnerable Code
python with open(markdown_path, "r", encoding="utf-8") as f: html = f"<html><body><pre>{f.read()}</pre></body></html>" HTML(string=html).write_pdf(str(pdf_path))Technical Analysis
The WeasyPrint fallback reads the Markdown report and interpolates it directly into an HTML document without HTML escaping. The
<pre>element does not provide a security boundary: input containing</pre>can terminate it and introduce arbitrary HTML and CSS that WeasyPrint will process.For example, attacker-controlled report content could contain:
html </pre><img src="https://attacker.example/track">Resource-bearing HTML or CSS may cause the PDF renderer to request remote URLs. Depending on the renderer's supported URL schemes and runtime permissions, references to accessible local resources may also be rendered or incorporated into the resulting PDF.
The primary Pandoc/wkhtmltopdf conversion path is also not configured here with an explicit resource allowlist, network isolation, or local-file restrictions. The confirmed unsafe interpolation, however, is specifically present in the WeasyPrint fallback shown above.
Attack Path
- An attacker gains control over, or can influence, the Markdown file supplied as
markdown_pathtoexport_pdf(). - The attacker inserts a closing
</pre>tag followed by resource-bearing HTML or CSS. - Pandoc conversion is unavailable or fails, causing execution to continue to the WeasyPrint fallback.
- The report is concatenated into active HTML without escaping.
- WeasyPrint parses the injected markup while generating the PDF.
- The renderer requests an attacker-controlled URL or attempts to load another resource reachable from its execution environment.
- The attacker may observ ...[truncated 808 chars]
- An attacker gains control over, or can influence, the Markdown file supplied as
- Remediation
View remediation
Remediation Suggestions
- Escape report text before placing it inside an HTML
<pre>element:
python import html with open(markdown_path, "r", encoding="utf-8") as f: escaped_report = html.escape(f.read()) document = f"<html><body><pre>{escaped_report}</pre></body></html>" HTML(string=document).write_pdf(str(pdf_path))-
If Markdown rendering is required, use a maintained Markdown parser configured to disable raw HTML rather than concatenating untrusted text into an HTML document.
-
Configure a custom WeasyPrint URL fetcher that rejects all resource schemes and destinations except an explicit allowlist. In particular, deny remote HTTP/HTTPS URLs and local
file:URLs unless they are necessary. -
Run PDF conversion in an isolated process or container with:
- No unnecessary network access.
- A read-only filesystem.
- Access only to the input and output files.
- A dedicated unprivileged account.
- CPU, memory, file-size, and execution-time limits.
-
Apply equivalent resource restrictions to the Pandoc/wkhtmltopdf path. Do not rely on renderer defaults for local-file or network isolation.
-
Validate that
markdown_pathrefers to an expected regular file in an approved report directory before processing it.
- Escape report text before placing it inside an HTML
