Symfony UX Toolkit CVE-2026-55878
HIGHSeverity by source
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Local console command (AV:L), no auth needed by the kit author (PR:N), but the victim must invoke `ux:install` against the malicious kit (UI:R); arbitrary file read/write yields full C/I/A impact.
Primary rating from Vendor (https://github.com/symfony/ux).
CVSS VectorVendor: https://github.com/symfony/ux
Lifecycle Timeline
2DescriptionCVE.org
Description
The ux:install console command installs files from a recipe kit by copying paths listed in a copy-files map. The only guard against malicious paths was Path::isRelative(), which returns true for paths like ../../../etc. Path::join() then resolves the .. segments without complaint, so the final path can escape the intended directory entirely. A crafted or compromised kit can therefore write attacker-controlled content to arbitrary locations on the developer's machine or CI runner.
Because the copy operation creates missing parent directories and can overwrite existing files silently (with --force or in non-interactive environments), an attacker who controls a kit can overwrite files such as controllers, git hooks, or .env to achieve code execution. The source side of copy-files is symmetrically affected, enabling local file reads outside the recipe directory.
Resolution
The fix introduces an Assert::pathDoesNotEscapeDirectory() helper that rejects any copy-files source or destination path containing a .. segment, regardless of whether / or \ is used as the separator. This check is enforced in both RecipeManifest (which also guards the source Finder) and File. As a last line of defense, the installer re-verifies the fully resolved paths with Path::isBasePath() immediately before each filesystem read and write.
Credits
Symfony would like to thank Pascal Cescon for reporting the issue and Hugo Alliaume for providing the fix.
AnalysisAI
Arbitrary file write and read in Symfony UX Toolkit's ux:install console command allows a crafted or compromised recipe kit to escape the intended installation directory and overwrite or read attacker-controlled files on a developer's machine or CI runner. Because overwriting controllers, git hooks, or .env files yields code execution, this path-traversal flaw escalates to local RCE, and no public exploit identified at time of analysis though the fix and root cause are documented in the upstream GHSA.
Technical ContextAI
The vulnerability lives in the recipe-kit installation logic of the symfony/ux-toolkit Composer package. The copy-files map in a recipe manifest lists source and destination paths, which were only validated with Path::isRelative() - a check that returns true for traversal strings such as ../../../etc. Path::join() then collapses the .. segments without rejecting them, so the resolved path can land anywhere on the filesystem the PHP process can reach. This is a textbook CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) and applies symmetrically: the source side enables out-of-tree reads, and the destination side enables out-of-tree writes, including silent overwrite when --force is supplied or the command runs non-interactively (e.g., CI).
RemediationAI
Vendor-released patch: upgrade symfony/ux-toolkit to 2.36.1 on the 2.x branch or to 3.2.0 on the 3.x branch via composer update symfony/ux-toolkit, as documented in GHSA-p9xj-fpr2-jf2q (https://github.com/symfony/ux/security/advisories/GHSA-p9xj-fpr2-jf2q). The fix introduces an Assert::pathDoesNotEscapeDirectory() helper that rejects .. segments using either separator and re-verifies fully resolved paths with Path::isBasePath() immediately before each read and write. Until the upgrade is applied, do not run ux:install against untrusted or unaudited recipe kits, avoid the --force flag, and review any copy-files manifests for .. segments before installing; in CI, pin kit sources to known-good revisions and run installs in ephemeral containers so that arbitrary writes cannot persist into source trees or credentials stores.
Same weakness CWE-22 – Path Traversal
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-p9xj-fpr2-jf2q