{"record":{"id":"f8617774e51ac9e8","repo":"ruvnet/ruflo","slug":"label-escapes-project-directory","errorCode":null,"errorMessage":"${label} escapes project directory","messagePattern":"(.+?) escapes project directory","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/commands/daemon.ts","lineNumber":385,"sourceCode":"  // Must be absolute after resolution\n  const resolved = resolve(path);\n\n  // Check for null bytes (injection attack)\n  if (path.includes('\\0')) {\n    throw new Error(`${label} contains null bytes`);\n  }\n\n  // Check for shell metacharacters in path components\n  if (/[;&|`$<>]/.test(path)) {\n    throw new Error(`${label} contains shell metacharacters`);\n  }\n\n  // Prevent path traversal outside expected directories\n  if (!resolved.includes('.claude-flow') && !resolved.includes('bin')) {\n    // Allow only paths within project structure\n    const cwd = process.cwd();\n    if (!resolved.startsWith(cwd)) {\n      throw new Error(`${label} escapes project directory`);\n    }\n  }\n}\n\n/**\n * #1914: Resolve the `--workspace` flag to an absolute path, or return null\n * if it is absent / not a usable string. Rejects values with null bytes or\n * shell metacharacters (defence-in-depth — the value is later embedded in a\n * forked child's argv and compared against `ps`/`tasklist` output).\n */\nexport function resolveWorkspaceFlag(raw: unknown): string | null {\n  if (typeof raw !== 'string') return null;\n  const trimmed = raw.trim();\n  if (!trimmed) return null;\n  if (trimmed.includes('\\0') || /[;&|`$<>]/.test(trimmed)) return null;\n  return resolve(trimmed);\n}\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/commands/daemon.ts#L367-L403","documentation":"Thrown by the internal validatePath() function in daemon.ts when a resolved absolute path does not start with process.cwd() and does not contain '.claude-flow' or 'bin' in its resolved form. This prevents path traversal outside the expected project directory structure. The '.claude-flow' and 'bin' exceptions allow legitimate daemon infrastructure paths.","triggerScenarios":"resolve(path) yields an absolute path outside the current working directory (e.g. '/etc/passwd' when cwd is '/home/user/project'), and the resolved path does not include '.claude-flow' or 'bin' as substrings.","commonSituations":"A relative path with '../' sequences that escapes the project root; an absolute path pointing to system directories; the daemon was started from a different working directory than expected.","solutions":["Ensure the path resolves within the current working directory","Remove '../' sequences that would traverse above the project root","If the path legitimately points to a .claude-flow or bin directory, verify the resolved path contains that substring"],"exampleFix":"// before (cwd is /home/user/myproject)\nvalidatePath('../../etc/config', 'config');\n\n// after\nvalidatePath('./config/daemon.json', 'config');\n// or use an absolute path within the project\nvalidatePath('/home/user/myproject/config/daemon.json', 'config');","handlingStrategy":"validation","validationCode":"import { resolve } from 'path';\n\nfunction isWithinProject(path: string, cwd: string = process.cwd()): boolean {\n  const resolved = resolve(path);\n  return resolved.includes('.claude-flow') || resolved.includes('bin') || resolved.startsWith(cwd);\n}\n\nif (!isWithinProject(userPath)) {\n  throw new Error('Path escapes project directory');\n}","typeGuard":"function isWithinCwd(path: string, cwd: string): boolean {\n  const resolved = resolve(path);\n  return resolved.startsWith(cwd) || resolved.includes('.claude-flow') || resolved.includes('bin');\n}","tryCatchPattern":"try {\n  validatePath(userPath, 'workspace');\n} catch (e) {\n  if (e instanceof Error && e.message.includes('escapes project directory')) {\n    // Use a path within cwd or a .claude-flow directory\n    userPath = resolve(process.cwd(), userPath.replace(/^\\.\\.[\\/\\\\]/g, ''));\n  }\n}","preventionTips":["Use relative paths or paths anchored to process.cwd()","Avoid '../' sequences that traverse above the project root","Paths under .claude-flow/ or bin/ are explicitly allowed"],"tags":["security","path-traversal","daemon","sandbox"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}