ruvnet/ruflo · error · Error

Invalid git ref: contains unsafe characters

Error message

Invalid git ref: contains unsafe characters

What it means

Thrown by validateGitRef(ref) when ref contains any character outside the safe set [a-zA-Z0-9_\-./~^@]. This is the first of three guards in getGitDiffNumstat's input validation, designed to block shell metacharacters before ref is passed to git via execFileSync. Even though execFileSync uses an args array (no shell), the denylist is defense-in-depth against argument-injection vectors. Refuses hard.

Source

Thrown at v3/@claude-flow/cli/src/ruvector/diff-classifier.ts:373

  recommendedReviewers?: string[];
}

// ============================================================================
// Optimized Git Diff Functions
// ============================================================================

// Cache for diff results (TTL-based)
const diffCache = new Map<string, { files: DiffFile[]; timestamp: number }>();
const CACHE_TTL_MS = 5000; // 5 seconds - short TTL since diffs change frequently

/**
 * Validate git ref to prevent command injection
 * Only allows safe characters: alphanumeric, -, _, /, ., ~, ^
 */
function validateGitRef(ref: string): void {
  // Block shell metacharacters and dangerous patterns
  if (!/^[a-zA-Z0-9_\-./~^@]+$/.test(ref)) {
    throw new Error(`Invalid git ref: contains unsafe characters`);
  }
  // Block multiple dots (path traversal)
  if (ref.includes('..') && !ref.match(/^[a-zA-Z0-9_\-]+\.\.\.?[a-zA-Z0-9_\-]+$/)) {
    if (!/^\w+\.\.[.\w]+$/.test(ref)) {
      throw new Error(`Invalid git ref: suspicious pattern`);
    }
  }
  // Max length check
  if (ref.length > 256) {
    throw new Error(`Invalid git ref: too long`);
  }
}

/**
 * Get git diff statistics using SINGLE combined command (optimized)
 * Replaces two separate git commands with one
 */
export function getGitDiffNumstat(ref: string = 'HEAD'): DiffFile[] {

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Strip/trim the ref before validation and reject anything containing whitespace.
  2. If you allow user input, validate against an explicit allowlist of known refs (git branch --list output) rather than passing raw.
  3. Normalize backslashes to forward slashes on Windows BEFORE validation.
  4. Never build the ref by concatenating untrusted segments — use a fixed schema.

Example fix

// before
const files = getGitDiffNumstat(req.query.ref);

// after — sanitize then validate
const ref = String(req.query.ref ?? 'HEAD').trim().replace(/\\/g, '/');
if (!/^[a-zA-Z0-9_\-./~^@]+$/.test(ref)) {
  throw new Error('invalid git ref');
}
const files = getGitDiffNumstat(ref);
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeGitRef(ref: unknown): string {
  const s = String(ref ?? 'HEAD').trim().replace(/\\/g, '/');
  if (!/^[a-zA-Z0-9_\-./~^@]+$/.test(s)) {
    throw new Error(`ref contains unsafe characters: ${JSON.stringify(s)}`);
  }
  return s;
}

const safe = sanitizeGitRef(req.query.ref);
const files = getGitDiffNumstat(safe);

Type guard

function isSafeGitRef(ref: string): boolean {
  return typeof ref === 'string' && /^[a-zA-Z0-9_\-./~^@]+$/.test(ref) && ref.length <= 256;
}

Try / catch

try {
  return getGitDiffNumstat(ref);
} catch (e) {
  if (/Invalid git ref/.test(String(e))) {
    // Refused on purpose — do NOT mutate and retry. Surface to caller.
    throw new Error(`rejected ref ${JSON.stringify(ref)}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Ref contains spaces, semicolons, pipes, backticks, $, (), {}, [], angle brackets, quotes, backslashes, commas, colons, or any other punctuation; a ref like 'origin/main;rm -rf /' or 'HEAD & cmd'; a ref built by string concatenation that picked up a stray space or newline.

Common situations: User-supplied ref from an HTTP query param or webhook payload passed unfiltered; ref read from a config file with trailing whitespace/newline; branch name containing a slash-separated team prefix that happens to include a blocked char; Windows ref string with a backslash path separator.

Related errors


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