{"record":{"id":"699eb636a0ea963e","repo":"abhigyanpatwari/GitNexus","slug":"clone-target-repo-name-expectedreponame-does-no","errorCode":null,"errorMessage":"Clone target repo name ${expectedRepoName} does not match requested URL","messagePattern":"Clone target repo name (.+?) does not match requested URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/server/git-clone.ts","lineNumber":337,"sourceCode":" *     (CodeQL js/second-order-command-line-injection).\n */\nexport async function cloneOrPull(\n  url: string,\n  targetDir: string,\n  onProgress?: (progress: CloneProgress) => void,\n  options?: CloneOrPullOptions,\n): Promise<string> {\n  // Containment barrier — inline with the canonical path.relative idiom so\n  // CodeQL recognizes the sanitizer at every following filesystem and\n  // subprocess sink. The same `safeTarget` is used for every downstream\n  // path operation — no reassignment that the analyzer could lose track of.\n  //\n  // The lexical check runs before filesystem creation; realpath and symlink\n  // checks below run before pull/clone and again after clone completes.\n  const cloneRoot = path.resolve(options?.allowedCloneRoot ?? CLONE_ROOT);\n  const expectedRepoName = options?.expectedRepoName;\n  if (expectedRepoName !== undefined && expectedRepoName !== extractRepoName(url)) {\n    throw new Error(`Clone target repo name ${expectedRepoName} does not match requested URL`);\n  }\n\n  const safeTarget = path.resolve(targetDir);\n  if (expectedRepoName !== undefined && path.basename(safeTarget) !== expectedRepoName) {\n    throw new Error(`Clone target basename must match repository name ${expectedRepoName}`);\n  }\n\n  const rel = path.relative(cloneRoot, safeTarget);\n  if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {\n    throw new Error(`Clone target must be a subdirectory of ${cloneRoot}`);\n  }\n\n  // Always validate the requested URL — the prior shape only ran this in\n  // the code path where the repo was cloned. Now it runs unconditionally,\n  // preventing SSRF / blocked-host bypasses even when targetDir already exists.\n  if (options?.allowAutoSyncSsh) validateAutoSyncRemoteUrl(url);\n  else validateGitUrl(url);\n  await fs.mkdir(cloneRoot, { recursive: true });","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/server/git-clone.ts#L319-L355","documentation":"cloneOrPull throws this when an expectedRepoName option was supplied and the repository name derived from the clone URL (extractRepoName) does not match it. This prevents cloning a URL whose repo differs from the expected, name-pinned target — a supply-chain / typo protection. The check is purely lexical and runs before any filesystem work.","triggerScenarios":"Calling cloneOrPull with both expectedRepoName and a url whose last path segment (minus .git) differs from expectedRepoName, e.g. url 'https://host/gitnexus.git' with expectedRepoName 'other-repo'.","commonSituations":"Caller derives the expected name from a config entry or a prior index but passes a stale/renamed URL; branch-sync code iterating remotes where one remote points at a fork with a different name; user-edited remote URLs after a repo rename on the host.","solutions":["Correct the url so its repo name matches expectedRepoName.","Update expectedRepoName to the actual name of the repo at the URL.","Drop expectedRepoName (pass undefined) if the name pin is not required for this call.","Log/inspect extractRepoName(url) to see what name the URL actually yields and reconcile it with config."],"exampleFix":"// before\nawait cloneOrPull({ url: 'https://host/org/wrong-name.git', expectedRepoName: 'right-name' });\n// after\nawait cloneOrPull({ url: 'https://host/org/right-name.git', expectedRepoName: 'right-name' });","handlingStrategy":"validation","validationCode":"import { extractRepoName } from './git-clone.js';\nif (expectedRepoName !== undefined && extractRepoName(url) !== expectedRepoName) {\n  throw new Error(`URL ${url} does not match expected repo ${expectedRepoName}`);\n}\nawait cloneOrPull({ url, expectedRepoName, targetDir });","typeGuard":null,"tryCatchPattern":"try {\n  await cloneOrPull(opts);\n} catch (err) {\n  if ((err as Error).message.startsWith('Clone target repo name')) {\n    console.error(`Configured repo name does not match URL ${opts.url}; fix config or URL.`);\n  }\n  throw err;\n}","preventionTips":["Derive expectedRepoName from extractRepoName(url) in callers instead of storing it separately.","Validate remote URLs against configured names in CI before deploy.","Alert on repo renames at the host so pinned names stay current."],"tags":["git","clone","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-09-08T00:40:44.970Z","contentChangedAt":"2026-09-08T00:40:44.970Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}