{"record":{"id":"0c79a5818b6bebee","repo":"ruvnet/ruflo","slug":"namespace-contains-disallowed-characters","errorCode":null,"errorMessage":"Namespace contains disallowed characters","messagePattern":"Namespace contains disallowed characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts","lineNumber":86,"sourceCode":"const DANGEROUS_KEY_CHARS = /[;&|`$(){}[\\]<>!#\\\\\\0]|\\.\\.[/\\\\]/g;\nconst DANGEROUS_KEY_PATTERN = /[;&|`$(){}[\\]<>!#\\\\\\0]|\\.\\.[/\\\\]/;\n\nfunction validateMemoryInput(key?: string, value?: string, query?: string, namespace?: string): void {\n  if (key && key.length > MAX_KEY_LENGTH) {\n    throw new Error(`Key exceeds maximum length of ${MAX_KEY_LENGTH} characters`);\n  }\n  if (value && value.length > MAX_VALUE_SIZE) {\n    throw new Error(`Value exceeds maximum size of ${MAX_VALUE_SIZE} bytes`);\n  }\n  if (query && query.length > MAX_QUERY_LENGTH) {\n    throw new Error(`Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`);\n  }\n  // Reject path traversal and shell metacharacters in keys/namespaces (#1425)\n  if (key && DANGEROUS_KEY_PATTERN.test(key)) {\n    throw new Error('Key contains disallowed characters');\n  }\n  if (namespace && DANGEROUS_KEY_PATTERN.test(namespace)) {\n    throw new Error('Namespace contains disallowed characters');\n  }\n}\n\n// #1884 — sanitize a key produced from arbitrary input (markdown headings,\n// frontmatter names, file names) so it survives validateMemoryInput on the\n// read/delete path. Replaces every dangerous char with `_`. Truncates to\n// MAX_KEY_LENGTH so the bound check in validateMemoryInput also passes.\n// Keep this in sync with DANGEROUS_KEY_PATTERN — they share DANGEROUS_KEY_CHARS.\nfunction sanitizeMemoryKey(key: string): string {\n  const safe = key.replace(DANGEROUS_KEY_CHARS, '_');\n  return safe.length > MAX_KEY_LENGTH ? safe.slice(0, MAX_KEY_LENGTH) : safe;\n}\n\n// #1937 — minimal glob → RegExp helper for memory_import_claude exclusion\n// patterns. Anchored. Supports the three operators the issue's voice-fidelity\n// workflow needs:\n//   `**` — any chars including path separators\n//   `*`  — any chars except path separators","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/ruvnet/ruflo/blob/9c61c86f06b439af2a95085ae9bb0ca839662e41/v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts#L68-L104","documentation":"The same DANGEROUS_KEY_PATTERN guard applied to keys is applied to the namespace argument in validateMemoryInput (memory-tools.ts:86): namespaces containing ; & | ` $ ( ) { } [ ] < > ! # \\ NUL or ../ / ..\\ are rejected with 'Namespace contains disallowed characters' (#1425). This is the write-side check only — memory_list/cleanup/export run the stricter shared validateIdentifier, so a namespace that passes here (e.g. one with spaces) can still fail those tools later.","triggerScenarios":"memory_store with namespace 'patterns#auth', 'team(a)', 'a$b', 'ns|prod', or 'data/../prod'. Note ':' and spaces pass this write-side check but spaces fail the list-side validator, producing the store/list asymmetry.","commonSituations":"Namespaces built from user or org names, folder paths, or tags that carry punctuation; unexpanded shell variables ('$name') copied from examples; shared root cause with key rejection — both checks use the same character set constant.","solutions":["Use a plain identifier namespace — alphanumeric plus _, -, ., : — which satisfies both this check and the stricter list/cleanup/export validator","Sanitize with the same replacement the library applies to keys (dangerous characters → '_')","Never build namespaces with '../' segments; use a flat naming scheme","Keep the namespace under 128 characters so the stricter validator's length cap also passes"],"exampleFix":"// before\nawait mcp.callTool('memory_store', { key: 'auth', value, namespace: 'patterns#v2' }); // Namespace contains disallowed characters\n\n// after\nawait mcp.callTool('memory_store', { key: 'auth', value, namespace: 'patterns-v2' });","handlingStrategy":"validation","validationCode":"// Use one canonical namespace policy that satisfies BOTH validators:\n// write-side DANGEROUS_KEY_CHARS and the stricter read-side IDENTIFIER_RE.\nconst IDENTIFIER_RE = /^[a-zA-Z0-9_][a-zA-Z0-9_\\-.:]{0,127}$/;\nfunction safeNamespace(ns: string): string | undefined {\n  const clean = ns.replace(/[^A-Za-z0-9_\\-.:]+/g, '-').replace(/^[^A-Za-z0-9_]+/, '');\n  return IDENTIFIER_RE.test(clean) ? clean : undefined; // undefined = list all namespaces\n}","typeGuard":null,"tryCatchPattern":"try {\n  await memoryStore({ key, value, namespace });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Namespace contains disallowed characters')) {\n    // sanitize namespace and retry once with the allowlist replacement\n  }\n  throw e;\n}","preventionTips":["Restrict namespaces to alphanumerics plus _, -, ., : from day one — it satisfies every memory tool, not just the store path","Never build namespaces from unexpanded shell variables or pasted markdown","Watch the asymmetry: memory_store tolerates characters (spaces) that memory_list/cleanup/export reject","Validate namespaces at configuration load time, not at call time, so bad values fail loudly and early"],"tags":["memory","mcp","security","validation","namespace","injection"],"backgroundTag":"invalid-identifier-characters","analyzedSha":"9c61c86f06b439af2a95085ae9bb0ca839662e41","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}