CVE-2026-102601
Flysystem: WhitespacePathNormalizer's control-character (CorruptedPathDetected) check is bypassed by malformed UTF-8 in the path, affecting every adapter
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
3.5
Flysystem: WhitespacePathNormalizer's control-character (CorruptedPathDetected) check is bypassed by malformed UTF-8 in the path, affecting every adapter
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
- Record
CVE-2026-102601- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-150
- Updated
- 2026-11-02
EUVDEUVD-2026-88998
3.5
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
- Record
EUVD-2026-88998- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cxf4-7mrp-vvpr
3.5
Flysystem: WhitespacePathNormalizer's control-character (CorruptedPathDetected) check is bypassed by malformed UTF-8 in the path, affecting every adapter
Related public issue (context, not a duplicate)
Closed issue #1429 ("Handle non-UTF-8 paths", 2024-03-24) raised exactly this general concern and
even suggested detection via preg_match('//u', $path) !== 1 -- note the reporter's suggested check
explicitly compares !== 1, which would correctly treat PCRE's false return as "reject." The
maintainer's reply pointed to the PathNormalizer interface as the place to implement this. The
control-character check that ended up shipping in WhitespacePathNormalizer
(if (preg_match('#\p{C}+#u', $unixPath))) addresses the general concern but does not use the
!== 1-style comparison the original issue suggested -- it uses a bare truthy check, which is exactly
the gap this report demonstrates. So this is not a duplicate of #1429; it's a concrete bypass
surviving in the fix that issue's concern led to.
Vulnerability Details
File: src/WhitespacePathNormalizer.php, lines 22-28 (normalizePath()) -- the default
PathNormalizer used by Filesystem for every adapter (Local, FTP, SFTP, S3, AsyncAwsS3, Azure, GCS,
ZipArchive, GridFS, InMemory) unless the application supplies a custom one.
Root Cause
public function normalizePath(string $path): string
{
$unixPath = str_replace('\\', '/', $path);
if (preg_match('#\p{C}+#u', $unixPath)) {
throw CorruptedPathDetected::forPath($path);
}
...
preg_match() returns false (a PHP engine error) rather than 0 when the subject string is not
valid UTF-8 and the pattern uses the /u modifier -- PCRE can't even attempt the match. false and
0 are both falsy in PHP, and if (preg_match(...)) does not distinguish them. So a path containing
any single invalid UTF-8 byte anywhere in the string makes preg_match() fail with a "Malformed
UTF-8 characters" engine error, the if evaluates false, and CorruptedPathDetected is silently not
thrown -- even when the same string also contains literal control characters this exact check exists
to catch.
the identical payload IS correctly rejected once it's valid UTF-8:
$n->normalizePath("foo\x1bbar"); // valid UTF-8, contains ESC -> throws CorruptedPathDetected (correct)
$n->normalizePath("foo\x80\x1bbar"); // 0x80 = invalid lone UTF-8 continuation byte -> NOT thrown (bypass)
The path-traversal protection is unaffected -- it's exact byte-string comparison on
/-delimited segments, independent of UTF-8 validity:
$n->normalizePath("\x80/../../etc/passwd"); // still throws PathTraversalDetected
Recommended Fix
public function normalizePath(string $path): string
{
$unixPath = str_replace('\\', '/', $path);
$matched = preg_match('#\p{C}+#u', $unixPath);
if ($matched !== 0) {
// $matched === false means malformed UTF-8 -- must also be treated as corrupted,
// not silently allowed through.
throw CorruptedPathDetected::forPath($path);
}
...
Verification
Dynamically confirmed on league/flysystem HEAD 6837e1d / tag 3.35.2, PHP 8.4.22 CLI, end-to-end
through LocalFilesystemAdapter:
[1] write() succeeded -- normalizer did NOT reject the path.
[2] Actual bytes on disk: ...801b5b386d6e6f726d616c2d6c6f6f6b696e672d66696c652e7478741b5b306d...
[3] Filesystem::listContents() path contains raw ESC (0x1b): YES
[4] Raw terminal output (via `cat -v`): M-^@^[[8mnormal-looking-file.txt^[[0m^[[2K^[[1Aurgent-invoice.pdf
- Record
GHSA-cxf4-7mrp-vvpr- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-150
- Updated
- 2026-11-02
NVD
3.5
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
- Record
CVE-2026-102601- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-150
- Updated
- 2026-11-02
OSV
3.5
Flysystem: WhitespacePathNormalizer's control-character (CorruptedPathDetected) check is bypassed by malformed UTF-8 in the path, affecting every adapter
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
- Record
CVE-2026-102601- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-150
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-102601
3.5
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
- Record
DEBIAN-CVE-2026-102601- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-cxf4-7mrp-vvpr
3.5
Flysystem: WhitespacePathNormalizer's control-character (CorruptedPathDetected) check is bypassed by malformed UTF-8 in the path, affecting every adapter
Related public issue (context, not a duplicate)
Closed issue #1429 ("Handle non-UTF-8 paths", 2024-03-24) raised exactly this general concern and
even suggested detection via preg_match('//u', $path) !== 1 -- note the reporter's suggested check
explicitly compares !== 1, which would correctly treat PCRE's false return as "reject." The
maintainer's reply pointed to the PathNormalizer interface as the place to implement this. The
control-character check that ended up shipping in WhitespacePathNormalizer
(if (preg_match('#\p{C}+#u', $unixPath))) addresses the general concern but does not use the
!== 1-style comparison the original issue suggested -- it uses a bare truthy check, which is exactly
the gap this report demonstrates. So this is not a duplicate of #1429; it's a concrete bypass
surviving in the fix that issue's concern led to.
Vulnerability Details
File: src/WhitespacePathNormalizer.php, lines 22-28 (normalizePath()) -- the default
PathNormalizer used by Filesystem for every adapter (Local, FTP, SFTP, S3, AsyncAwsS3, Azure, GCS,
ZipArchive, GridFS, InMemory) unless the application supplies a custom one.
Root Cause
public function normalizePath(string $path): string
{
$unixPath = str_replace('\\', '/', $path);
if (preg_match('#\p{C}+#u', $unixPath)) {
throw CorruptedPathDetected::forPath($path);
}
...
preg_match() returns false (a PHP engine error) rather than 0 when the subject string is not
valid UTF-8 and the pattern uses the /u modifier -- PCRE can't even attempt the match. false and
0 are both falsy in PHP, and if (preg_match(...)) does not distinguish them. So a path containing
any single invalid UTF-8 byte anywhere in the string makes preg_match() fail with a "Malformed
UTF-8 characters" engine error, the if evaluates false, and CorruptedPathDetected is silently not
thrown -- even when the same string also contains literal control characters this exact check exists
to catch.
the identical payload IS correctly rejected once it's valid UTF-8:
$n->normalizePath("foo\x1bbar"); // valid UTF-8, contains ESC -> throws CorruptedPathDetected (correct)
$n->normalizePath("foo\x80\x1bbar"); // 0x80 = invalid lone UTF-8 continuation byte -> NOT thrown (bypass)
The path-traversal protection is unaffected -- it's exact byte-string comparison on
/-delimited segments, independent of UTF-8 validity:
$n->normalizePath("\x80/../../etc/passwd"); // still throws PathTraversalDetected
Recommended Fix
public function normalizePath(string $path): string
{
$unixPath = str_replace('\\', '/', $path);
$matched = preg_match('#\p{C}+#u', $unixPath);
if ($matched !== 0) {
// $matched === false means malformed UTF-8 -- must also be treated as corrupted,
// not silently allowed through.
throw CorruptedPathDetected::forPath($path);
}
...
Verification
Dynamically confirmed on league/flysystem HEAD 6837e1d / tag 3.35.2, PHP 8.4.22 CLI, end-to-end
through LocalFilesystemAdapter:
[1] write() succeeded -- normalizer did NOT reject the path.
[2] Actual bytes on disk: ...801b5b386d6e6f726d616c2d6c6f6f6b696e672d66696c652e7478741b5b306d...
[3] Filesystem::listContents() path contains raw ESC (0x1b): YES
[4] Raw terminal output (via `cat -v`): M-^@^[[8mnormal-looking-file.txt^[[0m^[[2K^[[1Aurgent-invoice.pdf
- Record
GHSA-cxf4-7mrp-vvpr- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-150
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-102601
3.5
Flysystem is an open source file storage library for PHP. Prior to 3.35.3, the default WhitespacePathNormalizer in src/WhitespacePathNormalizer.php used by Filesystem across adapters calls preg_match with the u modifier and treats both false and 0 as falsy. A path containing malformed UTF-8 causes PCRE to return false, so paths that also contain control characters bypass CorruptedPathDetected::forPath() in normalizePath(). Filesystem::write() can store such names and Filesystem::listContents() can return the raw ANSI escape sequences, allowing hidden or spoofed terminal file listings when an administrator displays them. This issue is fixed in version 3.35.3.
- Record
UBUNTU-CVE-2026-102601- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
OSVCLSA-2026-1790868478 No score
TuxCare security update for league/flysystem (1 CVE)
TuxCare rebuilt league/flysystem to address 1 CVE. Fixed in 3.33.0-p1+tuxcare.
- Record
CLSA-2026-1790868478- Updated
- 2026-11-02
OSVCLSA-2026-1791742369 No score
TuxCare security update for league/flysystem (1 CVE)
TuxCare rebuilt league/flysystem to address 1 CVE. Fixed in 1.0.70-p2+tuxcare.
- Record
CLSA-2026-1791742369- Updated
- 2026-11-02
OSVCLSA-2026-1791742447 No score
TuxCare security update for league/flysystem (1 CVE)
TuxCare rebuilt league/flysystem to address 1 CVE. Fixed in 1.1.10-p2+tuxcare.
- Record
CLSA-2026-1791742447- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
league/flysystemPackagist |
through 3.35.2 | 3.35.3 |
league/flysystemTuxCare:Packagist |
3.33.0 up to 3.33.0-p1+tuxcare | 3.33.0-p1+tuxcare |
league/flysystemTuxCare:Packagist |
1.0.70 up to 1.0.70-p2+tuxcare, plus listed versions 1.0.70-p1+tuxcare |
1.0.70-p2+tuxcare |
league/flysystemTuxCare:Packagist |
1.1.10 up to 1.1.10-p2+tuxcare, plus listed versions 1.1.10-p1+tuxcare |
1.1.10-p2+tuxcare |
php-league-flysystemDebian:12 |
All versions, plus listed versions 1.1.10-11.1.10-23.28.0-13.29.1-13.29.1-23.36.0-1 |
None known |
php-league-flysystemDebian:13 |
All versions, plus listed versions 3.29.1-23.36.0-1 |
None known |
php-league-flysystemDebian:14 |
before 3.36.0-1, plus listed versions 3.29.1-2 |
3.36.0-1 |
league/flysystemPackagist |
before 3.35.3, plus listed versions 0.1.00.1.10.1.100.1.110.1.12 Show 45 moreShow less0.1.130.1.140.1.150.1.160.1.170.1.180.1.190.1.20.1.200.1.30.1.40.1.50.1.60.1.70.1.80.1.90.2.00.2.10.2.100.2.110.2.120.2.130.2.140.2.150.2.20.2.30.2.40.2.50.2.60.2.70.2.80.2.90.3.00.3.10.3.20.3.30.3.40.3.50.4.00.4.10.4.20.4.30.4.40.4.50.5.0 |
3.35.3 |
php-league-flysystemUbuntu:16.04:LTS |
All versions, plus listed versions 1.0.15-11.0.16-11.0.16-1build1 |
None known |
php-league-flysystemUbuntu:22.04:LTS |
All versions, plus listed versions 1.1.3-4 |
None known |
php-league-flysystemUbuntu:24.04:LTS |
All versions, plus listed versions 1.1.10-1 |
None known |
php-league-flysystemUbuntu:26.10 |
All versions, plus listed versions 3.29.1-2 |
None known |
php-league-flysystemUbuntu:26.04:LTS |
All versions, plus listed versions 3.29.1-2 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-102601 is a low vulnerability (CVSS 3.5) in Thephpleague Flysystem, published September 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.34%, the 27th percentile. Fixed in league/flysystem 3.35.3.
References
github.com/thephpleague/flysystem/security/advisories/GHSA-cxf4-7mrp-vvpr
github.com/thephpleague/flysystem/commit/ef4a9a557d769b5d472c403125716706a0d9cc77
github.com/thephpleague/flysystem/releases/tag/3.35.3
nvd.nist.gov/vuln/detail/CVE-2026-102601
github.com/advisories/GHSA-cxf4-7mrp-vvpr
github.com/CVEProject/cvelistV5/tree/main/cves/2026/102xxx/CVE-2026-102601.json
Left out here: 206 versions. The JSON record has them.