abhigyanpatwari/GitNexus · error · BadRequestError
Upload path must not contain traversal segments
Error message
Upload path must not contain traversal segments
What it means
After splitting and NFC-normalizing each segment, resolveContainedDest rejects any segment equal to '.' or '..' with 'Upload path must not contain traversal segments'. This is the direct anti-traversal rule: even though a later resolve-then-contain check would also catch escapes, '.'/'..' segments are unambiguous traversal syntax and are refused explicitly, including post-normalization forms.
Source
Thrown at gitnexus/src/server/upload-ingest.ts:91
// webkitRelativePath is always relative; a leading slash is absolute/hostile.
if (rel.startsWith('/')) {
throw new BadRequestError('Invalid upload path');
}
// Browsers emit forward slashes only; a NUL byte or backslash is hostile.
if (rel.includes('\u0000') || rel.includes('\\')) {
throw new BadRequestError('Invalid upload path');
}
const rawSegments = rel.split('/').filter((s) => s.length > 0);
if (rawSegments.length === 0 || rawSegments.length > MAX_PATH_DEPTH) {
throw new BadRequestError('Invalid upload path');
}
const segments: string[] = [];
for (const seg of rawSegments) {
// Normalize so NFC/NFD variants don't collide silently on case/unicode
// -folding filesystems (macOS/Windows).
const s = seg.normalize('NFC');
if (s === '.' || s === '..') {
throw new BadRequestError('Upload path must not contain traversal segments');
}
segments.push(s);
}
const dest = path.resolve(stageRoot, segments.join(path.sep));
// Suffix path.sep so a sibling prefix (/sandbox-evil vs /sandbox) can't pass.
const safePrefix = stageRoot.endsWith(path.sep) ? stageRoot : stageRoot + path.sep;
if (dest !== stageRoot && !dest.startsWith(safePrefix)) {
throw new BadRequestError('Upload path escapes the sandbox');
}
return dest;
}
interface DirState {
dirCount: number;
limits: IngestLimits;
}
/**View on GitHub (pinned to aac7515d2a)
Solutions
- Send only paths as the browser reports them — webkitRelativePath never contains '..' because the picker roots at the chosen folder
- Strip traversal client-side if you compute paths: segments.filter(s => s && s !== '.' && s !== '..').join('/')
- In security tests, keep '../..' and 'a/./b' cases asserting HTTP 400 'Upload path must not contain traversal segments'
Example fix
// before — client computes paths relative to CWD entry.path = path.relative(projectRoot, file.path); // may yield '../../downloads/x' // after entry.path = file.webkitRelativePath || file.name; // rooted at the picked folder
Defensive patterns
Strategy: type-guard
Validate before calling
// Strip traversal segments before sending
entry.path = entry.path
.split('/')
.filter((s) => s && s !== '.' && s !== '..')
.join('/'); Type guard
function isTraversalFreeUploadPath(rel: string): boolean {
return rel
.split('/')
.every((s) => s.normalize('NFC') !== '.' && s.normalize('NFC') !== '..');
} Prevention
- Use webkitRelativePath verbatim — the folder picker roots paths at the chosen folder so '..' cannot appear
- Never compute manifest paths with path.relative against locations outside the picked folder
- Keep '../..' payloads in your security test corpus asserting HTTP 400
When it happens
Trigger: POST multipart ingest with manifest paths like '../../etc/cron.d/x', 'src/../../../secrets', or 'a/./b' — any entry that yields a '.' or '..' segment after splitting on '/'. NFC normalization runs first so unicode tricks cannot smuggle a dot-dot equivalent.
Common situations: Classic path-traversal attack on the upload endpoint; a client that preserves '../' prefixes from paths outside the picked folder; test suites asserting OWASP File Upload coverage; accidentally built manifests that include parent references from relative-path math.
Related errors
- Invalid upload path
- Upload path too long
- Upload path escapes the sandbox
- Uploaded folder has no usable name
- Upload must be a folder
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/9a1ef3079470ab34.
Report an issue: GitHub.