nocobase/nocobase · error · Error
PATH_TRAVERSAL
PATH_TRAVERSAL
Error message
Invalid storage sub path
What it means
normalizeStoragePathForJoin (in plugin-file-manager utils) validates each path component before joining it into a storage path. It throws the given pathError message ('Invalid storage sub path') when a value is not a string, contains a NUL byte, or — unless allowLeadingSlash is set — begins with a leading '/'. This prevents absolute paths and injection characters from entering storage path joins.
Source
Thrown at packages/plugins/@nocobase/plugin-file-manager/src/server/utils.ts:199
const id = extname && fileIdSegment.endsWith(extname) ? fileIdSegment.slice(0, -extname.length) : fileIdSegment;
return decodeURIComponent(segments[filesIndex + 1]) === appName && id === String(file.id);
} catch (error) {
return false;
}
}
function pathError(message: string) {
const error = new Error(message) as NodeJS.ErrnoException;
error.code = 'PATH_TRAVERSAL';
return error;
}
function normalizeStoragePathForJoin(value: unknown, message: string, { allowLeadingSlash = false } = {}) {
if (value == null || value === '') {
return '';
}
if (typeof value !== 'string' || value.includes('\0')) {
throw pathError(message);
}
const normalized = value.replace(/\\/g, '/');
if (!allowLeadingSlash && normalized.startsWith('/')) {
throw pathError(message);
}
const segments = normalized
.replace(/^\/+|\/+$/g, '')
.split('/')
.filter((segment) => segment && segment !== '.');
if (segments.some((segment) => segment === '..')) {
throw pathError('Access denied');
}
return segments.join('/');
}
export function normalizeStorageSubPath(subPath?: unknown) {
return normalizeStoragePathForJoin(subPath, 'Invalid storage sub path');
}View on GitHub (pinned to fa42722fef)
Solutions
- Remove the leading '/' from the storage sub path — it must be relative.
- Strip NUL bytes and control characters from the path before use.
- Ensure the value passed is a plain string, not an object/array from query parsing.
- Fix the storage record's `path` option in the file-manager settings.
Example fix
// before storage.path = '/uploads/reports'; // leading slash rejected // after storage.path = 'uploads/reports';
Defensive patterns
Strategy: validation
Validate before calling
function assertSubPath(v: unknown) {
if (v == null || v === '') return;
if (typeof v !== 'string' || v.includes('\0')) throw new Error('Invalid storage sub path');
const norm = v.replace(/\\/g, '/');
if (norm.startsWith('/')) throw new Error('Invalid storage sub path: must be relative');
}
assertSubPath(storage.path); Type guard
function isRelativeSubPath(v: unknown): v is string {
return typeof v === 'string' && !v.includes('\0') && !v.replace(/\\/g, '/').startsWith('/');
} Try / catch
try {
const p = normalizeStorageSubPath(raw);
} catch (e) {
if (e.code === 'PATH_TRAVERSAL') return res.status(400).json({ error: 'sub path must be a relative, NUL-free string' });
throw e;
} Prevention
- Store storage sub paths as relative segments without leading slashes.
- Sanitize imported/migrated storage records for '\0' and leading '/' before saving.
- Treat sub-path validation failures from clients as suspicious input and log them.
When it happens
Trigger: Calling normalizeStorageSubPath / normalizedStoragePath with a sub path that is a non-string, contains '\0', or starts with '/' — e.g. storage.path configured as '/uploads' or a client sending an absolute path in a sub-path field.
Common situations: Storage records imported from other systems with leading-slash paths; template strings accidentally prefixing '/'; NUL-byte injection attempts on the path parameter.
Related errors
- PATH_TRAVERSAL
- TEMPORARY_FILE_ACCESS_EXPIRES_IN must be between 5m and 10m
- Performance testing script file not found: ${f}
- Please set TARGET_ORIGIN in environment variables or in .env
- (dynamic validation message passed to throwValidationError,
AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01).
Data as JSON: /api/errors/40c3edc472340637.
Report an issue: GitHub.