abhigyanpatwari/GitNexus · error · Error

Invalid repository name

Error message

Invalid repository name

What it means

getCloneDir re-validates its repoName argument at the boundary — callers may pass names from test fixtures, scripts, or other sources that never went through extractRepoName. Any name failing REPO_NAME_PATTERN ^[a-zA-Z0-9._-]+$, or '.'/'..'/empty, throws before path.join(CLONE_ROOT, repoName), so the result can never escape the clone root.

Source

Thrown at gitnexus/src/server/git-clone.ts:60

  const name = parseRepoNameFromUrl(url);
  if (
    !name ||
    name === '.' ||
    name === '..' ||
    name === 'unknown' ||
    !REPO_NAME_PATTERN.test(name)
  ) {
    throw new Error('Could not extract a valid repository name from URL');
  }
  return name;
}

/** Get the clone target directory for a repo name. */
export function getCloneDir(repoName: string): string {
  // Re-validate at the boundary even though extractRepoName already checked —
  // callers may pass a repoName from another source (test fixtures, scripts).
  if (!repoName || repoName === '.' || repoName === '..' || !REPO_NAME_PATTERN.test(repoName)) {
    throw new Error('Invalid repository name');
  }
  return path.join(CLONE_ROOT, repoName);
}

// Cloud metadata hostnames that must never be reachable via user-supplied URLs
const BLOCKED_HOSTNAMES = new Set([
  'localhost',
  'metadata.google.internal',
  'metadata.azure.com',
  'metadata.internal',
]);

/**
 * Validate a git URL to prevent SSRF attacks.
 * Only allows https:// and http:// schemes. Blocks private/internal addresses,
 * IPv6 private ranges, cloud metadata hostnames, and numeric IP encodings.
 */
export function validateGitUrl(url: string): void {

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Derive names with extractRepoName(url) (or sanitizeRepoName) before calling getCloneDir
  2. Restrict inputs to [A-Za-z0-9._-] and strip/reject everything else
  3. Import the exported REPO_NAME_PATTERN for shared validation

Example fix

// before
const dir = getCloneDir(userTypedName); // 'my repo' -> throws

// after
import { REPO_NAME_PATTERN } from '../server/git-clone.js';
const safe = REPO_NAME_PATTERN.test(userTypedName) ? userTypedName : sanitizeRepoName(userTypedName);
const dir = getCloneDir(safe);
Defensive patterns

Strategy: type-guard

Validate before calling

import { REPO_NAME_PATTERN } from './git-clone.js';
function assertSafeRepoName(name) {
  if (!REPO_NAME_PATTERN.test(name) || name === '.' || name === '..') {
    throw new Error(`Invalid repository name: ${JSON.stringify(name)}`);
  }
}

Type guard

import { REPO_NAME_PATTERN } from './git-clone.js';
function isSafeRepoName(name) {
  return typeof name === 'string' && name.length > 0 && name !== '.' && name !== '..' && REPO_NAME_PATTERN.test(name);
}
// narrow before use: if (isSafeRepoName(n)) dir = getCloneDir(n);

Try / catch

try { return getCloneDir(repoName); }
catch (e) {
  if (e.message === 'Invalid repository name') return getCloneDir(sanitizeRepoName(repoName)); // one repair attempt
  throw e;
}

Prevention

When it happens

Trigger: Directly calling getCloneDir('my repo'), getCloneDir('foo/bar'), getCloneDir('../escape'), or getCloneDir('') from a script/test/fixture instead of deriving the name via extractRepoName.

Common situations: Test fixtures inventing repo names with spaces or slashes; scripts passing user-typed display names straight through; refactors that route names around the validated extractor.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/6dc8467c8706dc33. Report an issue: GitHub.