mastra-ai/mastra · error

Path traversal detected: file "${filename}" escapes storage

Error message

Path traversal detected: file "${filename}" escapes storage directory

What it means

domainFileExists() resolves the given filename against the storage root and throws if the resolved path escapes that root. It prevents probing for files outside the storage directory via traversal sequences.

Source

Thrown at packages/core/src/storage/filesystem-db.ts:126

    }
    if (!existsSync(baseDir)) return [];
    if (!statSync(baseDir).isDirectory()) {
      throw new Error(`Configured domain path "${directory}" is a file, expected a directory`);
    }

    return readdirSync(baseDir)
      .filter(file => extname(file) === extension && statSync(join(baseDir, file)).isFile())
      .map(file => `${directory}/${file}`);
  }

  /**
   * Check whether a domain file currently exists on disk.
   */
  domainFileExists(filename: string): boolean {
    const filePath = resolve(this.dir, filename);
    const rootDir = resolve(this.dir);
    if (!filePath.startsWith(rootDir + sep) && filePath !== rootDir) {
      throw new Error(`Path traversal detected: file "${filename}" escapes storage directory`);
    }
    return existsSync(filePath);
  }

  removeDomainFile(filename: string): void {
    this.cache.delete(filename);
    const filePath = resolve(this.dir, filename);
    const rootDir = resolve(this.dir);
    if (!filePath.startsWith(rootDir + sep) && filePath !== rootDir) {
      throw new Error(`Path traversal detected: file "${filename}" escapes storage directory`);
    }
    if (existsSync(filePath)) {
      rmSync(filePath);
    }
  }

  /**
   * Invalidate the in-memory cache for a domain, forcing a re-read from disk on next access.

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Sanitize the filename before calling (strip path separators and `..`; derive from a allowlisted ID).
  2. Use library APIs that generate filenames internally (e.g. per-entity helpers) rather than hand-built strings.
  3. Validate IDs used in filenames against a strict pattern before composing paths.
  4. Log the offending filename to identify which caller is passing unsafe values.

Example fix

// before
const exists = db.domainFileExists(`../../${id}.json`); // throws

// after
const safeName = `${String(id).replace(/[^a-zA-Z0-9-_]/g, '')}.json`;
const exists = db.domainFileExists(safeName);
Defensive patterns

Strategy: validation

Validate before calling

const path = require('path');
function isSafeFileName(name) {
  if (typeof name !== 'string' || name.length === 0) return false;
  if (path.isAbsolute(name)) return false;
  const norm = path.normalize(name);
  return !norm.split(path.sep).includes('..') && !norm.includes('/') && !norm.includes('\\');
}

Try / catch

try {
  return db.domainFileExists(name);
} catch (e) {
  if (e.message.startsWith('Path traversal detected')) {
    return false; // treat unsafe names as nonexistent, and log
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling domainFileExists (directly or via sharedFileExists) with a filename containing `..`, an absolute path, or otherwise resolving outside `this.dir`.

Common situations: Passing untrusted/user-supplied filenames or keys into existence checks; building filenames by concatenating external IDs without sanitization.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/5fd4b0f36043801c. Report an issue: GitHub.