ruvnet/ruflo · error · Error

${label} escapes project directory

Error message

${label} escapes project directory

What it means

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.

Source

Thrown at v3/@claude-flow/cli/src/commands/daemon.ts:385

  // Must be absolute after resolution
  const resolved = resolve(path);

  // Check for null bytes (injection attack)
  if (path.includes('\0')) {
    throw new Error(`${label} contains null bytes`);
  }

  // Check for shell metacharacters in path components
  if (/[;&|`$<>]/.test(path)) {
    throw new Error(`${label} contains shell metacharacters`);
  }

  // Prevent path traversal outside expected directories
  if (!resolved.includes('.claude-flow') && !resolved.includes('bin')) {
    // Allow only paths within project structure
    const cwd = process.cwd();
    if (!resolved.startsWith(cwd)) {
      throw new Error(`${label} escapes project directory`);
    }
  }
}

/**
 * #1914: Resolve the `--workspace` flag to an absolute path, or return null
 * if it is absent / not a usable string. Rejects values with null bytes or
 * shell metacharacters (defence-in-depth — the value is later embedded in a
 * forked child's argv and compared against `ps`/`tasklist` output).
 */
export function resolveWorkspaceFlag(raw: unknown): string | null {
  if (typeof raw !== 'string') return null;
  const trimmed = raw.trim();
  if (!trimmed) return null;
  if (trimmed.includes('\0') || /[;&|`$<>]/.test(trimmed)) return null;
  return resolve(trimmed);
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Ensure the path resolves within the current working directory
  2. Remove '../' sequences that would traverse above the project root
  3. If the path legitimately points to a .claude-flow or bin directory, verify the resolved path contains that substring

Example fix

// before (cwd is /home/user/myproject)
validatePath('../../etc/config', 'config');

// after
validatePath('./config/daemon.json', 'config');
// or use an absolute path within the project
validatePath('/home/user/myproject/config/daemon.json', 'config');
Defensive patterns

Strategy: validation

Validate before calling

import { resolve } from 'path';

function isWithinProject(path: string, cwd: string = process.cwd()): boolean {
  const resolved = resolve(path);
  return resolved.includes('.claude-flow') || resolved.includes('bin') || resolved.startsWith(cwd);
}

if (!isWithinProject(userPath)) {
  throw new Error('Path escapes project directory');
}

Type guard

function isWithinCwd(path: string, cwd: string): boolean {
  const resolved = resolve(path);
  return resolved.startsWith(cwd) || resolved.includes('.claude-flow') || resolved.includes('bin');
}

Try / catch

try {
  validatePath(userPath, 'workspace');
} catch (e) {
  if (e instanceof Error && e.message.includes('escapes project directory')) {
    // Use a path within cwd or a .claude-flow directory
    userPath = resolve(process.cwd(), userPath.replace(/^\.\.[\/\\]/g, ''));
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/f8617774e51ac9e8. Report an issue: GitHub.