{"record":{"id":"f4fc230f7c341964","repo":"ruvnet/ruflo","slug":"invalid-git-ref-too-long","errorCode":null,"errorMessage":"Invalid git ref: too long","messagePattern":"Invalid git ref: too long","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/ruvector/diff-classifier.ts","lineNumber":383,"sourceCode":"\n/**\n * Validate git ref to prevent command injection\n * Only allows safe characters: alphanumeric, -, _, /, ., ~, ^\n */\nfunction validateGitRef(ref: string): void {\n  // Block shell metacharacters and dangerous patterns\n  if (!/^[a-zA-Z0-9_\\-./~^@]+$/.test(ref)) {\n    throw new Error(`Invalid git ref: contains unsafe characters`);\n  }\n  // Block multiple dots (path traversal)\n  if (ref.includes('..') && !ref.match(/^[a-zA-Z0-9_\\-]+\\.\\.\\.?[a-zA-Z0-9_\\-]+$/)) {\n    if (!/^\\w+\\.\\.[.\\w]+$/.test(ref)) {\n      throw new Error(`Invalid git ref: suspicious pattern`);\n    }\n  }\n  // Max length check\n  if (ref.length > 256) {\n    throw new Error(`Invalid git ref: too long`);\n  }\n}\n\n/**\n * Get git diff statistics using SINGLE combined command (optimized)\n * Replaces two separate git commands with one\n */\nexport function getGitDiffNumstat(ref: string = 'HEAD'): DiffFile[] {\n  // SECURITY: Validate git ref to prevent command injection\n  validateGitRef(ref);\n\n  // Check cache first\n  const cacheKey = `numstat:${ref}`;\n  const cached = diffCache.get(cacheKey);\n  if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {\n    return cached.files;\n  }\n","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/ruvector/diff-classifier.ts#L365-L401","documentation":"The final stage of validateGitRef(): the ref passed the charset and '..' checks but exceeds 256 characters. Real git refs are bounded (git itself caps refs at ~260 bytes), so anything longer is garbage or an injection attempt even if it is well-formed — the guard rejects it outright.","triggerScenarios":"Passing a pasted git URL ('https://github.com/org/repo.git' plus path), a concatenated ref list ('main dev feat/x release'), base64 or JWT-looking blobs that happen to be alphanumeric, or a ref built by accidental string repetition.","commonSituations":"UI textareas or generic 'reference' fields piped into diff stats; string concatenation bugs joining several refs; logs or tokens pasted where a ref belongs; copy-paste from issue trackers including formatting.","solutions":["Trim the input and pass exactly one ref","Validate length client-side: reject > 256 chars (and empty strings) before calling the library","Resolve verbose refnames to 40-char SHAs before passing","Log the offending ref length when this fires — it almost always indicates wrong input upstream, not a legit ref"],"exampleFix":"// before\nconst files = getGitDiffNumstat(rawUserText); // 300-char blob → throws: too long\n// after\nconst ref = rawUserText.trim();\nif (!ref || ref.length > 256 || !/^[a-zA-Z0-9_\\-./~^@]+$/.test(ref)) {\n  throw new Error(`invalid git ref: ${JSON.stringify(ref.slice(0, 40))}...`);\n}\nconst files = getGitDiffNumstat(ref);","handlingStrategy":"validation","validationCode":"function normalizeGitRef(ref: string): string {\n  const r = (ref ?? '').trim();\n  if (!r || r.length > 256) throw new Error('git ref must be 1..256 chars');\n  return r;\n}","typeGuard":"const isPlausibleGitRef = (ref: string): boolean =>\n  typeof ref === 'string' && ref.length > 0 && ref.length <= 256;","tryCatchPattern":"try {\n  files = getGitDiffNumstat(ref);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('too long')) {\n    res.status(400).send('git ref too long'); // wrong input shape, not transient\n  } else throw e;\n}","preventionTips":["Enforce a single-ref, single-line input field for refs in UIs","Reject > 256 chars at your boundary — legit refs never approach it","When this fires, log the length: it almost always means wrong data (URL/blob) was passed in"],"tags":["git","security","input-validation","length-limit","diff"],"backgroundTag":"invalid-git-ref","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}