{"record":{"id":"5da204444322c0e5","repo":"ruvnet/ruflo","slug":"browser-eval-script-contains-disallowed-pattern","errorCode":null,"errorMessage":"browser/eval: script contains disallowed pattern: ${pattern.source}","messagePattern":"browser/eval: script contains disallowed pattern: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/browser/src/mcp-tools/browser-tools.ts","lineNumber":677,"sourceCode":"        },\n      },\n      required: ['script'],\n    },\n    handler: async (input) => {\n      const script = input.script as string;\n\n      // Validate script length\n      if (!script || script.length === 0) {\n        throw new Error('browser/eval: script must not be empty');\n      }\n      if (script.length > MAX_EVAL_SCRIPT_LENGTH) {\n        throw new Error(`browser/eval: script exceeds maximum length of ${MAX_EVAL_SCRIPT_LENGTH} characters`);\n      }\n\n      // Check for dangerous patterns\n      for (const pattern of DANGEROUS_EVAL_PATTERNS) {\n        if (pattern.test(script)) {\n          throw new Error(`browser/eval: script contains disallowed pattern: ${pattern.source}`);\n        }\n      }\n\n      // Audit log\n      console.info(`[browser/eval] Executing script (${script.length} chars) in session ${input.session || 'default'}`);\n\n      const adapter = getAdapter(input.session as string);\n      return adapter.eval({ script });\n    },\n  },\n];\n\n// ============================================================================\n// Storage Tools\n// ============================================================================\n\nconst storageTools: MCPTool[] = [\n  {","sourceCodeStart":659,"sourceCodeEnd":695,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/browser/src/mcp-tools/browser-tools.ts#L659-L695","documentation":"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.","triggerScenarios":"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(...)","commonSituations":"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.","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"],"exampleFix":"// before\nawait tools.invoke('browser/eval', {\n  script: 'document.title = process.env.TITLE;', // \\bprocess\\b blocked\n});\n\n// after\nawait tools.invoke('browser/eval', {\n  script: 'document.title = window.__TITLE__;', // set via a prior tool call\n});","handlingStrategy":"validation","validationCode":"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*\\(/];\nconst offending = DANGEROUS.find(p => p.test(script));\nif (offending) throw new Error(`rewrite script: matches ${offending}`);\nawait tools.invoke('browser/eval', { script });","typeGuard":"const isSandboxSafeScript = (s: string): boolean => !DANGEROUS.some(p => p.test(s));","tryCatchPattern":"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; }","preventionTips":["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"],"tags":["browser","eval","security","static-analysis","mcp-tools"],"backgroundTag":"security-policy-blocked","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}