{"record":{"id":"f93b3052f52036ea","repo":"abhigyanpatwari/GitNexus","slug":"invalid-url","errorCode":null,"errorMessage":"Invalid URL","messagePattern":"Invalid URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/server/git-clone.ts","lineNumber":84,"sourceCode":"}\n\n/**\n * Derive a clone directory name for the web `/api/analyze` boundary.\n *\n * The API historically accepted Azure DevOps and similar URLs whose repo\n * segment contains spaces or other directory-unsafe characters by sanitizing\n * the final segment. Keep that compatibility at the web boundary while leaving\n * `extractRepoName()` strict for internal/security-sensitive callers.\n */\nexport function extractWebRepoName(url: string): string {\n  let trimmed = url.trim();\n  while (trimmed.endsWith('/')) trimmed = trimmed.slice(0, -1);\n  const withoutGit = trimmed.toLowerCase().endsWith('.git') ? trimmed.slice(0, -4) : trimmed;\n  const rawName = withoutGit.split(/[/:]/).filter(Boolean).pop() ?? '';\n  const safeName = sanitizeRepoName(rawName);\n  if (!rawName || safeName === 'unknown') {\n    throw new Error('Could not extract a valid repository name from URL');\n  }\n  return safeName;\n}\n\n/**\n * Longest single path component the supported filesystems accept (ext4, APFS,\n * NTFS all cap at 255). Compared against `.length`, which equals the byte\n * count here because every name this guards is ASCII by construction\n * (REPO_NAME_PATTERN and sanitizeRepoName both restrict to `[a-zA-Z0-9._-]`).\n */\nconst MAX_PATH_COMPONENT_BYTES = 255;\n\n/**\n * `branchSlug` for a clone-directory name, trimmed to fit one path component.\n *\n * `validateBranchName` allows a ref up to 255 characters and `branchSlug`\n * appends `-` plus 8 hash characters, so `<repo>__<slug>` can reach 267 — past\n * the filesystem limit, and the clone would then fail to create its target\n * directory (#3199 review).","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/server/git-clone.ts#L66-L102","documentation":"validateGitUrl's first step parses the input with new URL(); any string the WHATWG URL parser rejects — missing scheme, embedded spaces/control characters, bare 'github.com/user/repo' — throws and is rethrown as 'Invalid URL'. This is the malformed-input gate in front of the SSRF checks that follow.","triggerScenarios":"POST /api/analyze with url lacking a scheme ('github.com/user/repo.git'), containing raw spaces or newlines ('https:// ex ample.com'), or otherwise unparseable garbage such as pasted placeholder text.","commonSituations":"Users omitting https:// because git itself tolerates it; copy-paste introducing whitespace or a newline; frontends not trimming the field; scp-style 'git@host:repo' syntax, which the URL parser cannot parse at all.","solutions":["Trim whitespace/newlines from the URL before submitting","Prepend https:// when the scheme is missing","Run new URL(url) client-side as a pre-flight check","Convert scp-style git@host:repo remotes to https://host/repo form"],"exampleFix":"// before\nconst url = 'github.com/user/repo.git';\nvalidateGitUrl(url); // Invalid URL\n\n// after\nconst url = 'https://github.com/user/repo.git';\nvalidateGitUrl(url); // ok","handlingStrategy":"validation","validationCode":"function isParseableHttpUrl(s) {\n  if (typeof s !== 'string') return false;\n  try { const u = new URL(s.trim()); return u.protocol === 'https:' || u.protocol === 'http:'; }\n  catch { return false; }\n}","typeGuard":"function asTrimmedAbsoluteUrl(raw) {\n  const s = typeof raw === 'string' ? raw.trim() : '';\n  if (!s) return null;\n  const withScheme = /^[a-z][a-z0-9+.-]*:/i.test(s) ? s : `https://${s}`;\n  try { return new URL(withScheme).href; } catch { return null; }\n}","tryCatchPattern":"try { validateGitUrl(url); }\ncatch (e) {\n  if (e.message === 'Invalid URL') { const fixed = asTrimmedAbsoluteUrl(url); if (!fixed) throw e; validateGitUrl(fixed); }\n  else throw e;\n}","preventionTips":["Trim pasted URLs and strip newlines before submitting","Auto-prepend https:// when the scheme is missing","Run new URL() in the client as the cheapest possible pre-flight"],"tags":["git-clone","url-parsing","validation","ssrf-guard"],"backgroundTag":"malformed-url","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}