ruvnet/ruflo · error
namespace contains path traversal
Error message
namespace contains path traversal
What it means
validateIdentifier's PATH_TRAVERSAL check (/\.\.[/\\]/) fires before the charset check, so a namespace containing ../ or ..\ anywhere fails memory_list with 'namespace contains path traversal' (memory-tools.ts:705). This blocks attempts to escape the namespace's storage directory in file-backed memory layouts. A namespace like 'a..b' (dots without a following slash/backslash) is fine; 'a/../b' is not.
Solutions
- Remove '..' segments — use flat names ('prod', 'data-prod') instead of relative path walks
- Normalize first: namespace.replace(/\.\.[/\\]/g, '_')
- If namespaces need hierarchy, use ':' or '-' as the separator (allowed by the charset rule), never '/'
- Reject '../' in your input layer before the value ever reaches the tool
Example fix
// before
await mcp.callTool('memory_list', { namespace: 'data/../prod' }); // namespace contains path traversal
// after
await mcp.callTool('memory_list', { namespace: 'data:prod' }); Defensive patterns
Strategy: validation
Validate before calling
function denyTraversal(ns: string): string | null {
if (/\.\.[/\\]/.test(ns)) return null; // reject
return ns;
}
const safeNs = denyTraversal(rawNs) ?? rawNs.split('/').filter(Boolean).join(':'); Type guard
function containsPathTraversal(ns: string): boolean {
return /\.\.[/\\]/.test(ns);
}
// if (containsPathTraversal(ns)) throw new Error('namespace must not contain ../ or ..\\'); Try / catch
try {
await memoryList({ namespace: ns });
} catch (e) {
if (e instanceof Error && e.message.includes('namespace contains path traversal')) {
// security rejection — never 'fix' automatically beyond stripping ../; prefer rejecting the input
}
throw e;
} Prevention
- Never compose namespaces from relative path joins; use flat or ':'-separated names
- Treat traversal hits as hostile input: log and reject rather than silently rewrite
- Use ':' or '-' as hierarchy separators — '/' is not allowed by the charset rule anyway
- Fuzz-test namespace-receiving endpoints with '../' payloads to confirm your pre-validation fires
When it happens
Trigger: memory_list with namespace 'data/../prod', 'a\\..\\b' (Windows-style), or any user-supplied value in which '..' is immediately followed by / or \.
Common situations: Joining namespace segments with '../' shortcuts when composing paths; passing unvalidated user input as a namespace; security-test payloads probing for traversal.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- namespace contains disallowed characters
- memory path contains disallowed characters
- Namespace contains disallowed characters
- namespace contains invalid characters (allowed…
- namespace exceeds 128 characters
AI-assisted analysis of ruvnet/ruflo@9c61c86f06 (2026-08-18).
Data as JSON: /api/errors/15529cb52399a281.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts:805
description: 'Enumerate stored memory entries (optionally filtered by namespace/tags) without semantic search. Use when native Glob is wrong because the entries are not files (they live in .swarm/memory.db). For inspection / audit / "what is in my memory" — pair with memory_search for retrieval-by-meaning.',
category: 'memory',
inputSchema: {
type: 'object',
properties: {
namespace: { type: 'string', description: 'Filter by namespace' },
limit: { type: 'number', description: 'Maximum results (default: 50)' },
offset: { type: 'number', description: 'Offset for pagination (default: 0)' },
},
},
handler: async (input) => {
await ensureInitialized();
const { listEntries } = await getMemoryFunctions();
const namespace = input.namespace as string | undefined;
const limit = (input.limit as number) || 50;
const offset = (input.offset as number) || 0;
if (namespace) { const vNs = validateIdentifier(namespace, 'namespace'); if (!vNs.valid) throw new Error(vNs.error); }
try {
const result = await listEntries({
namespace,
limit,
offset,
});
const entries = result.entries.map(e => ({
key: e.key,
namespace: e.namespace,
storedAt: e.createdAt,
updatedAt: e.updatedAt,
accessCount: e.accessCount,
hasEmbedding: e.hasEmbedding,
size: e.size,
}));
View on GitHub (pinned to 9c61c86f06)