{"record":{"id":"7592baeda98207e9","repo":"ruvnet/ruflo","slug":"memory-path-contains-disallowed-characters","errorCode":null,"errorMessage":"memory path contains disallowed characters","messagePattern":"memory path contains disallowed characters","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agenticow-loader.ts","lineNumber":72,"sourceCode":"}\n\n/** Reset the module-level load cache. Test-only seam. */\nexport function __resetAgenticowCache(): void {\n  _agenticowMod = null;\n  _loadAttempted = false;\n}\n\nexport function degradedResult(reason: string): { success: true; degraded: true; reason: string } {\n  return { success: true, degraded: true, reason };\n}\n\n/**\n * Resolve a user-supplied memory path against the project cwd, rejecting path\n * traversal and NUL bytes (D-2 style hardening — same rule the MCP verbs use).\n */\nexport function resolveMemoryPath(path: string): string {\n  if (!path || typeof path !== 'string') throw new Error('memory path is required');\n  if (/\\.\\.[\\\\/]|\\0/.test(path)) throw new Error('memory path contains disallowed characters');\n  return isAbsolute(path) ? path : resolve(getProjectCwd(), path);\n}\n\n/**\n * Lineage manifest companion path. agenticow persists the COW chain\n * (working → checkpoints → base) into `<file>.agenticow.json` next to the\n * `.rvf` data file. Without it, forks/checkpoints are in-memory only and\n * disappear when the AgenticMemory handle closes.\n */\nexport function manifestFor(file: string): string {\n  return `${file}.agenticow.json`;\n}\n\n/** Validate a COW branch/checkpoint label (alnum + a small safe symbol set). */\nexport function validateLabel(label: string): string {\n  if (!label || typeof label !== 'string') throw new Error('label is required');\n  if (label.length > 256) throw new Error('label exceeds 256 chars');\n  if (!/^[A-Za-z0-9_.\\-:/@]+$/.test(label)) {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/mcp-tools/agenticow-loader.ts#L54-L90","documentation":"Security guard inside resolveMemoryPath() (agenticow-loader.ts:72): the raw, pre-resolution path is tested against /\\.\\.[\\\\/]|\\0/ and rejected. This is D-2 style hardening (the same rule the MCP verbs use) blocking path traversal ('../', '..\\\\') and NUL-byte injection before the path is joined onto the project cwd. Any '..' path segment with a following slash or backslash, anywhere in the string, is enough to trigger it.","triggerScenarios":"Any agenticow_* tool call whose path contains a parent-directory segment anywhere — '../../etc/passwd', 'data/../../secrets.rvf', '..\\\\win\\\\mem.rvf' — or an embedded NUL byte ('\\0'). Because the check runs on the raw string before resolve(), even a benign interior segment like 'a/../b' is rejected, not just leading traversal.","commonSituations":"Paths built by concatenating user input; normalizing paths that legitimately cross directories ('../shared/memory.rvf'); templates that emit relative paths rooted outside the project; adversarial probes against an exposed MCP endpoint.","solutions":["Restructure so the memory file lives under the project cwd and reference it with a plain relative path containing no '..' segments","Pass an absolute path when the file genuinely lives outside the project cwd","Sanitize incoming paths yourself (reject '..' segments and NUL) before forwarding them to the tool"],"exampleFix":"// before\nawait callTool('agenticow_ingest', {\n  path: '../shared/vectors.rvf', // throws: disallowed characters\n  records: [{ vector: [0.1] }],\n});\n\n// after\nawait callTool('agenticow_ingest', {\n  path: '/srv/shared/vectors.rvf', // absolute path, no '..' segment\n  records: [{ vector: [0.1] }],\n});","handlingStrategy":"validation","validationCode":"function isSafeMemoryPath(p: string): boolean {\n  return !/\\.\\.[\\\\/]|\\0/.test(p); // same rule as resolveMemoryPath\n}\nif (!isSafeMemoryPath(userPath)) throw new TypeError('path must not contain .. or NUL');","typeGuard":null,"tryCatchPattern":"try {\n  await callTool('agenticow_ingest', args);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('disallowed characters')) {\n    return { error: 'bad_request', detail: 'reject path traversal in memory path' };\n  }\n  throw e;\n}","preventionTips":["Build memory paths with path.join from an allow-listed base directory instead of concatenating raw input","Reject '..' segments at your API boundary before anything reaches the MCP tool","Treat repeated triggers from external input as reconnaissance and rate-limit the caller"],"tags":["security","path-traversal","validation","mcp","memory"],"backgroundTag":"path-traversal-blocked","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}