usebruno/bruno · error · Error

Access to files outside of the collectionPath is not allowed

Error message

Access to files outside of the collectionPath is not allowed.

What it means

Thrown by the QuickJS local-module loader shim when the resolved path of a required local file escapes the collectionPath. The check uses path.relative: if the relative path starts with '..' or is absolute, the require is rejected. This is a security guard preventing sandboxed scripts from reading arbitrary host files.

Source

Thrown at packages/bruno-js/src/sandbox/quickjs/shims/local-module.js:19

const path = require('path');
const fs = require('fs');
const { marshallToVm } = require('../utils');

const addLocalModuleLoaderShimToContext = (vm, collectionPath) => {
  let loadLocalModuleHandle = vm.newFunction('loadLocalModule', function (module) {
    const filename = vm.dump(module);

    // Check if the filename has an extension
    const hasExtension = path.extname(filename) !== '';
    const resolvedFilename = hasExtension ? filename : `${filename}.js`;

    // Resolve the file path and check if it's within the collectionPath
    const filePath = path.resolve(collectionPath, resolvedFilename);
    const relativePath = path.relative(collectionPath, filePath);

    // Ensure the resolved file path is inside the collectionPath
    if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
      throw new Error('Access to files outside of the collectionPath is not allowed.');
    }

    if (!fs.existsSync(filePath)) {
      throw new Error(`Cannot find module ${filename}`);
    }

    let code = fs.readFileSync(filePath).toString();

    return marshallToVm(code, vm);
  });

  vm.setProp(vm.global, '__brunoLoadLocalModule', loadLocalModuleHandle);
  loadLocalModuleHandle.dispose();
};

module.exports = addLocalModuleLoaderShimToContext;

View on GitHub (pinned to 9bdd81c7bd)

Solutions

  1. Move the needed file inside the current collection directory and require it with a relative path that stays under collectionPath.
  2. If sharing across collections, copy or symlink the helper into the collection.
  3. Never interpolate untrusted values into require() paths.

Example fix

// before
const helper = require('../../shared/helper'); // escapes collection

// after — copy helper into the collection
const helper = require('./shared/helper');
Defensive patterns

Strategy: validation

Validate before calling

const path = require('path');
function isInsideCollection(target, root) {
  const rel = path.relative(root, path.resolve(root, target));
  return !rel.startsWith('..') && !path.isAbsolute(rel);
}
if (!isInsideCollection(reqPath, bru.cwd())) throw new Error('path outside collection');

Type guard

const isSafeRelativePath = (p, root) => { const rel = path.relative(root, path.resolve(root, p)); return !rel.startsWith('..') && !path.isAbsolute(rel); };

Try / catch

try { const m = require(maybeUnsafePath); }
catch (err) {
  if (/outside of the collectionPath/.test(err.message)) {
    // use a copy of the file inside the collection instead
  } else throw err;
}

Prevention

When it happens

Trigger: require('../../../etc/passwd'), require('/etc/secrets'), or require('../../other-collection/file') inside a QuickJS-sandboxed Bru script where the resolved absolute path is not within the collection directory tree.

Common situations: A shared helper legitimately lives in a sibling collection and the developer tries to reach it via ../; an imported snippet contains a path-traversal string; a variable interpolated into require() yields an unexpected absolute path.

Related errors


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/85328d02b4e8162b. Report an issue: GitHub.