CVE-2026-55073 Overview
CVE-2026-55073 affects WeasyPrint, a Python library that converts HTML and CSS into PDF documents. Versions prior to 70.0 allow attackers to bypass a restrictive url_fetcher configuration when server-side applications pass attacker-influenced values to HTML.write_pdf(). The bypass occurs through the xmp_metadata and stylesheets options, which internally construct resource loaders without propagating the document's configured fetcher. As a result, an attacker can coerce the application into reading accessible local files and embedding them into the resulting PDF, or loading internal network resources through CSS imports. The issue is classified as Server-Side Request Forgery [CWE-918] and is fixed in WeasyPrint 70.0.
Critical Impact
Server-side WeasyPrint deployments that rely on url_fetcher restrictions to isolate PDF generation from local files and internal networks can be forced to disclose local file contents or reach internal services.
Affected Products
- WeasyPrint versions prior to 70.0
- Server-side applications invoking HTML.write_pdf() with attacker-controlled xmp_metadata values
- Server-side applications invoking HTML.write_pdf() with attacker-controlled stylesheets values
Discovery Timeline
- 2026-09-14 - CVE-2026-55073 published to NVD
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-55073
Vulnerability Analysis
WeasyPrint exposes a url_fetcher parameter that server-side integrators use to restrict which URLs the library may resolve during PDF rendering. This mechanism is the primary defense against Server-Side Request Forgery and local file disclosure when rendering attacker-influenced HTML. The vulnerability arises because two option channels, xmp_metadata and stylesheets, bypass the configured fetcher. In weasyprint/pdf/__init__.py, the XMP metadata code path calls select_source() without passing the document's url_fetcher. In weasyprint/document.py, the stylesheet path constructs a CSS() object without the document's url_fetcher, so the permissive default fetcher propagates through nested CSS @import statements and url() references.
Root Cause
The root cause is inconsistent propagation of the security-relevant url_fetcher object across internal APIs. The HTML object honors the caller-supplied fetcher, but auxiliary code paths reconstruct source-selection and stylesheet-loading logic with default behavior. Any file the WeasyPrint process can read may be embedded verbatim into the output PDF through xmp_metadata, and any resource the process can reach may be loaded through the stylesheet channel.
Attack Vector
Exploitation requires that a server-side application accept attacker-influenced input for either the xmp_metadata list or the stylesheets list passed to HTML.write_pdf(). An attacker submits URLs such as file:///etc/passwd or http://169.254.169.254/ through these options. WeasyPrint resolves them with the default fetcher, ignoring the restrictive fetcher configured on the document. Local files are embedded into the generated PDF as XMP metadata; internal resources fetched through the stylesheet channel are applied but not disclosed verbatim.
# Patch: weasyprint/pdf/__init__.py — propagate document.url_fetcher to select_source()
if options['xmp_metadata']:
for url in options['xmp_metadata']:
- result = select_source(url)
+ result = select_source(url, url_fetcher=document.url_fetcher)
with result as (file_obj, base_url, charset, _):
xmp_metadata = file_obj.read()
if charset:
# Patch: weasyprint/document.py — pass html.url_fetcher to CSS()
css = CSS(
guess=css, media_type=html.media_type,
font_config=font_config, counter_style=counter_style,
- color_profiles=color_profiles)
+ color_profiles=color_profiles, url_fetcher=html.url_fetcher)
user_stylesheets.append(css)
Source: GitHub Commit 289e278
Detection Methods for CVE-2026-55073
Indicators of Compromise
- Generated PDF documents containing embedded contents of local files such as /etc/passwd, application configuration files, or private keys inside XMP metadata streams.
- Outbound HTTP requests from PDF-rendering hosts to internal IP ranges, cloud metadata endpoints (169.254.169.254), or file:// URI resolution attempts logged by the Python process.
- Application logs showing xmp_metadata or stylesheets parameters populated with URLs that originated from untrusted request data.
Detection Strategies
- Inspect server-side code that invokes HTML.write_pdf() and audit whether values supplied to xmp_metadata or stylesheets originate from user input.
- Compare installed WeasyPrint versions against 70.0 using dependency scanners or pip show weasyprint across build and runtime environments.
- Review generated PDFs for XMP metadata streams that contain unexpected file contents or non-metadata payloads.
Monitoring Recommendations
- Log and alert on egress traffic from PDF-rendering workers to link-local, loopback, and internal RFC1918 destinations.
- Instrument the rendering service to record all URLs supplied to WeasyPrint options and flag any file://, gopher://, or internal-hostname schemes.
- Track process-level file reads by the PDF-rendering user account for access to sensitive paths outside the expected working directory.
How to Mitigate CVE-2026-55073
Immediate Actions Required
- Upgrade WeasyPrint to version 70.0 or later across all environments that render user-influenced HTML.
- Audit application code paths that pass values to HTML.write_pdf() and remove attacker control over xmp_metadata and stylesheets where possible.
- Run the PDF-rendering process under a dedicated, least-privileged user account with no access to sensitive files or internal network segments.
Patch Information
The fix is included in WeasyPrint v70.0 and delivered in commit 289e278. The patch ensures the document's url_fetcher is propagated to select_source() in the XMP metadata path and to CSS() construction in the stylesheet path. Full advisory details are available in GHSA-jf6q-chmf-3h3v.
Workarounds
- Do not accept user-supplied values for the xmp_metadata and stylesheets options; hard-code these to trusted, application-controlled resources.
- Enforce network egress restrictions on the rendering host so that requests to loopback, link-local, and internal ranges are blocked at the firewall.
- Execute WeasyPrint in a sandboxed environment such as a container with a read-only filesystem and no access to secrets or cloud metadata endpoints.
# Upgrade WeasyPrint to the patched release
pip install --upgrade 'weasyprint>=70.0'
# Verify 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.

