{"id":"CVE-2026-49359","aliases":["EUVD-2026-38054","GHSA-x8g9-h984-pc36"],"status":"published","title":"PhpWeasyPrint vulnerable to SSRF and local file disclosure via the attachment option","description":"PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.6.0, `pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a local file disclosure primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment. This is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in GHSA-c5fp-p67m-gq56. The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape. PhpWeasyPrint version 2.6.0 contains a patch for the issue.","severity":{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00422,"percentile":0.36821,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-918"],"published":"2026-06-19T17:06:12Z","modified":"2026-10-28T02:31:13Z","fixes":[{"ecosystem":"Packagist","package":"pontedilana/php-weasyprint","fixed_in":["2.6.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"pontedilana/php-weasyprint","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.5.1"},{"fixed":"2.6.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"pontedilana/php-weasyprint","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.6.0"}]}],"versions":["0.10.0","0.10.1","0.11.0","0.12.0","0.13.0","0.9.0","1.0.0","1.0.1","1.1.0","1.1.1","1.2.0","1.3.0","1.4.0","1.5.0","2.0.0","2.1.0","2.2.0","2.3.0","2.4.0","2.5.0","2.5.1"]}],"products":[{"vendor":"pontedilana","product":"php-weasyprint"}],"references":[{"url":"https://github.com/pontedilana/php-weasyprint/security/advisories/GHSA-x8g9-h984-pc36","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/KnpLabs/snappy/security/advisories/GHSA-c5fp-p67m-gq56","tags":["x_refsource_MISC","advisory","web"]},{"url":"https://github.com/pontedilana/php-weasyprint/commit/9582dcf119a405276cf55e9e10bc577a887792cb","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/pontedilana/php-weasyprint/releases/tag/2.6.0","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-49359","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-x8g9-h984-pc36"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/49xxx/CVE-2026-49359.json","tags":["advisory"]},{"url":"https://github.com/pontedilana/php-weasyprint","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-49359","title":"PhpWeasyPrint vulnerable to SSRF and local file disclosure via the attachment option","description":"PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.6.0, `pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a local file disclosure primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment. This is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in GHSA-c5fp-p67m-gq56. The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape. PhpWeasyPrint version 2.6.0 contains a patch for the issue.","status":"published","published":"2026-06-19T17:06:12.737Z","modified":"2026-06-23T02:16:42.011Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T13:35:10.963527Z"}],"euvd":[{"id":"EUVD-2026-38054","description":"PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.6.0, `pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a local file disclosure primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment. This is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in GHSA-c5fp-p67m-gq56. The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape. PhpWeasyPrint version 2.6.0 contains a patch for the issue.","status":"published","published":"2026-06-19T17:06:12Z","modified":"2026-06-23T02:16:42Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T18:20:33.850201Z"}],"ghsa":[{"id":"GHSA-x8g9-h984-pc36","title":"PhpWeasyPrint vulnerable to SSRF and local file disclosure via the attachment option","description":"### Summary\n\n`pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a **local file disclosure** primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment.\n\nThis is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in [GHSA-c5fp-p67m-gq56](https://github.com/KnpLabs/snappy/security/advisories/GHSA-c5fp-p67m-gq56). The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape.\n\n### Affected versions\n\n`pontedilana/php-weasyprint` versions `\u003c= 2.5.1`.\n\nPatched in: `2.6.0`.\n\n### Privilege required\n\nAny caller that can influence the `attachment` option value handed to `Pdf::generate()` / `Pdf::getOutput()` / `setOption('attachment', ...)`. Typical reach paths: a value sourced from a request parameter, a per-tenant configuration row, or any user-controllable field that flows into the attachment list.\n\n### Vulnerable code\n\n`src/Pdf.php` — `isOptionUrl()` accepts any well-formed URL regardless of scheme:\n\n```php\nprotected function isOptionUrl($option): bool\n{\n    return false !== \\filter_var($option, \\FILTER_VALIDATE_URL);\n}\n```\n\n`src/Pdf.php` — `handleArrayOptions()` fetches the URL content for the `attachment` option:\n\n```php\n$fetchUrlContent = 'attachment' === $option \u0026\u0026 $this-\u003eisOptionUrl($item);\nif ($saveToTempFile || $fetchUrlContent) {\n    $fileContent = $fetchUrlContent ? \\file_get_contents($item) : $item;\n    $returnOptions[] = $this-\u003ecreateTemporaryFile($fileContent, $this-\u003eoptionsWithContentCheck[$option] ?? 'temp');\n}\n```\n\n`FILTER_VALIDATE_URL` returns truthy for `http://`, `https://`, `ftp://`, `file://localhost/...`, and `php://filter/...`, so `\\file_get_contents()` is invoked on attacker-chosen schemes with no allow-list.\n\n### Proof of concept\n\n```php\n\u003c?php\nuse Pontedilana\\PhpWeasyPrint\\Pdf;\n\n$pdf = new Pdf('/usr/local/bin/weasyprint');\n\n// Attacker-controlled attachment value (e.g. from a request / tenant config):\n//   SSRF:               http://169.254.169.254/latest/meta-data/iam/security-credentials/\n//   Local file read:    php://filter/convert.base64-encode/resource=/etc/passwd\n$attachment = $_GET['doc'];\n\n$pdf-\u003egenerate('page.html', 'out.pdf', [\n    'attachment' =\u003e $attachment,\n]);\n\n// The bytes fetched server-side by file_get_contents() are embedded in out.pdf,\n// allowing the attacker to read internal HTTP responses or local files.\n```\n\n### Impact\n\n- **SSRF**: the server fetches arbitrary `http(s)`/`ftp` URLs, reaching internal-only services, link-local metadata endpoints, etc.\n- **Local file / wrapper disclosure**: `php://filter/...` (and similar) let an attacker read and exfiltrate local file content inside the generated PDF.\n- Affects any consumer that does not fully control the `attachment` option value.\n\nNote: passing a plain local path (e.g. `/etc/passwd`) or a `file://` path that resolves to an existing file is handled as a normal local attachment and is **not** the issue addressed here — that is the documented local-attachment feature (callers must not pass untrusted input to the option). The fix specifically removes the server-side fetch amplification through non-`http(s)` schemes.\n\nCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N (6.5, Medium) — adjust `PR`/`S`/`A` to the consuming application's reachability (e.g. `PR:N` if the attachment value is reachable from an unauthenticated surface).\n\nCWE-918 (Server-Side Request Forgery); secondary CWE-22 (Improper Limitation of a Pathname) for the wrapper-based file read.\n\n### Suggested fix\n\nRestrict the schemes the library will fetch to an allow-list (`http`, `https` by default), and treat any other scheme as inline content instead of fetching it:\n\n```php\nprivate array $allowedSchemes = ['http', 'https'];\n\n// new optional 4th constructor argument: ?array $allowedSchemes = null\n\nprotected function isOptionUrl($option): bool\n{\n    $url = \\parse_url((string)$option);\n\n    return false !== $url\n        \u0026\u0026 isset($url['scheme'])\n        \u0026\u0026 \\in_array(\\strtolower($url['scheme']), $this-\u003eallowedSchemes, true);\n}\n```\n\nA value with a non-allowed scheme (`file://`, `php://`, `ftp://`, ...) is then never passed to `file_get_contents()`.\n\n### Credit\n\nReported upstream to KnpLabs/snappy ([GHSA-c5fp-p67m-gq56](https://github.com/KnpLabs/snappy/security/advisories/GHSA-c5fp-p67m-gq56)); identified as applicable to `pontedilana/php-weasyprint`, which mirrors the same code.","status":"published","published":"2026-06-26T22:11:40Z","modified":"2026-06-26T22:11:43Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T14:40:34.713036Z"}],"nvd":[{"id":"CVE-2026-49359","description":"PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.6.0, `pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a local file disclosure primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment. This is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in GHSA-c5fp-p67m-gq56. The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape. PhpWeasyPrint version 2.6.0 contains a patch for the issue.","status":"published","published":"2026-06-19T18:16:19.89Z","modified":"2026-10-25T23:10:00.167Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T14:12:24.912854Z"}],"osv":[{"id":"CVE-2026-49359","title":"PhpWeasyPrint vulnerable to SSRF and local file disclosure via the attachment option","description":"PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.6.0, `pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a local file disclosure primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment. This is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in GHSA-c5fp-p67m-gq56. The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape. PhpWeasyPrint version 2.6.0 contains a patch for the issue.","status":"published","published":"2026-06-19T17:06:12.737Z","modified":"2026-10-28T02:31:13.730739568Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T15:45:07.060148Z"},{"id":"GHSA-x8g9-h984-pc36","title":"PhpWeasyPrint vulnerable to SSRF and local file disclosure via the attachment option","description":"### Summary\n\n`pontedilana/php-weasyprint` fetches the content of option values server-side via `file_get_contents()` when the value looks like a URL, without restricting the URL scheme. The `attachment` option of `Pdf` is the reachable sink: any value that passes `isOptionUrl()` (`filter_var(..., FILTER_VALIDATE_URL)`) is downloaded by the PHP process and embedded into the generated PDF. Because `FILTER_VALIDATE_URL` accepts `http`, `https`, `ftp`, `file` and PHP stream wrappers such as `php://`, an attacker who can influence the `attachment` value reaches both a **Server-Side Request Forgery** primitive (e.g. internal HTTP endpoints, cloud metadata) and a **local file disclosure** primitive (`file://`, `php://filter/...`), with the fetched bytes exfiltrated as a PDF attachment.\n\nThis is the same class of issue KnpLabs/snappy patched for its `xsl-style-sheet` option in [GHSA-c5fp-p67m-gq56](https://github.com/KnpLabs/snappy/security/advisories/GHSA-c5fp-p67m-gq56). The library is documented as a one-to-one substitute for KnpLabs/snappy and shares the same code shape.\n\n### Affected versions\n\n`pontedilana/php-weasyprint` versions `\u003c= 2.5.1`.\n\nPatched in: `2.6.0`.\n\n### Privilege required\n\nAny caller that can influence the `attachment` option value handed to `Pdf::generate()` / `Pdf::getOutput()` / `setOption('attachment', ...)`. Typical reach paths: a value sourced from a request parameter, a per-tenant configuration row, or any user-controllable field that flows into the attachment list.\n\n### Vulnerable code\n\n`src/Pdf.php` — `isOptionUrl()` accepts any well-formed URL regardless of scheme:\n\n```php\nprotected function isOptionUrl($option): bool\n{\n    return false !== \\filter_var($option, \\FILTER_VALIDATE_URL);\n}\n```\n\n`src/Pdf.php` — `handleArrayOptions()` fetches the URL content for the `attachment` option:\n\n```php\n$fetchUrlContent = 'attachment' === $option \u0026\u0026 $this-\u003eisOptionUrl($item);\nif ($saveToTempFile || $fetchUrlContent) {\n    $fileContent = $fetchUrlContent ? \\file_get_contents($item) : $item;\n    $returnOptions[] = $this-\u003ecreateTemporaryFile($fileContent, $this-\u003eoptionsWithContentCheck[$option] ?? 'temp');\n}\n```\n\n`FILTER_VALIDATE_URL` returns truthy for `http://`, `https://`, `ftp://`, `file://localhost/...`, and `php://filter/...`, so `\\file_get_contents()` is invoked on attacker-chosen schemes with no allow-list.\n\n### Proof of concept\n\n```php\n\u003c?php\nuse Pontedilana\\PhpWeasyPrint\\Pdf;\n\n$pdf = new Pdf('/usr/local/bin/weasyprint');\n\n// Attacker-controlled attachment value (e.g. from a request / tenant config):\n//   SSRF:               http://169.254.169.254/latest/meta-data/iam/security-credentials/\n//   Local file read:    php://filter/convert.base64-encode/resource=/etc/passwd\n$attachment = $_GET['doc'];\n\n$pdf-\u003egenerate('page.html', 'out.pdf', [\n    'attachment' =\u003e $attachment,\n]);\n\n// The bytes fetched server-side by file_get_contents() are embedded in out.pdf,\n// allowing the attacker to read internal HTTP responses or local files.\n```\n\n### Impact\n\n- **SSRF**: the server fetches arbitrary `http(s)`/`ftp` URLs, reaching internal-only services, link-local metadata endpoints, etc.\n- **Local file / wrapper disclosure**: `php://filter/...` (and similar) let an attacker read and exfiltrate local file content inside the generated PDF.\n- Affects any consumer that does not fully control the `attachment` option value.\n\nNote: passing a plain local path (e.g. `/etc/passwd`) or a `file://` path that resolves to an existing file is handled as a normal local attachment and is **not** the issue addressed here — that is the documented local-attachment feature (callers must not pass untrusted input to the option). The fix specifically removes the server-side fetch amplification through non-`http(s)` schemes.\n\nCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N (6.5, Medium) — adjust `PR`/`S`/`A` to the consuming application's reachability (e.g. `PR:N` if the attachment value is reachable from an unauthenticated surface).\n\nCWE-918 (Server-Side Request Forgery); secondary CWE-22 (Improper Limitation of a Pathname) for the wrapper-based file read.\n\n### Suggested fix\n\nRestrict the schemes the library will fetch to an allow-list (`http`, `https` by default), and treat any other scheme as inline content instead of fetching it:\n\n```php\nprivate array $allowedSchemes = ['http', 'https'];\n\n// new optional 4th constructor argument: ?array $allowedSchemes = null\n\nprotected function isOptionUrl($option): bool\n{\n    $url = \\parse_url((string)$option);\n\n    return false !== $url\n        \u0026\u0026 isset($url['scheme'])\n        \u0026\u0026 \\in_array(\\strtolower($url['scheme']), $this-\u003eallowedSchemes, true);\n}\n```\n\nA value with a non-allowed scheme (`file://`, `php://`, `ftp://`, ...) is then never passed to `file_get_contents()`.\n\n### Credit\n\nReported upstream to KnpLabs/snappy ([GHSA-c5fp-p67m-gq56](https://github.com/KnpLabs/snappy/security/advisories/GHSA-c5fp-p67m-gq56)); identified as applicable to `pontedilana/php-weasyprint`, which mirrors the same code.","status":"published","published":"2026-06-26T22:11:40Z","modified":"2026-09-10T03:51:09.734478312Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T15:46:59.835583Z"}]}}
