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
- 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
- Replace Node APIs with in-page equivalents: read data from the DOM or fetch() instead of require/import
- Watch for false positives from whole-word matches and rephrase comments/strings that contain reserved words like process or Reflect
- 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
- Write eval scripts browser-only: no Node globals, no require/import, no constructor tricks
- Avoid identifiers containing reserved words (processQueue, requireLogin, myEval) — the regexes match whole words
- Lint scripts locally with the same pattern list before sending
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
- browser/eval: script exceeds maximum length of
- browser/eval: script must not be empty
- page-agent bundle still contains a demo/sandbox endpoint
- AI budget file is a symlink (refusing)
- AI job registry is a symlink (refusing)
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)