/** * Git Clone Utility * * Shallow-clones repositories into ~/.gitnexus/repos/{name}/. * If already cloned, does git pull instead. */ /** * Extract the repository name from a git URL (HTTPS or SSH). * * Throws if the URL does not yield a filesystem-safe last segment. A name * like `..` or `foo/bar` would otherwise let `getCloneDir(name)` escape the * clone root via path traversal. */ export declare function extractRepoName(url: string): string; /** Get the clone target directory for a repo name. */ export declare function getCloneDir(repoName: string): string; /** * 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 declare function validateGitUrl(url: string): void; export interface CloneProgress { phase: 'cloning' | 'pulling'; message: string; } /** * Build the `git clone` argument list for a given URL and target directory. * * The `--` separator is non-negotiable: it stops git from parsing a URL that * starts with `--` (e.g. `--upload-pack=evil`) as an option flag, which would * otherwise execute an attacker-chosen subprocess (CodeQL * js/second-order-command-line-injection, alerts #166/#167). * * Exported so the separator placement is testable without mocking spawn. */ export declare function buildCloneArgs(url: string, targetDir: string): string[]; /** * Normalize a git URL into a comparable form. * * Two URLs are considered the same repository when their normalized forms * are identical: lowercased hostname, no trailing `.git`, no trailing * slashes on the path, default port stripped. Path comparison stays * case-sensitive because that's how Git hosts treat the path component on * the wire (case-folding GitHub's web UI is a separate convenience). * * Returns the original input if URL parsing fails — the caller can still * compare with the literal string for non-URL forms (e.g. SSH `git@host:`). */ export declare function normalizeGitUrlForCompare(url: string): string; /** * Read `remote.origin.url` from an existing clone using `git config --get`. * * Returns `null` if the config key is absent, the spawn fails, or the * directory isn't a git repository. The caller decides what a missing * remote means for its threat model — for cloneOrPull, a missing remote * on an existing clone is treated as a refuse-to-pull condition. */ export declare function getRemoteOriginUrl(cwd: string): Promise; /** * Verify that an existing clone's `remote.origin.url` matches the requested * URL (after normalization). Throws on mismatch or missing remote. * * Closes the wrong-repo silent-analysis vector that Codex's adversarial * review on PR #1325 surfaced: clone dirs are keyed by URL basename, so a * request for `https://gitlab.example/attacker/repo.git` would otherwise * collide with an existing `~/.gitnexus/repos/repo` cloned from a different * origin and `git pull --ff-only` would silently succeed against the wrong * remote. * * Exported so the comparison logic is testable in isolation against any * tmpdir-based fixture, without needing to populate CLONE_ROOT. */ export declare function assertRemoteMatchesRequestedUrl(targetDir: string, requestedUrl: string): Promise; /** * Clone or pull a git repository. * If targetDir doesn't exist: git clone --depth 1 * If targetDir exists with .git: git pull --ff-only (after verifying the * existing clone's remote.origin matches the requested URL). * * Security: * - targetDir must resolve inside CLONE_ROOT (~/.gitnexus/repos/). The * path.relative containment barrier below is the inline canonical idiom * CodeQL's js/path-injection sanitizer recognizes. * - validateGitUrl runs unconditionally on the requested URL — both the * clone path and the pull path. An earlier shape only validated on the * clone branch; an existing clone with the same basename let an * attacker's URL skip the SSRF / scheme / private-IP checks (Codex * adversarial review on PR #1325). * - When the target already has `.git`, the existing clone's * remote.origin.url is fetched and compared (normalized) to the * requested URL. Refuses to pull if they differ — this closes the * wrong-repo silent-analysis vector where two URLs sharing a basename * would collide on the same on-disk clone dir. * - The git URL is passed after a `--` separator so a value beginning with * `--` (e.g. `--upload-pack=evil`) cannot be interpreted as a git option * (CodeQL js/second-order-command-line-injection). */ export declare function cloneOrPull(url: string, targetDir: string, onProgress?: (progress: CloneProgress) => void): Promise;