{"id":"GHSA-6prc-h956-hxjq","kind":"vulnerability","aliases":["GHSA-6prc-h956-hxjq"],"related":[],"title":"Filesystem Path Treats Any Prefix Before \"://\" as a Root, Letting Paths Escape Their Base Directory","description":"### Description\n\nMost methods of `Symfony\\Component\\Filesystem\\Path` rely on `Path::split()`, which takes everything before the first `://` as the root of a path. It doesn't check that this prefix is a URI scheme, so the `..` segments in that prefix are never resolved. PHP sees a scheme only at the start of a string. So `/base/../x://../secret.txt` is a plain local path to `/secret.txt` for PHP, while `Path::isBasePath('/base', '/base/../x://../secret.txt')` returns `true`. `canonicalize()`, `join()`, `makeAbsolute()` and `makeRelative()` are affected the same way.\n\nSome applications check a path built from user input with `isBasePath()` or `canonicalize()`, and then open it. They can be made to read or write files outside the expected directory.\n\nOn Symfony 6.4 and later, the AssetMapper component builds with `Path::join()` the path of the files that a remote package references from its CSS. A malicious or compromised package can therefore make `importmap:require` or `importmap:install` write a file of its choice outside `assets/vendor`. For example, the CSS `url(../../../../../public/x://shell.php)` writes `public/x:/shell.php`.\n\n### Resolution\n\nA prefix is now a scheme only when PHP would look for a stream wrapper with that name. That is two or more letters, digits, `+`, `-` or `.`, followed by `://` at the start of the path. Other prefixes are regular path segments, and the `..` segments after them are resolved. Real schemes such as `phar://`, `file://`, `vfs://` or `compress.zlib://` keep working.\n\nThe patch for this issue is available [here](https://github.com/symfony/symfony/commit/9441dd5f05e78ad898317083828c1acebff586a1) for branch 5.4 (and forward-ported to 6.4, 7.4, 8.1 and 8.2).\n\n### Credits\n\nWe would like to thank Nicolas Grekas for reporting the issue and providing the fix.","summary":"The Symfony Filesystem Path helpers treat anything before the first \"://\" as a root, so \"..\" segments in that prefix are not resolved. An application that checks or opens user-built paths with isBasePath(), canonicalize() or similar can be made to read or write files outside its intended directory; AssetMapper can also be made to write files outside assets/vendor via a malicious remote package's CSS.","severity":"high","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"symfony/filesystem","note":">=5.4.0, <5.4.56","fixed":["5.4.56"],"affected":">= 5.4.0, < 5.4.56","ecosystem":"composer"},{"name":"symfony/filesystem","note":">=6, <6.4.50","fixed":["6.4.50"],"affected":">= 6, < 6.4.50","ecosystem":"composer"},{"name":"symfony/filesystem","note":">=7, <7.4.23","fixed":["7.4.23"],"affected":">= 7, < 7.4.23","ecosystem":"composer"},{"name":"symfony/filesystem","note":">=8, <8.1.11","fixed":["8.1.11"],"affected":">= 8, < 8.1.11","ecosystem":"composer"},{"name":"symfony/symfony","note":">=5.4.0, <5.4.56","fixed":["5.4.56"],"affected":">= 5.4.0, < 5.4.56","ecosystem":"composer"},{"name":"symfony/symfony","note":">=6, <6.4.50","fixed":["6.4.50"],"affected":">= 6, < 6.4.50","ecosystem":"composer"},{"name":"symfony/symfony","note":">=7, <7.4.23","fixed":["7.4.23"],"affected":">= 7, < 7.4.23","ecosystem":"composer"},{"name":"symfony/symfony","note":">=8, <8.1.11","fixed":["8.1.11"],"affected":">= 8, < 8.1.11","ecosystem":"composer"}],"ecosystems":["composer"],"tags":["assetmapper","composer","cwe-22","package","php","symfony","symfony-filesystem"],"not_affected_if":["auth-required","no-user-input-in-paths","static-paths-only"],"how_to_check":"- Check the installed version with `composer show symfony/filesystem` and `composer show symfony/symfony`, and compare against the fixed versions below.\n- Search application code for calls to `Path::isBasePath`, `Path::canonicalize`, `Path::join` or `Path::makeAbsolute` applied to user-supplied paths before a file is opened.\n- On Symfony 6.4 or later, check whether the AssetMapper component is used with `importmap:require` or `importmap:install`.","fix":"- `symfony/filesystem` 5.4 branch: `5.4.56`; 6.x: `6.4.50`; 7.x: `7.4.23`; 8.x: `8.1.11`\n- `symfony/symfony` 5.4 branch: `5.4.56`; 6.x: `6.4.50`; 7.x: `7.4.23`; 8.x: `8.1.11`","status":"open","published_at":"2026-11-04T11:43:35+00:00","first_seen_at":"2026-11-09T22:53:32+00:00","triaged_at":"2026-11-09T22:58:42+00:00","updated_at":"2026-11-09T22:58:42+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:58:42Z","model":"claude-haiku-5-5","output":{"fix":"- `symfony/filesystem` 5.4 branch: `5.4.56`; 6.x: `6.4.50`; 7.x: `7.4.23`; 8.x: `8.1.11`\n- `symfony/symfony` 5.4 branch: `5.4.56`; 6.x: `6.4.50`; 7.x: `7.4.23`; 8.x: `8.1.11`","reach":"unknown","title":"Symfony Filesystem: paths with a prefix before \"://\" escape their base directory","impact":"medium","summary":"The Symfony Filesystem Path helpers treat anything before the first \"://\" as a root, so \"..\" segments in that prefix are not resolved. An application that checks or opens user-built paths with isBasePath(), canonicalize() or similar can be made to read or write files outside its intended directory; AssetMapper can also be made to write files outside assets/vendor via a malicious remote package's CSS.","audience":"developers","category":"package","nicknames":[],"who_cares":["symfony","php","composer","symfony-filesystem","assetmapper"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"Attacker reads or writes files outside the expected directory.","analyst_note":"product_name not needed; products listed.","how_to_check":"- Check the installed version with `composer show symfony/filesystem` and `composer show symfony/symfony`, and compare against the fixed versions below.\n- Search application code for calls to `Path::isBasePath`, `Path::canonicalize`, `Path::join` or `Path::makeAbsolute` applied to user-supplied paths before a file is opened.\n- On Symfony 6.4 or later, check whether the AssetMapper component is used with `importmap:require` or `importmap:install`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-user-input-in-paths","auth-required","static-paths-only"],"who_is_affected":"Applications that use symfony/filesystem or the full symfony/symfony framework and pass user-influenced paths into Path::isBasePath(), canonicalize(), join(), makeAbsolute() or makeRelative() before reading or writing files. On Symfony 6.4 and later, projects using AssetMapper with remote packages are also exposed to a malicious package writing outside assets/vendor. Projects that never build paths from untrusted input with these helpers are unlikely to be affected.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"unknown","severity_if_unknown":"high"},"cost_usd":0.000979,"input_tokens":5456,"output_tokens":867,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-22':37B '/../../../../public/x':275C '/../secret.txt':166C,181C '/base':179C '/base/../x':165C,180C '/secret.txt':173C '/shell.php':276C,279C '/symfony/symfony/commit/9441dd5f05e78ad898317083828c1acebff586a1)':346C '5.4':349C '6.4':226C,355C '6prc':18A,23A '7.4':356C '8.1':357C '8.2':359C 'affect':190C 'also':94B 'anyth':50B 'applic':66B,195C 'assetmapp':34B,92B,230C 'assets/vendor':101B,269C 'avail':342C 'base':14A 'branch':348C 'build':232C 'built':73B,199C 'canonic':77B,184C,206C 'check':68B,136C,196C 'choic':267C 'compon':114C,231C 'compos':35B 'compress.zlib':333C 'compromis':252C 'credit':360C 'css':108B,248C,273C 'cwe':36B 'descript':109C 'digit':305C 'directori':15A,91B,223C 'doesn':134C 'escap':12A 'everyth':123C 'exampl':271C 'expect':222C 'file':87B,99B,219C,240C,264C 'filesystem':1A,43B,46B,115C 'first':53B,126C 'fix':375C 'follow':307C 'forward':352C 'forward-port':351C 'ghsa':17A,22A 'ghsa-6prc-h956-hxjq':16A,21A 'github.com':345C 'github.com/symfony/symfony/commit/9441dd5f05e78ad898317083828c1acebff586a1)':344C 'greka':367C 'h956':19A,24A 'helper':48B 'hxjq':20A,25A 'importmap':257C,260C 'input':202C 'instal':261C 'intend':90B 'isbasepath':76B,178C,204C 'issu':340C,371C 'join':185C,235C 'keep':334C 'later':228C 'let':10A 'letter':304C 'like':363C 'local':170C 'look':291C 'made':82B,96B,214C 'make':256C 'makeabsolut':186C 'makerel':188C 'malici':104B,250C 'method':111C 'name':298C 'never':151C 'nicola':366C 'open':70B,209C 'outsid':88B,100B,220C,268C 'packag':38B,106B,244C,253C 'patch':337C 'path':2A,11A,47B,74B,116C,119C,132C,171C,177C,198C,234C,237C,314C,319C 'php':39B,153C,175C,289C 'plain':169C 'port':353C 'prefix':5A,61B,139C,149C,282C,316C 'provid':373C 'public/x':278C 'read':84B,216C 'real':328C 'refer':245C 'regular':318C 'reli':117C 'remot':105B,243C 'report':369C 'requir':258C 'resolut':280C 'resolv':64B,152C,327C 'return':182C 'root':9A,56B,129C 'scheme':143C,156C,286C,329C 'see':154C 'segment':58B,146C,320C,323C 'similar':79B 'split':120C 'start':160C,311C 'stream':294C 'string':163C 'symfoni':40B,42B,45B,113C,225C 'symfony-filesystem':41B 'symfony/filesystem':26A,27A,28A,29A 'symfony/symfony':30A,31A,32A,33A 'take':122C 'thank':365C 'therefor':255C 'treat':3A,49B 'true':183C 'two':301C 'uri':142C 'url':274C 'user':72B,201C 'user-built':71B 'via':102B 'way':193C 'work':335C 'would':290C,362C 'wrapper':295C 'write':86B,98B,218C,262C,277C","nicknames":[],"mitigations":[],"who_affected":"Applications that use symfony/filesystem or the full symfony/symfony framework and pass user-influenced paths into Path::isBasePath(), canonicalize(), join(), makeAbsolute() or makeRelative() before reading or writing files. On Symfony 6.4 and later, projects using AssetMapper with remote packages are also exposed to a malicious package writing outside assets/vendor. Projects that never build paths from untrusted input with these helpers are unlikely to be affected.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["symfonyfilesystem","symfonysymfony"],"ptokens":["filesystem","symfony"],"labels":["assetmapper","composer","cwe22","high","package","php","symfony","symfonyfilesystem"],"campaign_type":null,"search_a":"'6prc':18A,23A 'base':14A 'directori':15A 'escap':12A 'filesystem':1A 'ghsa':17A,22A 'ghsa-6prc-h956-hxjq':16A,21A 'h956':19A,24A 'hxjq':20A,25A 'let':10A 'path':2A,11A 'prefix':5A 'root':9A 'symfony/filesystem':26A,27A,28A,29A 'symfony/symfony':30A,31A,32A,33A 'treat':3A","search_b":"'-22':37B 'also':94B 'anyth':50B 'applic':66B 'assetmapp':34B,92B 'assets/vendor':101B 'built':73B 'canonic':77B 'check':68B 'compos':35B 'css':108B 'cwe':36B 'directori':91B 'file':87B,99B 'filesystem':43B,46B 'first':53B 'helper':48B 'intend':90B 'isbasepath':76B 'made':82B,96B 'malici':104B 'open':70B 'outsid':88B,100B 'packag':38B,106B 'path':47B,74B 'php':39B 'prefix':61B 'read':84B 'remot':105B 'resolv':64B 'root':56B 'segment':58B 'similar':79B 'symfoni':40B,42B,45B 'symfony-filesystem':41B 'treat':49B 'user':72B 'user-built':71B 'via':102B 'write':86B,98B","search_c":"'/../../../../public/x':275C '/../secret.txt':166C,181C '/base':179C '/base/../x':165C,180C '/secret.txt':173C '/shell.php':276C,279C '/symfony/symfony/commit/9441dd5f05e78ad898317083828c1acebff586a1)':346C '5.4':349C '6.4':226C,355C '7.4':356C '8.1':357C '8.2':359C 'affect':190C 'applic':195C 'assetmapp':230C 'assets/vendor':269C 'avail':342C 'branch':348C 'build':232C 'built':199C 'canonic':184C,206C 'check':136C,196C 'choic':267C 'compon':114C,231C 'compress.zlib':333C 'compromis':252C 'credit':360C 'css':248C,273C 'descript':109C 'digit':305C 'directori':223C 'doesn':134C 'everyth':123C 'exampl':271C 'expect':222C 'file':219C,240C,264C 'filesystem':115C 'first':126C 'fix':375C 'follow':307C 'forward':352C 'forward-port':351C 'github.com':345C 'github.com/symfony/symfony/commit/9441dd5f05e78ad898317083828c1acebff586a1)':344C 'greka':367C 'importmap':257C,260C 'input':202C 'instal':261C 'isbasepath':178C,204C 'issu':340C,371C 'join':185C,235C 'keep':334C 'later':228C 'letter':304C 'like':363C 'local':170C 'look':291C 'made':214C 'make':256C 'makeabsolut':186C 'makerel':188C 'malici':250C 'method':111C 'name':298C 'never':151C 'nicola':366C 'open':209C 'outsid':220C,268C 'packag':244C,253C 'patch':337C 'path':116C,119C,132C,171C,177C,198C,234C,237C,314C,319C 'php':153C,175C,289C 'plain':169C 'port':353C 'prefix':139C,149C,282C,316C 'provid':373C 'public/x':278C 'read':216C 'real':328C 'refer':245C 'regular':318C 'reli':117C 'remot':243C 'report':369C 'requir':258C 'resolut':280C 'resolv':152C,327C 'return':182C 'root':129C 'scheme':143C,156C,286C,329C 'see':154C 'segment':146C,320C,323C 'split':120C 'start':160C,311C 'stream':294C 'string':163C 'symfoni':113C,225C 'take':122C 'thank':365C 'therefor':255C 'true':183C 'two':301C 'uri':142C 'url':274C 'user':201C 'way':193C 'work':335C 'would':290C,362C 'wrapper':295C 'write':218C,262C,277C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-04T11:43:35+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-6prc-h956-hxjq","url":"https://github.com/symfony/symfony/security/advisories/GHSA-6prc-h956-hxjq","severity":"high","published_at":"2026-11-04T11:43:35+00:00","modified_at":"2026-11-04T11:43:36+00:00","first_seen_at":"2026-11-09T22:53:32+00:00","fetched_at":"2026-11-09T22:53:32+00:00","note":null}],"documents":[{"id":110707,"url":"https://github.com/symfony/symfony/security/advisories/GHSA-6prc-h956-hxjq","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}