T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- index.ts:210
- Finding
- Caller-Controlled Persistence Path Enables Unauthorized Local File Reads## Vulnerability Details **File Location**: `index.ts`, lines 210–216 **Vulnerability Type**: Arbitrary local JSON file read through an unrestricted path **Risk Level**: High ```typescript case "load": { const { persistPath } = validatedParams; try { const content = await Deno.readTextFile(persistPath); memoryStore = JSON.parse(content); config.persistPath = persistPath; return { success: true, loadedCount: memoryStore.length, persistPath }; ``` ### Technical Analysis The `load` action accepts `persistPath` directly from the caller and passes it to `Deno.readTextFile` without enforcing an application-owned storage directory. There is no path canonicalization, traversal prevention, absolute-path rejection, extension restriction, or authorization check. Consequently, a caller can request any file that is readable under the Deno process's effective permissions. If that file contains compatible JSON, its data is assigned to the global `memoryStore` and can subsequently be returned through the `list`, `search`, or `summarize` actions. The parsed content is also not validated using the declared `MemoryItem` schema. `JSON.parse` results are assigned directly to `memoryStore`, permitting malformed or structurally unexpected state to enter the application. Exploitation is constrained by the runtime's Deno read permissions and by the requirement that the selected file contain parseable JSON. Broad permissions such as `--allow-read` substantially increase the affected scope. ### Attack Path 1. Identify or guess the path of a JSON file readable by the Deno process. 2. Invoke the skill with `action: "load"` and set `persistPath` to that file, including an absolute path or a traversal path outside the intended memory-storage location. 3. The skill reads and parses the selected file into the global `memoryStore`. 4. Invoke `action: "list"`, `action: "search"`, or `action: "summarize"` to retrieve data deri ...[truncated 808 chars]
- Remediation
- ## Remediation Suggestions - Remove caller control over the full persistence path and use a fixed application-owned file where possible. - If selectable filenames are required, define a dedicated storage root and accept only a basename or opaque store identifier. - Resolve and canonicalize the requested path, then verify that it remains inside the configured storage root. - Reject absolute paths, parent-directory traversal, symbolic-link escapes, and unexpected file extensions. - Start Deno with narrowly scoped permissions such as `--allow-read=/dedicated/memory/directory` rather than unrestricted `--allow-read`. - Validate loaded records before updating global state: ```typescript const MemoryStore = z.array(MemoryItem); const storageRoot = await Deno.realPath("./memory-data"); const candidate = await Deno.realPath(`${storageRoot}/${safeFileName}`); if (!candidate.startsWith(`${storageRoot}/`)) { throw new Error("Persistence path escapes the storage directory"); } const content = await Deno.readTextFile(candidate); const loadedStore = MemoryStore.parse(JSON.parse(content)); memoryStore = loadedStore; ``` - Do not replace the existing `memoryStore` until reading, parsing, and schema validation all succeed. - Return generic errors to untrusted callers rather than exposing detailed filesystem paths or operating-system error messages.
