Every CVE Search/ Get API key

CVE-2026-49260

PhpWeasyPrint: shell command injection via configurable WeasyPrint binary path due to inverted is_executable() guard (mirror of KnpLabs/snappy GHSA-vpr4-p6fq-85jc)

8.2 HighFix available

PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.5.1, pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc. PhpWeasyPrint version 2.5.1 contains a patch for the issue.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
Low
Privileges needed
High
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.21%Up 0.1 points since Jun 23, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.2

PhpWeasyPrint: shell command injection via configurable WeasyPrint binary path due to inverted is_executable() guard (mirror of KnpLabs/snappy GHSA-vpr4-p6fq-85jc)

PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.5.1, pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc. PhpWeasyPrint version 2.5.1 contains a patch for the issue.

Record
CVE-2026-49260
CVSS 3.1
8.2 HighCVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
EUVDEUVD-2026-38048 8.2

PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.5.1, pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc. PhpWeasyPrint version 2.5.1 contains a patch for the issue.

Record
EUVD-2026-38048
CVSS 3.1
8.2 HighCVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-f5gc-qxf8-mh9g 8.2

php-weasyprint: shell command injection via configurable WeasyPrint binary path due to inverted is_executable() guard (mirror of KnpLabs/snappy GHSA-vpr4-p6fq-85jc)

Summary

pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc (Snappy 1.7.1).

Affected versions

pontedilana/php-weasyprint versions <= 2.5.0 (current master tip commit c2b51fed0bf442c3bf0292b879a09944d436f2a0, 2026-04-03).

Patched in: 2.5.1

Privilege required

Any caller that can influence the binary string handed to the Pdf constructor or to AbstractGenerator::setBinary(). Typical reach paths:

  • An application config file (config/services.yaml, .env, helm chart value) read at boot time, where the path is auto-detected from environment or driven by a per-tenant override.
  • An admin UI that lets operators pick between multiple WeasyPrint builds (weasyprint-v60, weasyprint-v66) for compatibility reasons.
  • A multi-tenant SaaS that resolves binary location from a tenant config row.

Once an attacker plants a string containing shell metacharacters in one of those channels, every subsequent generate() call shells out the injected payload as the PHP process user.

Vulnerable code

src/AbstractGenerator.php#L169-L172:

protected function buildCommand(string $binary, string $input, string $output, array $options = []): string
{
    $escapedBinary = \escapeshellarg($binary);
    $command = \is_executable($escapedBinary) ? $escapedBinary : $binary;

src/Pdf.php#L167-L170 overrides buildCommand with the same guard:

protected function buildCommand(string $binary, string $input, string $output, array $options = []): string
{
    $escapedBinary = \escapeshellarg($binary);
    $command = \is_executable($escapedBinary) ? $escapedBinary : $binary;

escapeshellarg($binary) returns a single-quoted string. is_executable() then looks up a file whose name literally contains the surrounding single-quote characters, which essentially never exists. The ternary therefore always falls through to the right-hand side, where $command is the raw, unescaped $binary string. The rest of the command construction (options, input, output) is correctly escaped, so injection has to land in the binary segment — which is exactly the segment configuration-driven deployments treat as trusted.

This is the same primitive KnpLabs/snappy patched in version 1.7.1. The README of php-weasyprint states: "This library is massively inspired by KnpLabs/snappy, of which it aims to be a one-to-one substitute (GeneratorInterface is the same)." The vulnerable buildCommand was copied verbatim and never updated.

How $binary reaches the shell

caller code
  └── new Pdf($binary)                 // src/Pdf.php constructor
        └── parent::__construct($binary)
              └── $this->setBinary($binary)               // src/AbstractGenerator.php:276
                        $this->binary =…
Record
GHSA-f5gc-qxf8-mh9g
CVSS 3.1
8.2 HighCVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
NVD 8.2

PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.5.1, pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc. PhpWeasyPrint version 2.5.1 contains a patch for the issue.

Record
CVE-2026-49260
CVSS 3.1
8.2 HighCVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
OSV 8.2

PhpWeasyPrint: shell command injection via configurable WeasyPrint binary path due to inverted is_executable() guard (mirror of KnpLabs/snappy GHSA-vpr4-p6fq-85jc)

PhpWeasyPrint is a PHP library allowing PDF generation from a URL or an HTML page. Prior to version 2.5.1, pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc. PhpWeasyPrint version 2.5.1 contains a patch for the issue.

Record
CVE-2026-49260
CVSS 3.1
8.2 HighCVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
OSVGHSA-f5gc-qxf8-mh9g 8.2

php-weasyprint: shell command injection via configurable WeasyPrint binary path due to inverted is_executable() guard (mirror of KnpLabs/snappy GHSA-vpr4-p6fq-85jc)

Summary

pontedilana/php-weasyprint builds the shell command for WeasyPrint by passing the binary path through escapeshellarg() first and then checking the quoted result with is_executable(). On POSIX escapeshellarg('/usr/local/bin/weasyprint') returns '/usr/local/bin/weasyprint' with the single-quote characters as part of the string, so is_executable() looks for a file whose actual name includes those quotes. That file never exists, the "safe" branch is dead code, and the raw $binary string (set via the constructor or setBinary()) flows directly into Symfony\Component\Process\Process::fromShellCommandline(). Any deployment whose binary path is sourced from configuration, an environment variable, or a per-tenant setting reaches a shell-command-injection sink. The library is documented as a one-to-one substitute for KnpLabs/snappy and inherited the exact pre-fix codepath KnpLabs patched in GHSA-vpr4-p6fq-85jc (Snappy 1.7.1).

Affected versions

pontedilana/php-weasyprint versions <= 2.5.0 (current master tip commit c2b51fed0bf442c3bf0292b879a09944d436f2a0, 2026-04-03).

Patched in: 2.5.1

Privilege required

Any caller that can influence the binary string handed to the Pdf constructor or to AbstractGenerator::setBinary(). Typical reach paths:

  • An application config file (config/services.yaml, .env, helm chart value) read at boot time, where the path is auto-detected from environment or driven by a per-tenant override.
  • An admin UI that lets operators pick between multiple WeasyPrint builds (weasyprint-v60, weasyprint-v66) for compatibility reasons.
  • A multi-tenant SaaS that resolves binary location from a tenant config row.

Once an attacker plants a string containing shell metacharacters in one of those channels, every subsequent generate() call shells out the injected payload as the PHP process user.

Vulnerable code

src/AbstractGenerator.php#L169-L172:

protected function buildCommand(string $binary, string $input, string $output, array $options = []): string
{
    $escapedBinary = \escapeshellarg($binary);
    $command = \is_executable($escapedBinary) ? $escapedBinary : $binary;

src/Pdf.php#L167-L170 overrides buildCommand with the same guard:

protected function buildCommand(string $binary, string $input, string $output, array $options = []): string
{
    $escapedBinary = \escapeshellarg($binary);
    $command = \is_executable($escapedBinary) ? $escapedBinary : $binary;

escapeshellarg($binary) returns a single-quoted string. is_executable() then looks up a file whose name literally contains the surrounding single-quote characters, which essentially never exists. The ternary therefore always falls through to the right-hand side, where $command is the raw, unescaped $binary string. The rest of the command construction (options, input, output) is correctly escaped, so injection has to land in the binary segment — which is exactly the segment configuration-driven deployments treat as trusted.

This is the same primitive KnpLabs/snappy patched in version 1.7.1. The README of php-weasyprint states: "This library is massively inspired by KnpLabs/snappy, of which it aims to be a one-to-one substitute (GeneratorInterface is the same)." The vulnerable buildCommand was copied verbatim and never updated.

How $binary reaches the shell

caller code
  └── new Pdf($binary)                 // src/Pdf.php constructor
        └── parent::__construct($binary)
              └── $this->setBinary($binary)               // src/AbstractGenerator.php:276
                        $this->binary =…
Record
GHSA-f5gc-qxf8-mh9g
CVSS 3.1
8.2 HighCVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
pontedilana/php-weasyprint
Packagist
through 2.5.0
2.5.1
pontedilana/php-weasyprint
Packagist
before 2.5.1, plus listed versions
0.10.00.10.10.11.00.12.00.13.0
Show 15 moreShow less
0.9.01.0.01.0.11.1.01.1.11.2.01.3.01.4.01.5.02.0.02.1.02.2.02.3.02.4.02.5.0
2.5.1

History

History
RecordedChangedSources
aliases, publishedEUVD
first indexedCVE List, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-49260 is a high vulnerability (CVSS 8.2) in Pontedilana Php-weasyprint, published June 19, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.21%, the 11th percentile. Fixed in pontedilana/php-weasyprint 2.5.1.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open