CVE-2026-49452 Overview
CVE-2026-49452 is a CSS injection vulnerability in WeasyPrint, a Python library that converts HTML documents into PDF files. Versions prior to 69.0 embed unescaped HTML presentational-hint attribute values into generated CSS when presentational_hints=True. The background attribute is inserted into a background-image:url() declaration and parsed by tinycss2.parse_blocks_contents(), allowing attacker-controlled HTML to inject arbitrary CSS declarations. Applications that render untrusted HTML with presentational hints enabled are exposed to CSS injection and server-side request forgery through injected url() values. The issue is tracked as [CWE-74] (Improper Neutralization of Special Elements in Output) and is fixed in version 69.0.
Critical Impact
Untrusted HTML input can inject CSS declarations and trigger server-side requests through crafted url() values, exposing internal network resources.
Affected Products
- WeasyPrint versions prior to 69.0
- Applications rendering untrusted HTML with presentational_hints=True
- Downstream Python packages and services embedding WeasyPrint for PDF generation
Discovery Timeline
- 2026-08-18 - CVE-2026-49452 published to NVD
- 2026-08-19 - Last updated in NVD database
Technical Details for CVE-2026-49452
Vulnerability Analysis
WeasyPrint supports HTML presentational hints, which convert legacy HTML attributes such as background, bgcolor, and width into equivalent CSS declarations. The library performs this translation in weasyprint/css/__init__.py by concatenating attribute values directly into a CSS string. The concatenated text is then handed to tinycss2.parse_blocks_contents() for parsing.
Because the attribute value is not escaped, an attacker who controls the HTML input can close the url() construct and append additional CSS declarations. Injected declarations execute in the styling context of the rendered document. Injected url() values also cause WeasyPrint to issue outbound HTTP requests from the server running the conversion.
Root Cause
The root cause is unsafe string interpolation of untrusted attribute values into a CSS declaration. The background attribute value is inserted into background-image:url(...) without CSS-context escaping or URL validation. Presentational hints share the same parsing pipeline as author stylesheets, so any injected tokens are treated as legitimate CSS.
Attack Vector
An attacker submits HTML containing an element such as <body background="..."> where the attribute value contains characters that terminate the url() function and introduce new CSS blocks. When the target application calls WeasyPrint with presentational_hints=True, the injected CSS is parsed and applied. The attacker can point url() at internal endpoints to perform blind server-side request forgery, or manipulate the rendered PDF's styling to alter document content.
# Patch excerpt from weasyprint/formatting_structure/boxes.py
# The fix replaces the loose parse_html_integer with parse_integer
# and routes presentational hints through HTML-aware parsers.
if self.children:
return len(self.children)
else:
- from ..html import parse_html_integer
+ from ..html import parse_integer
- span = parse_html_integer(self.element.get('span'))
+ span = parse_integer(self.element.get('span'))
return max(span, 1) if span is not None else 1
Source: GitHub Commit e158264
Detection Methods for CVE-2026-49452
Indicators of Compromise
- Outbound HTTP requests from PDF-generation workers to unexpected internal or external hosts during rendering
- HTML input containing background, bgcolor, or similar legacy attributes with parentheses, semicolons, or CSS keywords
- WeasyPrint logs showing fetch attempts for URLs that did not appear in the source document body
Detection Strategies
- Inspect application dependencies for WeasyPrint versions below 69.0 using pip show weasyprint or SBOM tooling
- Review code paths that invoke HTML(...).write_pdf(presentational_hints=True) on user-supplied HTML
- Instrument the PDF-generation service with egress monitoring to detect unauthorized outbound requests
Monitoring Recommendations
- Log and alert on DNS resolutions and HTTP requests originating from PDF-rendering hosts
- Monitor for repeated conversion errors from tinycss2 that may indicate injection attempts
- Correlate PDF-generation activity with identity and network telemetry in your SIEM to spot anomalous fetch patterns
How to Mitigate CVE-2026-49452
Immediate Actions Required
- Upgrade WeasyPrint to version 69.0 or later in all environments that process untrusted HTML
- Audit application code and disable presentational_hints on untrusted input until the upgrade is complete
- Restrict egress from PDF-rendering workers to only the destinations required for legitimate operation
Patch Information
The fix landed in WeasyPrint 69.0 via the pull request that routes presentational hints through HTML-aware parsers rather than raw string concatenation. See the GitHub Security Advisory GHSA-jhhc-3hcp-qhm5, the GitHub Pull Request Discussion, and the GitHub Release Notes v69.0 for full details.
Workarounds
- Set presentational_hints=False (the default) when invoking WeasyPrint on untrusted HTML
- Sanitize input HTML to strip legacy presentational attributes such as background, bgcolor, width, and height before rendering
- Place PDF-generation workers on an isolated network segment with strict egress allow-listing to prevent server-side request forgery
# Upgrade WeasyPrint to a patched release
pip install --upgrade 'weasyprint>=69.0'
# Verify the installed version
python -c "import weasyprint; print(weasyprint.__version__)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

