DEV Community

IronSoftware
IronSoftware

Posted on

pdfmake in 2026: The SSRF Fix That Isn't On by Default

Before pdfmake goes into a service, one fact belongs at the top. It ships a current server-side request forgery, CVE-2026-26801, and the fix is off by default. The library itself is a clean idea, since you describe a document as a JavaScript object and it lays out the tables and columns for you. The security posture is the part that needs more than a glance.

IronPDF for Node.js renders the same documents from HTML and does not carry pdfmake's opt-in SSRF policy, which is the difference this piece is about.

Full disclosure. We build IronPDF for Node.js at Iron Software, and this read looks at where pdfmake's opt-in SSRF fix and CSS subset cost a team and where IronPDF renders the same report from HTML.

What are the four CVEs, and which one is still open?

The record shows four CVEs in pdfmake's own code, and one is still open, so each needs stating precisely rather than a blanket warning. CVE-2022-46161 was an unsafe eval() in the dev-playground component, which was never bundled in the npm package, cdnjs, or Packagist releases, so package-manager installs were never exposed, and it was fixed in 0.2.7. CVE-2024-25180 is a reported remote code execution marked disputed in the record, so it belongs on the list but not as a confirmed hit. CVE-2025-11362 is a resource-exhaustion issue from repeated redirects during URL-based file resolution, in the 0.3.0 beta line before general release.

CVE-2026-26801 is the one to watch. It is a server-side request forgery in pdfmake's URLResolver.js, affecting 0.3.0-beta.2 through 0.3.5, patched in 0.3.6 and included in the current 0.3.11, but the patch is not enabled by default. It adds a setUrlAccessPolicy() method for restricting which URLs the library will fetch, and without configuring that policy a server-side deployment logs a warning rather than being protected. If your service resolves remote URLs on pdfmake's behalf, such as user-supplied image URLs, configuring that policy is the actual fix, not upgrading alone.

Rendering the same report, with nothing to fetch

IronPDF renders HTML and CSS through Chromium, so styling is the full language rather than a subset, and there is no pdfmake-style URL resolver to guard, since remote resources load through the browser engine as they do in any browser rather than through a library policy you switch on.

import { PdfDocument } from "@ironsoftware/ironpdf";

const invoice = await PdfDocument.fromHtml(`
  <style>.total { font-weight: bold; color: #1a5fb4; }</style>
  <table><tr><td>Invoice #1042</td><td class="total">$250.00</td></tr></table>
`);

await invoice.saveAs("invoice.pdf");
Enter fullscreen mode Exit fullscreen mode

That renders real CSS with no VFS step for fonts and no library-specific URL policy to remember to enable. The same style block a browser understands, gradients, flexbox, and web fonts loaded by URL, renders straight through, so a design never has to be reduced to a property subset first. IronPDF covers the same tables and columns from ordinary markup, and the HTML-to-PDF guide walks the path end to end.

How pdfmake and IronPDF for Node.js compare

Capability pdfmake IronPDF for Node.js
Document model JSON object, docDefinition HTML and CSS
CSS styling Narrow subset, no gradients or modern CSS Full CSS via Chromium
Custom fonts A build-vfs.js step Standard web font loading
Own-code CVE history Four, one current SSRF None found
Opt-in SSRF policy setUrlAccessPolicy(), off by default Not required
HTML to PDF Not supported Supported, PdfDocument.fromHtml

Table 1. How pdfmake and IronPDF for Node.js compare across the styling and security questions, from pdfmake's own documentation and the GitHub Advisory Database.

What pdfmake gets right

pdfmake's document-definition model fits structured content, and built-in tables with automatic pagination are the standout. Tables and columns are a first-class part of the document rather than something bolted on, content that overflows flows to the next page with no manual calculation of where a page ends, and the same definition runs in Node and the browser, so a document can be previewed client-side before it is generated server-side. It is MIT-licensed, and for a report shaped like tables and columns, that model is more direct than hand-rolled layout or a full browser engine.

Where the report model stops

Where pdfmake stops is the styling model. Its styles dictionary gives you properties like bold, italics, alignment, and fontSize, handy for consistent report formatting but well short of full CSS, so pdfmake has no equivalent for gradients, modern layout, or browser-specific styling, and a design that leans on real CSS has to be cut down to what the subset expresses. Custom fonts add a step of their own, because pdfmake cannot load a font from a file path or URL directly and instead needs a build-vfs.js script to build a virtual filesystem file, registered through addVirtualFileSystem(). And anything outside the content, table, and column model, such as absolute-position or unstructured layout, works against the document model rather than with it, since that model was shaped for report layout and not for arbitrary positioning.

The one case for staying on pdfmake is a document that lives inside that subset and never resolves a remote URL. That holds until the design needs real CSS, though, or until an opt-in SSRF policy becomes a compliance line your team would rather not own, and few reports stay inside those lines for long. If your report has outgrown that subset, IronPDF for Node.js renders it from HTML.

If you run pdfmake server-side and resolve any URLs through it, have you configured setUrlAccessPolicy() yet? Tell us in the comments, we suspect a lot of deployments have not.

pdfmake is an open-source project distributed under the MIT licence, and is not affiliated with Iron Software. The details above are drawn from the project's own documentation, npm metadata, and the GitHub Advisory Database. If something has changed since, correct us in the comments.

Top comments (0)