{"id":"GHSA-mjrq-cffv-q4qc","kind":"vulnerability","aliases":["GHSA-mjrq-cffv-q4qc"],"related":[],"title":"Nuxt dev server serves files from a parent directory with a stray lockfile or workspace file","description":"### Summary\n\nNuxt can pick a parent directory that does not declare a workspace as its default `workspaceDir`: the farthest directory with a lockfile when the project is not inside a git repository, or the farthest directory with a `pnpm-workspace.yaml` or `deno.json` file, even one that only holds settings. The Vite dev server allows `/_nuxt/@fs/` requests for any file under `workspaceDir`. A stray lockfile or settings file in a parent directory, such as the home directory, therefore lets anyone who can reach the dev server read files in that directory and everything below it.\n\n### Impact\n\nThis affects the development server only; production builds are not affected. All of the following have to hold:\n\n- A parent directory contains either:\n  - a lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock` and similar), while the project is not inside a git repository and has no workspace file (`pnpm-workspace.yaml`, `turbo.json`, `lerna.json` and similar) above it. Running `npm install` once in the home directory is enough to create one there.\n  - a `pnpm-workspace.yaml` without a `packages` list (for example one that only holds pnpm settings such as `onlyBuiltDependencies`), or a `deno.json` or `deno.jsonc` without a `workspace` field. This applies even if the project is inside a git repository.\n- The dev server is reachable from the network (`nuxt dev --host`, `nuxt dev --tunnel`, a published container port, or a proxy onto the dev port). The default localhost bind limits this to local processes, which can usually read the same files directly.\n\nFor example, with `~/package-lock.json` and a project at `~/projects/app` that has no `.git` directory, `nuxt dev --host` serves `GET /_nuxt/@fs/<home>/.ssh/id_ed25519` with status 200. Any file in the home directory that is not covered by Vite's `server.fs.deny` list can be read this way, including SSH keys, cloud credentials and the Nuxt DevTools auth token in `~/.nuxt/devtools/dev-auth-token.txt`.\n\nNo browser is involved: a plain HTTP client is enough. A web page on another origin cannot read the response, and DNS rebinding is rejected by Vite's host check.\n\nProjects whose parent directories contain none of these files are not affected, nor are projects inside a git repository whose only lockfile is their own. Nuxt 3.5.0 and later are affected.\n\n### Patches\n\nFixed in `@nuxt/schema` 4.6.4, which `nuxt` 4.6.4 depends on. A parent directory now only becomes the default `workspaceDir` if it declares a workspace: a `pnpm-workspace.yaml` with `packages`, a `deno.json` or `deno.jsonc` with `workspace`, a `lerna.json`, `turbo.json` or `rush.json`, a `package.json` with `workspaces`, or a git repository. A lockfile or a settings file alone no longer widens the dev server's `server.fs.allow` list. If your monorepo root is not detected this way, set `workspaceDir` in `nuxt.config.ts`. Nuxt 3 has reached end of life and will not receive a fix.\n\n### Workarounds\n\nSet `workspaceDir` explicitly in `nuxt.config.ts` to the project root (or to the real monorepo root), or remove the stray lockfile or settings file from the parent directory. Do not expose the dev server to untrusted networks. If the dev port was reachable from an untrusted network while this layout was in place, treat files in the parent directory as exposed and rotate credentials such as SSH keys and cloud tokens.\n\n### Credits\n\nThanks to [@superboy-zjc](https://github.com/superboy-zjc) for the report.","summary":"The Nuxt development server can serve any file under a parent directory when a stray lockfile or settings file sits above the project. An attacker who can reach the dev server over the network can read files such as SSH keys and cloud credentials in that directory.","severity":"medium","cvss":5.9,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@nuxt/schema","fixed":[],"affected":">= 3.5.0, < 4.0.0","ecosystem":"npm"},{"name":"@nuxt/schema","fixed":["4.6.4"],"affected":">= 4.0.0, < 4.6.4","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-552","developers","npm","nuxt","package","self-hosted"],"not_affected_if":["localhost-only","managed-hosting","no-parent-lockfile","production-build"],"how_to_check":"- Run `npm ls @nuxt/schema` or check `node_modules/@nuxt/schema/package.json` for a version below `4.6.4`\n- Look for `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock` or `bun.lock` in parent directories above the project, such as the home directory, with no git repository around the project\n- Check whether `nuxt dev` runs with `--host` or `--tunnel`, or the dev port is published from a container","fix":"- `@nuxt/schema` `4.6.4` (affected `>= 4.0.0, < 4.6.4`; `nuxt` 4.6.4 depends on it)\n- Nuxt 3 (`>= 3.5.0, < 4.0.0`): no fix, Nuxt 3 has reached end of life; the advisory suggests setting `workspaceDir` explicitly in `nuxt.config.ts`","status":"open","published_at":"2026-11-02T20:55:43+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","triaged_at":"2026-11-09T22:56:12+00:00","updated_at":"2026-11-09T22:56:12+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-09T22:56:12Z","model":"claude-haiku-5-5","output":{"fix":"- `@nuxt/schema` `4.6.4` (affected `>= 4.0.0, < 4.6.4`; `nuxt` 4.6.4 depends on it)\n- Nuxt 3 (`>= 3.5.0, < 4.0.0`): no fix, Nuxt 3 has reached end of life; the advisory suggests setting `workspaceDir` explicitly in `nuxt.config.ts`","reach":"network","title":"Nuxt dev server serves parent-directory files via stray lockfile","impact":"high","summary":"The Nuxt development server can serve any file under a parent directory when a stray lockfile or settings file sits above the project. An attacker who can reach the dev server over the network can read files such as SSH keys and cloud credentials in that directory.","audience":"developers","category":"package","nicknames":[],"who_cares":["nuxt","self-hosted","npm","developers"],"confidence":0.85,"fix_status":"available","needs_auth":"no","worst_case":"Attacker reads SSH keys and cloud credentials from parent directory","analyst_note":"","how_to_check":"- Run `npm ls @nuxt/schema` or check `node_modules/@nuxt/schema/package.json` for a version below `4.6.4`\n- Look for `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock` or `bun.lock` in parent directories above the project, such as the home directory, with no git repository around the project\n- Check whether `nuxt dev` runs with `--host` or `--tunnel`, or the dev port is published from a container","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["production-build","localhost-only","no-parent-lockfile","managed-hosting"],"who_is_affected":"Only the development server (`nuxt dev`) is affected, not production builds. Exposure needs a lockfile or settings file such as `pnpm-workspace.yaml` or `deno.json` in a parent directory, the project outside any git repository, and the dev server reachable from the network through `--host`, `--tunnel`, a published container port or a proxy. Teams that keep the dev server on localhost, or have no stray files above their projects, can skip it.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000978,"input_tokens":5846,"output_tokens":786,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-552':30B '/.nuxt/devtools/dev-auth-token.txt':382C '/.ssh/id_ed25519':346C '/_nuxt':138C,344C '/package-lock.json':328C '/projects/app':333C '/superboy-zjc)':612C '200':349C '3':521C '3.5.0':439C '4.6.4':448C,451C 'affect':181C,190C,424C,443C 'allow':137C 'alon':497C 'anoth':397C 'anyon':163C 'appli':273C 'attack':62B 'auth':379C 'becom':459C 'bind':311C 'browser':384C 'build':187C 'bun.lock':208C 'cannot':399C 'cffv':20A,25A 'check':412C 'client':390C 'cloud':80B,373C,602C 'contain':201C,299C,417C 'cover':359C 'creat':243C 'credenti':81B,374C,596C 'credit':604C 'cwe':29B 'declar':95C,465C 'default':100C,309C,461C 'deno.json':125C,265C,473C 'deno.jsonc':267C,475C 'depend':452C 'detect':513C 'dev':2A,67B,135C,168C,284C,292C,295C,306C,340C,502C,565C,572C 'develop':31B,40B,183C 'devtool':378C 'direct':324C 'directori':9A,49B,84B,91C,104C,120C,155C,160C,174C,200C,239C,338C,355C,416C,456C,560C,591C 'dns':404C 'either':202C 'end':524C 'enough':241C,392C 'even':127C,274C 'everyth':176C 'exampl':253C,326C 'explicit':536C 'expos':563C,593C 'farthest':103C,119C 'field':271C 'file':5A,16A,45B,56B,74B,126C,143C,151C,171C,224C,323C,351C,421C,496C,556C,587C 'fix':445C,532C 'follow':194C 'fs':139C,345C 'get':343C 'ghsa':18A,23A 'ghsa-mjrq-cffv-q4qc':17A,22A 'git':115C,218C,281C,337C,430C,489C 'github.com':611C 'github.com/superboy-zjc)':610C 'hold':131C,197C,257C 'home':159C,238C,354C 'host':37B,293C,341C,411C 'http':389C 'impact':179C 'includ':370C 'insid':113C,216C,279C,428C 'instal':234C 'involv':386C 'key':78B,372C,600C 'later':441C 'layout':582C 'lerna.json':227C,479C 'let':162C 'life':526C 'limit':312C 'list':251C,364C,506C 'local':315C 'localhost':310C 'lockfil':13A,53B,107C,148C,204C,434C,492C,553C 'longer':499C 'mjrq':19A,24A 'monorepo':509C,547C 'network':71B,290C,569C,579C 'none':418C 'npm':32B,233C 'nuxt':1A,33B,39B,86C,291C,294C,339C,377C,438C,450C,520C 'nuxt.config.ts':519C,538C 'nuxt/schema':27A,28A,447C 'one':128C,244C,254C 'onlybuiltdepend':262C 'onto':304C 'origin':398C 'packag':34B,250C,471C 'package-lock.json':205C 'package.json':484C 'page':395C 'parent':8A,48B,90C,154C,199C,415C,455C,559C,590C 'patch':444C 'pick':88C 'place':585C 'plain':388C 'pnpm':258C 'pnpm-lock.yaml':206C 'pnpm-workspace.yaml':123C,225C,247C,469C 'port':300C,307C,573C 'process':316C 'product':186C 'project':60B,110C,213C,277C,331C,413C,427C,541C 'proxi':303C 'publish':298C 'q4qc':21A,26A 'reach':65B,166C,523C 'reachabl':287C,575C 'read':73B,170C,320C,367C,400C 'real':546C 'rebind':405C 'receiv':530C 'reject':407C 'remov':550C 'report':615C 'repositori':116C,219C,282C,431C,490C 'request':140C 'respons':402C 'root':510C,542C,548C 'rotat':595C 'run':232C 'rush.json':482C 'self':36B 'self-host':35B 'serv':4A,43B,342C 'server':3A,41B,68B,136C,169C,184C,285C,503C,566C 'server.fs.allow':505C 'server.fs.deny':363C 'set':55B,132C,150C,259C,495C,516C,534C,555C 'similar':210C,229C 'sit':57B 'ssh':77B,371C,599C 'status':348C 'stray':12A,52B,147C,552C 'summari':85C 'superboy':608C 'superboy-zjc':607C 'thank':605C 'therefor':161C 'token':380C,603C 'treat':586C 'tunnel':296C 'turbo.json':226C,480C 'untrust':568C,578C 'usual':319C 'vite':134C,361C,409C 'way':369C,515C 'web':394C 'whose':414C,432C 'widen':500C 'without':248C,268C 'workaround':533C 'workspac':15A,97C,223C,270C,467C,477C,486C 'workspacedir':101C,145C,462C,517C,535C 'yarn.lock':207C 'zjc':609C","nicknames":[],"mitigations":[],"who_affected":"Only the development server (`nuxt dev`) is affected, not production builds. Exposure needs a lockfile or settings file such as `pnpm-workspace.yaml` or `deno.json` in a parent directory, the project outside any git repository, and the dev server reachable from the network through `--host`, `--tunnel`, a published container port or a proxy. Teams that keep the dev server on localhost, or have no stray files above their projects, can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["schema"],"ptokens":["nuxt","schema"],"labels":["cwe552","developers","medium","npm","nuxt","package","selfhosted"],"campaign_type":null,"search_a":"'cffv':20A,25A 'dev':2A 'directori':9A 'file':5A,16A 'ghsa':18A,23A 'ghsa-mjrq-cffv-q4qc':17A,22A 'lockfil':13A 'mjrq':19A,24A 'nuxt':1A 'nuxt/schema':27A,28A 'parent':8A 'q4qc':21A,26A 'serv':4A 'server':3A 'stray':12A 'workspac':15A","search_b":"'-552':30B 'attack':62B 'cloud':80B 'credenti':81B 'cwe':29B 'dev':67B 'develop':31B,40B 'directori':49B,84B 'file':45B,56B,74B 'host':37B 'key':78B 'lockfil':53B 'network':71B 'npm':32B 'nuxt':33B,39B 'packag':34B 'parent':48B 'project':60B 'reach':65B 'read':73B 'self':36B 'self-host':35B 'serv':43B 'server':41B,68B 'set':55B 'sit':57B 'ssh':77B 'stray':52B","search_c":"'/.nuxt/devtools/dev-auth-token.txt':382C '/.ssh/id_ed25519':346C '/_nuxt':138C,344C '/package-lock.json':328C '/projects/app':333C '/superboy-zjc)':612C '200':349C '3':521C '3.5.0':439C '4.6.4':448C,451C 'affect':181C,190C,424C,443C 'allow':137C 'alon':497C 'anoth':397C 'anyon':163C 'appli':273C 'auth':379C 'becom':459C 'bind':311C 'browser':384C 'build':187C 'bun.lock':208C 'cannot':399C 'check':412C 'client':390C 'cloud':373C,602C 'contain':201C,299C,417C 'cover':359C 'creat':243C 'credenti':374C,596C 'credit':604C 'declar':95C,465C 'default':100C,309C,461C 'deno.json':125C,265C,473C 'deno.jsonc':267C,475C 'depend':452C 'detect':513C 'dev':135C,168C,284C,292C,295C,306C,340C,502C,565C,572C 'develop':183C 'devtool':378C 'direct':324C 'directori':91C,104C,120C,155C,160C,174C,200C,239C,338C,355C,416C,456C,560C,591C 'dns':404C 'either':202C 'end':524C 'enough':241C,392C 'even':127C,274C 'everyth':176C 'exampl':253C,326C 'explicit':536C 'expos':563C,593C 'farthest':103C,119C 'field':271C 'file':126C,143C,151C,171C,224C,323C,351C,421C,496C,556C,587C 'fix':445C,532C 'follow':194C 'fs':139C,345C 'get':343C 'git':115C,218C,281C,337C,430C,489C 'github.com':611C 'github.com/superboy-zjc)':610C 'hold':131C,197C,257C 'home':159C,238C,354C 'host':293C,341C,411C 'http':389C 'impact':179C 'includ':370C 'insid':113C,216C,279C,428C 'instal':234C 'involv':386C 'key':372C,600C 'later':441C 'layout':582C 'lerna.json':227C,479C 'let':162C 'life':526C 'limit':312C 'list':251C,364C,506C 'local':315C 'localhost':310C 'lockfil':107C,148C,204C,434C,492C,553C 'longer':499C 'monorepo':509C,547C 'network':290C,569C,579C 'none':418C 'npm':233C 'nuxt':86C,291C,294C,339C,377C,438C,450C,520C 'nuxt.config.ts':519C,538C 'nuxt/schema':447C 'one':128C,244C,254C 'onlybuiltdepend':262C 'onto':304C 'origin':398C 'packag':250C,471C 'package-lock.json':205C 'package.json':484C 'page':395C 'parent':90C,154C,199C,415C,455C,559C,590C 'patch':444C 'pick':88C 'place':585C 'plain':388C 'pnpm':258C 'pnpm-lock.yaml':206C 'pnpm-workspace.yaml':123C,225C,247C,469C 'port':300C,307C,573C 'process':316C 'product':186C 'project':110C,213C,277C,331C,413C,427C,541C 'proxi':303C 'publish':298C 'reach':166C,523C 'reachabl':287C,575C 'read':170C,320C,367C,400C 'real':546C 'rebind':405C 'receiv':530C 'reject':407C 'remov':550C 'report':615C 'repositori':116C,219C,282C,431C,490C 'request':140C 'respons':402C 'root':510C,542C,548C 'rotat':595C 'run':232C 'rush.json':482C 'serv':342C 'server':136C,169C,184C,285C,503C,566C 'server.fs.allow':505C 'server.fs.deny':363C 'set':132C,150C,259C,495C,516C,534C,555C 'similar':210C,229C 'ssh':371C,599C 'status':348C 'stray':147C,552C 'summari':85C 'superboy':608C 'superboy-zjc':607C 'thank':605C 'therefor':161C 'token':380C,603C 'treat':586C 'tunnel':296C 'turbo.json':226C,480C 'untrust':568C,578C 'usual':319C 'vite':134C,361C,409C 'way':369C,515C 'web':394C 'whose':414C,432C 'widen':500C 'without':248C,268C 'workaround':533C 'workspac':97C,223C,270C,467C,477C,486C 'workspacedir':101C,145C,462C,517C,535C 'yarn.lock':207C 'zjc':609C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-02T20:55:43+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-mjrq-cffv-q4qc","url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-mjrq-cffv-q4qc","severity":"medium","published_at":"2026-11-02T20:55:43+00:00","modified_at":"2026-11-02T20:55:43+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":110434,"url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-mjrq-cffv-q4qc","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"}