{"record":{"id":"ba33390dd8b8784f","repo":"abhigyanpatwari/GitNexus","slug":"cloning-from-private-internal-addresses-is-not-all","errorCode":null,"errorMessage":"Cloning from private/internal addresses is not allowed","messagePattern":"Cloning from private/internal addresses is not allowed","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/server/git-clone.ts","lineNumber":94,"sourceCode":" * IPv6 private ranges, cloud metadata hostnames, and numeric IP encodings.\n */\nexport function validateGitUrl(url: string): void {\n  let parsed: URL;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error('Invalid URL');\n  }\n\n  if (!['https:', 'http:'].includes(parsed.protocol)) {\n    throw new Error('Only https:// and http:// git URLs are allowed');\n  }\n\n  const host = parsed.hostname.toLowerCase();\n\n  // Block known dangerous hostnames (cloud metadata services)\n  if (BLOCKED_HOSTNAMES.has(host)) {\n    throw new Error('Cloning from private/internal addresses is not allowed');\n  }\n\n  // Strip IPv6 brackets if present (URL parser behavior varies across Node versions)\n  let normalizedHost = host;\n  if (host.startsWith('[') && host.endsWith(']')) {\n    normalizedHost = host.slice(1, -1);\n  }\n\n  // Check if this is an IPv6 address\n  // Use manual colon detection as fallback since isIP may return 0 for some\n  // normalized IPv6 forms (e.g. ::ffff:7f00:1)\n  const isIPv6 = isIP(normalizedHost) === 6 || normalizedHost.includes(':');\n  if (isIPv6) {\n    assertNotPrivateIPv6(normalizedHost);\n    return;\n  }\n\n  // Check if this is an IPv4 address (including numeric encodings)","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/git-clone.ts#L76-L112","documentation":"validateGitUrl lowercases the parsed hostname and rejects a fixed blocklist — localhost, metadata.google.internal, metadata.azure.com, metadata.internal — before DNS or clone happens. These are the hostnames of the server itself and of cloud instance-metadata services; cloning from them is the canonical SSRF primitive this guard exists to deny.","triggerScenarios":"POST /api/analyze with url='http://localhost:8080/repo.git' (local Gitea on the same box) or 'http://metadata.google.internal/computeMetadata/v1/...' (probing GCP metadata via the clone feature).","commonSituations":"Testing against a self-hosted local git server; penetration-test payloads; misconfigured tooling that records 'localhost' instead of a real hostname for an internal mirror. The block is intentional, not a bug to work around from untrusted input.","solutions":["Expose the git server over a real (public, https) hostname and use that URL","For repos already on the server's machine, use the analyze-by-'path' option (absolute filesystem path) instead of cloning","Never attempt to reach cloud metadata endpoints through this API"],"exampleFix":"// before\n{ url: 'http://localhost:8080/myrepo.git' }\n\n// after\n{ path: '/srv/git/myrepo' } // analyze the local copy directly","handlingStrategy":"validation","validationCode":"const BLOCKED = new Set(['localhost', 'metadata.google.internal', 'metadata.azure.com', 'metadata.internal']);\nfunction targetsBlockedHost(url) {\n  try { return BLOCKED.has(new URL(url).hostname.toLowerCase()); } catch { return true; }\n}","typeGuard":"function isNonBlockedHostname(host) { return !BLOCKED.has(String(host).toLowerCase()); }","tryCatchPattern":"try { validateGitUrl(url); }\ncatch (e) {\n  if (/private\\/internal addresses|Invalid URL|https/.test(e.message)) rejectSubmission(e.message); // permanent client error; do not retry\n  else throw e;\n}","preventionTips":["Never submit localhost or metadata hostnames in analyze URLs","Model these as permanent 4xx-style rejections in retry policies — retrying cannot help","Route local repos through the path option"],"tags":["git-clone","ssrf","security","network-blocklist"],"backgroundTag":"ssrf-protection","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}