{"record":{"id":"664f3ee6a6f1db6e","repo":"thedotmack/claude-mem","slug":"rejected-path-traversal-attempt-in-watch-context-p","errorCode":null,"errorMessage":"Rejected path traversal attempt in watch.context.path","messagePattern":"Rejected path traversal attempt in watch\\.context\\.path","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/transcripts/processor.ts","lineNumber":376,"sourceCode":"    if (shouldSuppressNativeCodexAgentsContext(watch)) return;\n\n    const workerReady = await ensureWorkerRunning();\n    if (!workerReady) return;\n\n    const cwd = session.cwd ?? watch.workspace;\n    if (!cwd) return;\n\n    const context = getProjectContext(cwd);\n    const projectsParam = context.allProjects.join(',');\n\n    const contextUrl = `/api/context/inject?projects=${encodeURIComponent(projectsParam)}&platformSource=${encodeURIComponent(session.platformSource)}`;\n    const agentsPath = expandHomePath(watch.context.path ?? `${cwd}/AGENTS.md`);\n\n    const resolvedAgentsPath = path.resolve(agentsPath);\n    const allowedRoots = [path.resolve(cwd), path.resolve(DATA_DIR)];\n    const isPathSafe = allowedRoots.some(root => resolvedAgentsPath.startsWith(root + path.sep) || resolvedAgentsPath === root);\n    if (!isPathSafe) {\n      logger.warn('SECURITY', 'Rejected path traversal attempt in watch.context.path', {\n        original: watch.context.path,\n        resolved: resolvedAgentsPath,\n        allowedRoots\n      });\n      return;\n    }\n\n    let response: Awaited<ReturnType<typeof workerHttpRequest>>;\n    try {\n      response = await workerHttpRequest(contextUrl);\n    } catch (error: unknown) {\n      logger.warn('TRANSCRIPT', 'Failed to fetch AGENTS.md context', {\n        error: error instanceof Error ? error.message : String(error)\n      });\n      return;\n    }\n\n    if (!response.ok) return;","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/8bc631a71a487424b866756e43a6efa4574cc66b/src/services/transcripts/processor.ts#L358-L394","documentation":"claude-mem injects an AGENTS.md-style context file into observer requests, reading the path from watch.context.path in the transcript watch config. Before fetching, the processor resolves that path and requires it to stay inside one of two allowed roots: the project cwd or the claude-mem DATA_DIR. If the resolved path escapes both roots, this security warning is logged and context injection is skipped for that cycle.","triggerScenarios":"A watch entry sets context.path to an absolute path outside the project (e.g. /etc/agents.md or another repo), a relative path whose ../ segments climb out of cwd, or a ~/ path that expandHomePath expands outside both the project and DATA_DIR.","commonSituations":"Sharing a transcript watch config between machines or projects where cwd differs; pointing context.path at a central dotfiles repo; renaming/moving the project so a previously valid absolute path no longer falls under cwd.","solutions":["Remove the context.path key so the default `${cwd}/AGENTS.md` is used","Point context.path at a file inside the current project directory","If the file must live elsewhere, copy or symlink it under the project root or DATA_DIR and reference that location"],"exampleFix":"// config watch entry - before\n{ \"name\": \"codex\", \"context\": { \"path\": \"~/shared/AGENTS.md\" } }\n\n// after (default resolves inside cwd)\n{ \"name\": \"codex\" }","handlingStrategy":"validation","validationCode":"import path from 'node:path';\n\nfunction isContextPathSafe(cwd: string, dataDir: string, configured?: string): boolean {\n  const resolved = path.resolve(configured ?? path.join(cwd, 'AGENTS.md'));\n  const roots = [path.resolve(cwd), path.resolve(dataDir)];\n  return roots.some(r => resolved === r || resolved.startsWith(r + path.sep));\n}\n\n// before starting the watcher / shipping config:\nif (!isContextPathSafe(process.cwd(), DATA_DIR, watch.context?.path)) {\n  throw new Error(`watch '${watch.name}' context.path escapes allowed roots`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep watch.context.path relative to the project root or omit it entirely","Add a config lint step that resolves every context.path and asserts it stays under cwd or DATA_DIR","Remember the guard is string-prefix based on the resolved path — symlinked targets that point outside will still be caught only by their resolved location"],"tags":["security","path-traversal","config","context-injection"],"backgroundTag":"path-traversal","analyzedSha":"8bc631a71a487424b866756e43a6efa4574cc66b","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}