abhigyanpatwari/GitNexus · error · InvalidStoragePathError
Repository path must not contain a NUL character.
Error message
Repository path must not contain a NUL character.
What it means
resolveRepoPath rejects repository paths containing a NUL byte ('\0'), which POSIX/Windows filesystem APIs treat as a string terminator and which often signals path injection. The check runs before path.resolve so no filesystem access happens with the tainted value.
Solutions
- Strip or reject the offending input at the boundary: if (value.includes('\0')) fail with your own clear error.
- If the value came from a Buffer, decode with the correct encoding (buffer.toString('utf8')) and trim trailing NULs: value.replace(/\0+$/, '').
- Treat NUL-bearing input as hostile — log and reject rather than sanitize silently.
Example fix
// before
const repoPath = rawBuffer.toString('utf16le'); // leaves NUL padding
const canonical = await canonicalRepoPath(repoPath);
// after
const repoPath = rawBuffer.toString('utf16le').replace(/\0+$/g, '');
if (repoPath.includes('\0')) throw new Error('repoPath contains NUL bytes');
const canonical = await canonicalRepoPath(repoPath); Defensive patterns
Strategy: validation
Validate before calling
if (typeof repoPath !== 'string' || repoPath.includes('\0')) {
throw new Error('repoPath must be a string without NUL bytes');
} Type guard
const isSafePathString = (v: unknown): v is string => typeof v === 'string' && !v.includes('\0'); Try / catch
try {
return await resolvedRepoPath(repoPath);
} catch (e) {
if (e instanceof InvalidStoragePathError && e.message.includes('NUL')) {
throw new Error('repo path contained a NUL byte — check input encoding/source');
}
throw e;
} Prevention
- Sanitize any path derived from untrusted input at the boundary.
- Decode Buffers with the correct encoding and strip NUL padding.
- Log-and-reject tainted values rather than trying to fix them silently.
When it happens
Trigger: Calling canonicalRepoPath/defaultStoragePath/resolvedRepoPath/requireDeletableStoragePath with a repo path string that embeds '\0' — typically from untrusted input, binary-corrupted config, or buffer-to-string conversions of padded bytes.
Common situations: Reading a path from an untrusted file or request parameter that was never sanitized; decoding a UTF-16/UTF-32 buffer with wrong encoding leaving NUL padding; fuzzed or malicious CLI input.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- must not contain a NUL character.
- must not be blank.
- argument contains a double quote, unsafe for the Windows…
- argument contains NUL/CR/LF, unsafe for the Windows shell
- "asyncApiSpecPath" must be a non-empty string
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/8d2611d5ad508bd9.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/storage/storage-resolver.ts:178
const isRepositoryLocalStoragePath = (repoPath: string, storagePath: string): boolean =>
samePath(comparablePath(defaultStoragePath(repoPath)), comparablePath(storagePath));
const isMissingFilesystemError = (error: unknown): boolean => {
const code = (error as NodeJS.ErrnoException)?.code;
return code === 'ENOENT' || code === 'ENOTDIR';
};
const filesystemErrorDetail = (error: unknown): string => {
const code = (error as NodeJS.ErrnoException)?.code;
return code ? `${code}: ${(error as Error)?.message ?? String(error)}` : String(error);
};
const resolveRepoPath = (value: string): string => {
if (typeof value !== 'string' || value.length === 0) {
throw new InvalidStoragePathError('Repository path must be non-empty.');
}
if (value.includes('\0')) {
throw new InvalidStoragePathError('Repository path must not contain a NUL character.');
}
return path.resolve(value);
};
const validateAbsolutePath = (value: string, label: string): string => {
if (typeof value !== 'string' || value.length === 0) {
throw new InvalidStoragePathError(`${label} must be an absolute, non-empty path.`);
}
if (value.includes('\0')) {
throw new InvalidStoragePathError(`${label} must not contain a NUL character.`);
}
if (!path.isAbsolute(value)) {
throw new InvalidStoragePathError(`${label} must be an absolute path.`);
}
return path.resolve(value);
};
// Mirror registry lookup semantics without importing repo-manager and creating a cycle.View on GitHub (pinned to ac9a4e9abd)