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

  1. Send only the final path component: strip directories before calling the store (path.basename on the caller side)
  2. Rewrite legacy/stored file names that contain separators to their basename before migration or access
  3. Never relax this guard - it is the server's path-traversal defense for the FileSystem store
  4. 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

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


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/625e8eb69661d352. Report an issue: GitHub.