ruvnet/ruflo · error

browser/eval: script contains disallowed pattern

Error message

browser/eval: script contains disallowed pattern: ${pattern.source}

What it means

Thrown by the browser/eval MCP tool handler when the script matches one of the DANGEROUS_EVAL_PATTERNS regexes: process, require, __dirname, __filename, child_process, global., globalThis, Function(, .constructor, Reflect, import(, and eval(. These patterns block Node.js escape hatches and eval-equivalent tricks so the script stays inside the browser sandbox, and the error message echoes the offending pattern's source so you know which one tripped.

Solutions

  1. Identify the matching token from pattern.source in the message and rename or remove it — e.g. rename processQueue to handleQueue, drop the webpack process shim reference
  2. Replace Node APIs with in-page equivalents: read data from the DOM or fetch() instead of require/import
  3. Watch for false positives from whole-word matches and rephrase comments/strings that contain reserved words like process or Reflect
  4. Re-run the tool call after editing; the scan runs before any execution so iterating is safe

Example fix

// before
await tools.invoke('browser/eval', {
  script: 'document.title = process.env.TITLE;', // \bprocess\b blocked
});

// after
await tools.invoke('browser/eval', {
  script: 'document.title = window.__TITLE__;', // set via a prior tool call
});
Defensive patterns

Strategy: validation

Validate before calling

const DANGEROUS = [/\bprocess\b/, /\brequire\b/, /\b__dirname\b/, /\b__filename\b/, /\bchild_process\b/, /\bglobal\b\s*\./, /\bglobalThis\b/, /\bFunction\s*\(/, /\.constructor\b/, /\bReflect\b/, /\bimport\s*\(/, /\beval\s*\(/];
const offending = DANGEROUS.find(p => p.test(script));
if (offending) throw new Error(`rewrite script: matches ${offending}`);
await tools.invoke('browser/eval', { script });

Type guard

const isSandboxSafeScript = (s: string): boolean => !DANGEROUS.some(p => p.test(s));

Try / catch

try { await tools.invoke('browser/eval', { script }); } catch (e) { if (e instanceof Error && e.message.includes('disallowed pattern')) { script = sanitize(script /* rename offending identifiers */); await tools.invoke('browser/eval', { script }); } else throw e; }

Prevention

When it happens

Trigger: A script containing the literal word 'process' anywhere (even in a comment or a DOM selector), since /\bprocess\b/ matches whole-word occurrences; using require() or await import(); accessing globalThis or Function constructor; prototype-style access like x.constructor; a string containing 'eval(' such as element.dataset.eval(' or myEval(...)

Common situations: Porting Node-side scripts into browser/eval unchanged; scripts referencing window.process (left by bundlers like webpack/browserify shims); innocent identifiers such as processQueue() or requireLogin() matching the word-boundary regexes; minified code that uses .constructor chains.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/5da204444322c0e5. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/browser/src/mcp-tools/browser-tools.ts:677

        },
      },
      required: ['script'],
    },
    handler: async (input) => {
      const script = input.script as string;

      // Validate script length
      if (!script || script.length === 0) {
        throw new Error('browser/eval: script must not be empty');
      }
      if (script.length > MAX_EVAL_SCRIPT_LENGTH) {
        throw new Error(`browser/eval: script exceeds maximum length of ${MAX_EVAL_SCRIPT_LENGTH} characters`);
      }

      // Check for dangerous patterns
      for (const pattern of DANGEROUS_EVAL_PATTERNS) {
        if (pattern.test(script)) {
          throw new Error(`browser/eval: script contains disallowed pattern: ${pattern.source}`);
        }
      }

      // Audit log
      console.info(`[browser/eval] Executing script (${script.length} chars) in session ${input.session || 'default'}`);

      const adapter = getAdapter(input.session as string);
      return adapter.eval({ script });
    },
  },
];

// ============================================================================
// Storage Tools
// ============================================================================

const storageTools: MCPTool[] = [
  {

View on GitHub (pinned to fa13ee4ad6)