ruvnet/ruflo · error · Error

basePath contains disallowed characters

Error message

basePath contains disallowed characters

What it means

Thrown by resolveBasePath() in agentbbs-tools when the user-supplied basePath matches the regex `/\.\.[\\/]|\0/` — i.e. it contains a `..` path segment (forward or back slash) or a NUL byte. This is the D-2 path-traversal hardening shared across the MCP verbs: it prevents a caller from escaping the resolved project cwd root. The default `.agentbbs` is always safe.

Source

Thrown at v3/@claude-flow/cli/src/mcp-tools/agentbbs-tools.ts:64

  } catch (err: any) {
    if (err && (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND' ||
                /Cannot find (module|package)/i.test(String(err?.message)))) {
      _agentbbsMod = false;
      return null;
    }
    throw err;
  }
}

function degradedResult(reason: string): { success: true; degraded: true; reason: string } {
  return { success: true, degraded: true, reason };
}

function resolveBasePath(input?: string): string {
  const p = input && typeof input === 'string' && input.length > 0
    ? input
    : '.agentbbs';
  if (/\.\.[\\/]|\0/.test(p)) throw new Error('basePath contains disallowed characters');
  const abs = isAbsolute(p) ? p : resolve(getProjectCwd(), p);
  return abs;
}

function validateRoomLabel(label: string): string {
  if (!label || typeof label !== 'string') throw new Error('roomLabel is required');
  if (label.length > 128) throw new Error('roomLabel exceeds 128 chars');
  // Rooms are conventionally `#sales`, `#finance`, etc. — keep `#` in the allow-list.
  if (!/^[A-Za-z0-9_.\-:/@#]+$/.test(label)) {
    throw new Error('roomLabel may only contain [A-Za-z0-9_.\\-:/@#]');
  }
  return label;
}

function validateRoomId(roomId: string): string {
  if (!roomId || typeof roomId !== 'string') throw new Error('roomId is required');
  if (roomId.length > 128) throw new Error('roomId exceeds 128 chars');
  if (!/^[A-Za-z0-9_.\-:/@#]+$/.test(roomId)) {

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Pass a simple relative directory name (e.g. `.agentbbs`) or an absolute path inside the project root.
  2. Sanitize upstream: strip `..` segments and NUL bytes before calling the tool, or resolve+verify the result is inside getProjectCwd().
  3. If an absolute path outside the project is genuinely required, host the BBS state there directly (absolute paths are allowed as long as they contain no `..` or NUL), but confirm the operator intends that location.

Example fix

// before — traversal attempt
federation_bbs_register({ basePath: '../../etc', roomLabel: '#x' });
// after
federation_bbs_register({ basePath: '.agentbbs', roomLabel: '#x' });
Defensive patterns

Strategy: validation

Validate before calling

function safeBasePath(p?: string): string {
  const v = (p && typeof p === 'string' && p.length > 0) ? p : '.agentbbs';
  if (/\.\.[\\/]|\0/.test(v)) throw new Error('basePath would escape project root');
  return v;
}
const basePath = safeBasePath(input.basePath);

Type guard

const isTraversalFree = (p: string): boolean => typeof p === 'string' && !/\.\.[\\/]|\0/.test(p);

Try / catch

null

Prevention

When it happens

Trigger: A tool caller passes `basePath: '../../../tmp'` or `basePath: 'foo/../bar'`; an automated pipeline interpolates an unvalidated env var into basePath; a NUL byte is injected to truncate the path in the underlying syscall.

Common situations: User-supplied config value for basePath that was not sanitized; a path built via template string from request data; testing harness that fed a literal traversal string.

Related errors


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