thedotmack/claude-mem · warning
Rejected path traversal attempt in watch.context.path
Error message
Rejected path traversal attempt in watch.context.path
What it means
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.
Source
Thrown at src/services/transcripts/processor.ts:376
if (shouldSuppressNativeCodexAgentsContext(watch)) return;
const workerReady = await ensureWorkerRunning();
if (!workerReady) return;
const cwd = session.cwd ?? watch.workspace;
if (!cwd) return;
const context = getProjectContext(cwd);
const projectsParam = context.allProjects.join(',');
const contextUrl = `/api/context/inject?projects=${encodeURIComponent(projectsParam)}&platformSource=${encodeURIComponent(session.platformSource)}`;
const agentsPath = expandHomePath(watch.context.path ?? `${cwd}/AGENTS.md`);
const resolvedAgentsPath = path.resolve(agentsPath);
const allowedRoots = [path.resolve(cwd), path.resolve(DATA_DIR)];
const isPathSafe = allowedRoots.some(root => resolvedAgentsPath.startsWith(root + path.sep) || resolvedAgentsPath === root);
if (!isPathSafe) {
logger.warn('SECURITY', 'Rejected path traversal attempt in watch.context.path', {
original: watch.context.path,
resolved: resolvedAgentsPath,
allowedRoots
});
return;
}
let response: Awaited<ReturnType<typeof workerHttpRequest>>;
try {
response = await workerHttpRequest(contextUrl);
} catch (error: unknown) {
logger.warn('TRANSCRIPT', 'Failed to fetch AGENTS.md context', {
error: error instanceof Error ? error.message : String(error)
});
return;
}
if (!response.ok) return;View on GitHub (pinned to 8bc631a71a)
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
Example fix
// config watch entry - before
{ "name": "codex", "context": { "path": "~/shared/AGENTS.md" } }
// after (default resolves inside cwd)
{ "name": "codex" } Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
function isContextPathSafe(cwd: string, dataDir: string, configured?: string): boolean {
const resolved = path.resolve(configured ?? path.join(cwd, 'AGENTS.md'));
const roots = [path.resolve(cwd), path.resolve(dataDir)];
return roots.some(r => resolved === r || resolved.startsWith(r + path.sep));
}
// before starting the watcher / shipping config:
if (!isContextPathSafe(process.cwd(), DATA_DIR, watch.context?.path)) {
throw new Error(`watch '${watch.name}' context.path escapes allowed roots`);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Transcript watch config not found: ${resolvedPath}
- Invalid transcript watch config: ${resolvedPath}
- Native Codex hooks registered, but failed to disable legac
- Missing schema for watch
- Forbidden
AI-assisted analysis of thedotmack/claude-mem@8bc631a71a (2026-08-20).
Data as JSON: /api/errors/664f3ee6a6f1db6e.
Report an issue: GitHub.