RocketChat/Rocket.Chat · error · Error
error-invalid-file-name
error-invalid-file-name
Error message
error-invalid-file-name
What it means
sanitizeFileName (apps/meteor/server/lib/media/file/functions/sanitizeFileName.ts:5-8) is a path-traversal guard used by the FileSystem store's createWriteStream/createReadStream/stat/unlink (file.server.ts). It rejects any name where path.basename(fileName) !== fileName, i.e. a name containing path separators that could escape the store directory ('../etc/passwd', 'sounds/alert.mp3', '/etc/passwd'). It throws a plain Error, not a Meteor.Error.
Source
Thrown at apps/meteor/server/lib/media/file/functions/sanitizeFileName.ts:7
import path from 'node:path';
export function sanitizeFileName(fileName: string) {
const base = path.basename(fileName);
if (base !== fileName) {
throw new Error('error-invalid-file-name');
}
if (base === '.' || base.startsWith('..')) {
throw new Error('error-invalid-file-name');
}
if (!/^[a-zA-Z0-9._-]+$/.test(base)) {
throw new Error('error-invalid-characters-in-file-name');
}
return base;
}
View on GitHub (pinned to b2c16d5842)
Solutions
- Send only the final path component: strip directories before calling the store (path.basename on the caller side)
- Rewrite legacy/stored file names that contain separators to their basename before migration or access
- Never relax this guard - it is the server's path-traversal defense for the FileSystem store
- Wrap store calls in try/catch and surface a validation error to the client instead of crashing
Example fix
// before
const store = FileUpload.getStore('FileSystem');
const stream = store.createWriteStream({ name: userSuppliedName, ... }); // '../evil' -> error-invalid-file-name
// after
const safeName = path.basename(userSuppliedName).replace(/[^a-zA-Z0-9._-]/g, '_');
const stream = store.createWriteStream({ name: safeName, ... }); Defensive patterns
Strategy: validation
Validate before calling
const isFlatName = (name: string): boolean => path.basename(name) === name;
if (!isFlatName(name)) throw new Error('File name must not contain path separators'); Type guard
const isSafeStoreFileName = (name: string): name is string =>
path.basename(name) === name &&
name !== '.' &&
!name.startsWith('..') &&
/^[a-zA-Z0-9._-]+$/.test(name); Try / catch
try {
const ws = store.createWriteStream({ name, ... });
} catch (error: any) {
if (/error-invalid-file-name|error-invalid-characters-in-file-name/.test(error.message)) {
useSanitizedBasename(name); // fall back to basename with cleaned characters
return;
}
throw error;
} Prevention
- Always send path.basename(fileName) from clients
- Never build names from external paths verbatim
- Keep stored names within [a-zA-Z0-9._-]
When it happens
Trigger: Storing or retrieving a FileSystem-store file whose name contains '/' (directory components), e.g. a client or integration passing a full relative path as the file name; traversal attempts like '../../etc/shadow' in the file name field.
Common situations: Custom integrations uploading with unsanitized names from external systems (paths included); clients on other OSes sending names like 'C:\\uploads\\x.png' (backslash survives this check but fails the later regex); migrations importing legacy upload records whose names embed slashes; security scanners probing upload endpoints.
Related errors
- error-invalid-characters-in-file-name
- error-invalid-data
- error-not-allowed
- error-invalid-file
- error-invalid-user
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/625e8eb69661d352.
Report an issue: GitHub.