{"id":"GHSA-r3h5-4585-7x3w","kind":"vulnerability","aliases":["GHSA-r3h5-4585-7x3w"],"related":[],"title":"Image endpoint in @astrojs/node can read files from a case-variant sibling of the output directory on case-sensitive filesystems","description":"## Summary\n\nThe `/_image` endpoint used by the `@astrojs/node` adapter checks that a requested local image is inside the build output directory before reading it from disk. That check compared paths case-insensitively. On case-sensitive filesystems (such as Linux), a directory whose path differs from the output directory only by letter case is a different directory, but it passed the check.\n\nFor example, with the client output directory at `/srv/app/dist/client/`, a request such as:\n\n```text\n/_image?href=/../../DIST/client/secret.png&w=32&f=png\n```\n\nresolved to `/srv/app/DIST/client/secret.png` and was served, even though that file is outside the output directory.\n\n## Impact\n\nAn unauthenticated attacker can read image files from outside the output directory, but only when all of the following are true:\n\n- The site uses on-demand rendering with the `@astrojs/node` adapter.\n- The server runs on a case-sensitive filesystem.\n- A directory exists whose path matches the output directory except for letter case (for example `DIST/client` next to `dist/client`). Such a layout is unusual, and the attacker cannot create it.\n\nOnly files the image service accepts can be read. Raster images are returned after resizing; SVG files are returned unchanged. Other file types fail during processing. Plain path traversal outside the output directory (for example `/../../secret.png`) was already rejected and is not affected.\n\n## Affected versions\n\n`astro` >= 5.14.3, <= 7.4.6, when used with `@astrojs/node` (all versions up to 11.1.11). The vulnerable image endpoint is provided by `astro` and used by the `@astrojs/node` adapter.\n\n## Patches\n\nFixed in `astro` 7.4.7. The path check now compares paths exactly, and only ignores letter case for Windows drive-letter paths. The fix ships in `astro`, so upgrading `@astrojs/node` alone is not enough.\n\n## Workarounds\n\nUpgrade to `astro` 7.4.7 or later. Before upgrading, make sure no directory next to the build output directory has a path that differs from it only by letter case.\n\n## Credits\n\nReported by @zachariah-mithani.","summary":"On case-sensitive filesystems, the Astro /_image endpoint's path check compared paths case-insensitively, so a directory differing only by letter case from the build output directory passed the check. An unauthenticated attacker could read image files from outside the output directory, but only when a case-variant directory already exists on the server.","severity":"low","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@astrojs/node","fixed":[],"affected":"<= 11.1.11","ecosystem":"npm"},{"name":"astro","fixed":["7.4.7"],"affected":">= 5.14.3, <= 7.4.6","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["@astrojs/node","astro","cwe-178","cwe-22","linux","package","self-hosted"],"not_affected_if":["managed-hosting","no-case-variant-directory","static-export"],"how_to_check":"- Check the `astro` version in `package.json` and the lockfile; affected range is `>= 5.14.3, <= 7.4.6`.\n- Confirm the site uses on-demand rendering with the `@astrojs/node` adapter.\n- Check whether the server runs on a case-sensitive filesystem and whether a directory exists next to the build output (e.g. `DIST/client` beside `dist/client`).","fix":"- `astro` `7.4.7`\n- `@astrojs/node` alone is not enough; the fix ships in `astro`.","status":"open","published_at":"2026-11-09T12:21:36+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","triaged_at":"2026-11-09T22:58:31+00:00","updated_at":"2026-11-09T22:58:31+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:58:31Z","model":"claude-haiku-5-5","output":{"fix":"- `astro` `7.4.7`\n- `@astrojs/node` alone is not enough; the fix ships in `astro`.","reach":"internet","title":"Astro: image endpoint reads files from case-variant sibling directories","impact":"low","summary":"On case-sensitive filesystems, the Astro /_image endpoint's path check compared paths case-insensitively, so a directory differing only by letter case from the build output directory passed the check. An unauthenticated attacker could read image files from outside the output directory, but only when a case-variant directory already exists on the server.","audience":"developers","category":"package","nicknames":[],"who_cares":["astro","@astrojs/node","self-hosted","linux"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"image files outside the output directory are readable by anyone","analyst_note":"","how_to_check":"- Check the `astro` version in `package.json` and the lockfile; affected range is `>= 5.14.3, <= 7.4.6`.\n- Confirm the site uses on-demand rendering with the `@astrojs/node` adapter.\n- Check whether the server runs on a case-sensitive filesystem and whether a directory exists next to the build output (e.g. `DIST/client` beside `dist/client`).","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["static-export","managed-hosting","no-case-variant-directory"],"who_is_affected":"Sites built with Astro that use on-demand rendering with the @astrojs/node adapter on a case-sensitive filesystem such as Linux. Exposure also requires a directory whose path differs from the output directory only by letter case, which the advisory calls unusual and which the attacker cannot create. Static-only builds and sites not using the node adapter are not affected by the image endpoint path.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"low"},"cost_usd":0.000862,"input_tokens":5271,"output_tokens":670,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-178':40B '-22':42B '-7':27A,33A '/../../dist/client/secret.png':185C '/../../secret.png':312C '/_image':55B,108C,183C '/srv/app/dist/client':177C '/srv/app/dist/client/secret.png':192C '11.1.11':333C '32':187C '4585':26A,32A '5.14.3':323C '7.4.6':324C '7.4.7':352C,387C 'accept':282C 'adapt':114C,237C,347C 'affect':319C,320C 'alon':379C 'alreadi':101B,314C 'astro':36A,38B,54B,322C,341C,351C,375C,386C 'astrojs/node':4A,35A,37B,113C,236C,328C,346C,378C 'attack':83B,208C,273C 'build':75B,124C,399C 'cannot':274C 'case':11A,20A,50B,63B,72B,98B,137C,141C,159C,244C,259C,364C,412C 'case-insensit':62B,136C 'case-sensit':19A,49B,140C,243C 'case-vari':10A,97B 'check':59B,80B,115C,133C,168C,355C 'client':173C 'compar':60B,134C,357C 'could':84B 'creat':275C 'credit':413C 'cwe':39B,41B 'demand':232C 'differ':68B,151C,162C,406C 'directori':17A,67B,77B,92B,100B,126C,148C,155C,163C,175C,204C,217C,248C,255C,309C,395C,401C 'disk':131C 'dist/client':262C,265C 'drive':368C 'drive-lett':367C 'endpoint':2A,56B,109C,337C 'enough':382C 'even':196C 'exact':359C 'exampl':170C,261C,311C 'except':256C 'exist':102B,249C 'f':188C 'fail':300C 'file':7A,87B,199C,212C,278C,293C,298C 'filesystem':22A,52B,143C,246C 'fix':349C,372C 'follow':224C 'ghsa':24A,30A 'ghsa-r3h5':23A,29A 'host':47B 'href':184C 'ignor':362C 'imag':1A,86B,120C,211C,280C,287C,336C 'impact':205C 'insensit':64B,138C 'insid':122C 'later':389C 'layout':268C 'letter':71B,158C,258C,363C,369C,411C 'linux':43B,146C 'local':119C 'make':392C 'match':252C 'mithani':418C 'next':263C,396C 'on-demand':230C 'output':16A,76B,91B,125C,154C,174C,203C,216C,254C,308C,400C 'outsid':89B,201C,214C,306C 'packag':44B 'pass':78B,166C 'patch':348C 'path':58B,61B,135C,150C,251C,304C,354C,358C,370C,404C 'plain':303C 'png':189C 'process':302C 'provid':339C 'r3h5':25A,31A 'raster':286C 'read':6A,85B,128C,210C,285C 'reject':315C 'render':233C 'report':414C 'request':118C,179C 'resiz':291C 'resolv':190C 'return':289C,295C 'run':240C 'self':46B 'self-host':45B 'sensit':21A,51B,142C,245C 'serv':195C 'server':105B,239C 'servic':281C 'ship':373C 'sibl':13A 'site':228C 'summari':106C 'sure':393C 'svg':292C 'text':182C 'though':197C 'travers':305C 'true':226C 'type':299C 'unauthent':82B,207C 'unchang':296C 'unusu':270C 'upgrad':377C,384C,391C 'use':110C,229C,326C,343C 'variant':12A,99B 'version':321C,330C 'vulner':335C 'w':186C 'whose':149C,250C 'window':366C 'workaround':383C 'x3w':28A,34A 'zachariah':417C 'zachariah-mithani':416C","nicknames":[],"mitigations":[],"who_affected":"Sites built with Astro that use on-demand rendering with the @astrojs/node adapter on a case-sensitive filesystem such as Linux. Exposure also requires a directory whose path differs from the output directory only by letter case, which the advisory calls unusual and which the attacker cannot create. Static-only builds and sites not using the node adapter are not affected by the image endpoint path.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["astro","node"],"ptokens":["astro","astrojs","node"],"labels":["astro","cwe178","cwe22","linux","low","node","npm","package","selfhosted"],"campaign_type":null,"search_a":"'-7':27A,33A '4585':26A,32A 'astro':36A 'astrojs/node':4A,35A 'case':11A,20A 'case-sensit':19A 'case-vari':10A 'directori':17A 'endpoint':2A 'file':7A 'filesystem':22A 'ghsa':24A,30A 'ghsa-r3h5':23A,29A 'imag':1A 'output':16A 'r3h5':25A,31A 'read':6A 'sensit':21A 'sibl':13A 'variant':12A 'x3w':28A,34A","search_b":"'-178':40B '-22':42B '/_image':55B 'alreadi':101B 'astro':38B,54B 'astrojs/node':37B 'attack':83B 'build':75B 'case':50B,63B,72B,98B 'case-insensit':62B 'case-sensit':49B 'case-vari':97B 'check':59B,80B 'compar':60B 'could':84B 'cwe':39B,41B 'differ':68B 'directori':67B,77B,92B,100B 'endpoint':56B 'exist':102B 'file':87B 'filesystem':52B 'host':47B 'imag':86B 'insensit':64B 'letter':71B 'linux':43B 'output':76B,91B 'outsid':89B 'packag':44B 'pass':78B 'path':58B,61B 'read':85B 'self':46B 'self-host':45B 'sensit':51B 'server':105B 'unauthent':82B 'variant':99B","search_c":"'/../../dist/client/secret.png':185C '/../../secret.png':312C '/_image':108C,183C '/srv/app/dist/client':177C '/srv/app/dist/client/secret.png':192C '11.1.11':333C '32':187C '5.14.3':323C '7.4.6':324C '7.4.7':352C,387C 'accept':282C 'adapt':114C,237C,347C 'affect':319C,320C 'alon':379C 'alreadi':314C 'astro':322C,341C,351C,375C,386C 'astrojs/node':113C,236C,328C,346C,378C 'attack':208C,273C 'build':124C,399C 'cannot':274C 'case':137C,141C,159C,244C,259C,364C,412C 'case-insensit':136C 'case-sensit':140C,243C 'check':115C,133C,168C,355C 'client':173C 'compar':134C,357C 'creat':275C 'credit':413C 'demand':232C 'differ':151C,162C,406C 'directori':126C,148C,155C,163C,175C,204C,217C,248C,255C,309C,395C,401C 'disk':131C 'dist/client':262C,265C 'drive':368C 'drive-lett':367C 'endpoint':109C,337C 'enough':382C 'even':196C 'exact':359C 'exampl':170C,261C,311C 'except':256C 'exist':249C 'f':188C 'fail':300C 'file':199C,212C,278C,293C,298C 'filesystem':143C,246C 'fix':349C,372C 'follow':224C 'href':184C 'ignor':362C 'imag':120C,211C,280C,287C,336C 'impact':205C 'insensit':138C 'insid':122C 'later':389C 'layout':268C 'letter':158C,258C,363C,369C,411C 'linux':146C 'local':119C 'make':392C 'match':252C 'mithani':418C 'next':263C,396C 'on-demand':230C 'output':125C,154C,174C,203C,216C,254C,308C,400C 'outsid':201C,214C,306C 'pass':166C 'patch':348C 'path':135C,150C,251C,304C,354C,358C,370C,404C 'plain':303C 'png':189C 'process':302C 'provid':339C 'raster':286C 'read':128C,210C,285C 'reject':315C 'render':233C 'report':414C 'request':118C,179C 'resiz':291C 'resolv':190C 'return':289C,295C 'run':240C 'sensit':142C,245C 'serv':195C 'server':239C 'servic':281C 'ship':373C 'site':228C 'summari':106C 'sure':393C 'svg':292C 'text':182C 'though':197C 'travers':305C 'true':226C 'type':299C 'unauthent':207C 'unchang':296C 'unusu':270C 'upgrad':377C,384C,391C 'use':110C,229C,326C,343C 'version':321C,330C 'vulner':335C 'w':186C 'whose':149C,250C 'window':366C 'workaround':383C 'zachariah':417C 'zachariah-mithani':416C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-09T12:21:36+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-r3h5-4585-7x3w","url":"https://github.com/withastro/astro/security/advisories/GHSA-r3h5-4585-7x3w","severity":"low","published_at":"2026-11-09T12:21:36+00:00","modified_at":"2026-11-09T12:21:37+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","fetched_at":"2026-11-09T22:53:16+00:00","note":null}],"documents":[{"id":110445,"url":"https://github.com/withastro/astro/security/advisories/GHSA-r3h5-4585-7x3w","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"}