{"record":{"id":"520285eb83c45f4c","repo":"abhigyanpatwari/GitNexus","slug":"could-not-extract-a-valid-repository-name-from-url","errorCode":null,"errorMessage":"Could not extract a valid repository name from URL","messagePattern":"Could not extract a valid repository name from URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/server/git-clone.ts","lineNumber":50,"sourceCode":"export const REPO_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;\n\n/**\n * Extract the repository name from a git URL (HTTPS or SSH).\n *\n * Throws if the URL does not yield a filesystem-safe last segment. A name\n * like `..` or `foo/bar` would otherwise let `getCloneDir(name)` escape the\n * clone root via path traversal.\n */\nexport function extractRepoName(url: string): string {\n  const name = parseRepoNameFromUrl(url);\n  if (\n    !name ||\n    name === '.' ||\n    name === '..' ||\n    name === 'unknown' ||\n    !REPO_NAME_PATTERN.test(name)\n  ) {\n    throw new Error('Could not extract a valid repository name from URL');\n  }\n  return name;\n}\n\n/** Get the clone target directory for a repo name. */\nexport function getCloneDir(repoName: string): string {\n  // Re-validate at the boundary even though extractRepoName already checked —\n  // callers may pass a repoName from another source (test fixtures, scripts).\n  if (!repoName || repoName === '.' || repoName === '..' || !REPO_NAME_PATTERN.test(repoName)) {\n    throw new Error('Invalid repository name');\n  }\n  return path.join(CLONE_ROOT, repoName);\n}\n\n// Cloud metadata hostnames that must never be reachable via user-supplied URLs\nconst BLOCKED_HOSTNAMES = new Set([\n  'localhost',\n  'metadata.google.internal',","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/git-clone.ts#L32-L68","documentation":"extractRepoName takes the last path segment of a git URL (via parseRepoNameFromUrl) and requires a filesystem-safe name matching ^[a-zA-Z0-9._-]+$, additionally rejecting '.', '..', and the 'unknown' sentinel. Anything else throws, because getCloneDir would otherwise let names like '..' or 'foo/bar' escape the clone root via path traversal.","triggerScenarios":"POST /api/analyze (or any cloneOrPull caller) with a URL whose final segment is empty ('https://github.com/user/') or contains characters outside the safe set — spaces ('my repo'), CJK/unicode, backslashes, or shell metacharacters ('repo;ls').","commonSituations":"Trailing-slash or bare-host URLs; repositories with human-display names containing spaces/unicode; pasted URLs with a trailing fragment or whitespace; URLs where the repo slug itself is non-ASCII.","solutions":["Send the canonical HTTPS URL ending in the repo slug, optionally with .git","Ensure the repo slug is pure ASCII alphanumerics plus . _ - (rename or alias non-ASCII slugs)","Trim whitespace and strip fragments/queries from user-pasted URLs before submitting"],"exampleFix":"// before\nconst url = 'https://github.com/user/my repo';\nextractRepoName(url); // throws\n\n// after\nconst url = 'https://github.com/user/my-repo';\nextractRepoName(url); // 'my-repo'","handlingStrategy":"validation","validationCode":"const SAFE_NAME = /^[a-zA-Z0-9._-]+$/;\nfunction lastSegment(url) { return new URL(url).pathname.replace(/\\/+$/, '').split('/').pop() || ''; }\nfunction urlYieldsSafeRepoName(url) {\n  try { const n = lastSegment(url).replace(/\\.git$/, ''); return SAFE_NAME.test(n) && n !== '.' && n !== '..' && n !== 'unknown'; }\n  catch { return false; }\n}","typeGuard":"function isSafeRepoName(name) {\n  return typeof name === 'string' && name.length > 0 && name !== '.' && name !== '..' && name !== 'unknown' && /^[a-zA-Z0-9._-]+$/.test(name);\n}","tryCatchPattern":"try { targetPath = getCloneDir(extractRepoName(url)); }\ncatch (e) {\n  if (e.message === 'Could not extract a valid repository name from URL') throw new BadRequest(`unsupported repo url: ${url}`, 400);\n  throw e;\n}","preventionTips":["Normalize URLs client-side: trim, strip fragments/queries, drop trailing slashes","Reject or transliterate non-ASCII repo slugs before submission","Keep repo names to [A-Za-z0-9._-] by policy"],"tags":["git-clone","url-parsing","validation","path-traversal-guard"],"backgroundTag":"invalid-repository-url","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}