VYPR
Medium severity4.3NVD Advisory· Published Jun 1, 2026

CVE-2026-10291

CVE-2026-10291

Description

A security vulnerability has been detected in Enderfga claw-orchestrator up to 3.7.0. The impacted element is the function validateRegex of the file claw-orchestrator/src/embedded-server.ts of the component Session Grep Endpoint. The manipulation of the argument body.pattern leads to inefficient regular expression complexity. The attack may be initiated remotely. Upgrading to version 3.7.1 is sufficient to resolve this issue. The identifier of the patch is 3f970a974c65a94555c25af9f2796f11315e4584. It is recommended to upgrade the affected component.

Affected products

1

Patches

1
3f970a974c65

fix: use RE2 for user-supplied regex in /session/grep (v3.7.1)

https://github.com/enderfga/claw-orchestratorGuian FangMay 11, 2026via nvd-ref
6 files changed · +178 21
  • CHANGELOG.md+13 0 modified
    @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
     The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
     and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     
    +## [3.7.1] - 2026-05-11
    +
    +### Fixed
    +
    +- `/session/grep` and the `session-grep` tool now compile user-supplied patterns
    +  with [RE2](https://github.com/uhop/node-re2) instead of the V8 regex engine.
    +  RE2 evaluates regexes in linear time and never backtracks, so patterns like
    +  `(a+)+$` that previously could block the Node event loop now complete in
    +  microseconds. Closes #64. Thanks to @ybdesire for the report.
    +- Note: RE2 does not support a handful of PCRE-only features (lookbehind,
    +  backreferences). Patterns using those features will be rejected at compile
    +  time with an `Invalid regex pattern` error.
    +
     ## [3.7.0] - 2026-05-11
     
     ### Added — Model Context Protocol (MCP) server
    
  • package.json+3 2 modified
    @@ -1,6 +1,6 @@
     {
       "name": "@enderfga/claw-orchestrator",
    -  "version": "3.7.0",
    +  "version": "3.7.1",
       "description": "Claw Orchestrator — run Claude Code, Codex, Gemini, Cursor Agent, OpenCode and custom coding CLIs as one unified runtime. Drop into Hermes Agent, Claude Desktop, Cursor, Cline, Continue, Zed, Windsurf, Goose or any Model Context Protocol (MCP) host, install as an OpenClaw plugin, or run standalone. Persistent sessions, multi-agent council, ultraplan, ultrareview, autoloop, tool orchestration.",
       "type": "module",
       "main": "./dist/src/index.js",
    @@ -91,7 +91,8 @@
       "license": "MIT",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.29.0",
    -    "commander": "^12.1.0"
    +    "commander": "^12.1.0",
    +    "re2": "^1.24.0"
       },
       "peerDependencies": {
         "openclaw": ">=2026.3.0"
    
  • package-lock.json+142 11 modified
    @@ -10,7 +10,8 @@
           "license": "MIT",
           "dependencies": {
             "@modelcontextprotocol/sdk": "^1.29.0",
    -        "commander": "^12.1.0"
    +        "commander": "^12.1.0",
    +        "re2": "^1.24.0"
           },
           "bin": {
             "clawo": "dist/bin/cli.js",
    @@ -2426,7 +2427,6 @@
           "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
           "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
           "license": "ISC",
    -      "peer": true,
           "dependencies": {
             "minipass": "^7.0.4"
           },
    @@ -4931,6 +4931,15 @@
             "url": "https://opencollective.com/vitest"
           }
         },
    +    "node_modules/abbrev": {
    +      "version": "4.0.0",
    +      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz",
    +      "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==",
    +      "license": "ISC",
    +      "engines": {
    +        "node": "^20.17.0 || >=22.9.0"
    +      }
    +    },
         "node_modules/abort-controller": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
    @@ -5415,7 +5424,6 @@
           "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
           "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
           "license": "BlueOak-1.0.0",
    -      "peer": true,
           "engines": {
             "node": ">=18"
           }
    @@ -5923,6 +5931,15 @@
             "url": "https://github.com/fb55/entities?sponsor=1"
           }
         },
    +    "node_modules/env-paths": {
    +      "version": "2.2.1",
    +      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
    +      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
    +      "license": "MIT",
    +      "engines": {
    +        "node": ">=6"
    +      }
    +    },
         "node_modules/es-define-property": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
    @@ -6405,6 +6422,12 @@
             "node": ">=12.0.0"
           }
         },
    +    "node_modules/exponential-backoff": {
    +      "version": "3.1.3",
    +      "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
    +      "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
    +      "license": "Apache-2.0"
    +    },
         "node_modules/express": {
           "version": "5.2.1",
           "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
    @@ -6581,7 +6604,6 @@
           "version": "6.5.0",
           "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
           "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
    -      "dev": true,
           "license": "MIT",
           "engines": {
             "node": ">=12.0.0"
    @@ -7196,8 +7218,7 @@
           "version": "4.2.11",
           "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
           "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
    -      "license": "ISC",
    -      "peer": true
    +      "license": "ISC"
         },
         "node_modules/gtoken": {
           "version": "7.1.0",
    @@ -7521,6 +7542,19 @@
           "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
           "license": "ISC"
         },
    +    "node_modules/install-artifact-from-github": {
    +      "version": "1.6.0",
    +      "resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.6.0.tgz",
    +      "integrity": "sha512-wKsuzN8fy8QK7iEUqyWTQmvZ1QFGPn1xyl3/1iIIDthDjS7Hn9HoPwHlNakZirWbCsbad0lZMkr6Xfbpe1pUzw==",
    +      "license": "BSD-3-Clause",
    +      "bin": {
    +        "install-from-cache": "bin/install-from-cache.js",
    +        "save-to-github-cache": "bin/save-to-github-cache.js"
    +      },
    +      "engines": {
    +        "node": ">=18"
    +      }
    +    },
         "node_modules/ip-address": {
           "version": "10.1.0",
           "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
    @@ -8204,7 +8238,6 @@
           "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
           "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
           "license": "MIT",
    -      "peer": true,
           "dependencies": {
             "minipass": "^7.1.2"
           },
    @@ -8241,6 +8274,12 @@
             "thenify-all": "^1.0.0"
           }
         },
    +    "node_modules/nan": {
    +      "version": "2.26.2",
    +      "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz",
    +      "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==",
    +      "license": "MIT"
    +    },
         "node_modules/nanoid": {
           "version": "3.3.11",
           "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
    @@ -8422,6 +8461,63 @@
             "url": "https://opencollective.com/node-fetch"
           }
         },
    +    "node_modules/node-gyp": {
    +      "version": "12.3.0",
    +      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz",
    +      "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==",
    +      "license": "MIT",
    +      "dependencies": {
    +        "env-paths": "^2.2.0",
    +        "exponential-backoff": "^3.1.1",
    +        "graceful-fs": "^4.2.6",
    +        "nopt": "^9.0.0",
    +        "proc-log": "^6.0.0",
    +        "semver": "^7.3.5",
    +        "tar": "^7.5.4",
    +        "tinyglobby": "^0.2.12",
    +        "undici": "^6.25.0",
    +        "which": "^6.0.0"
    +      },
    +      "bin": {
    +        "node-gyp": "bin/node-gyp.js"
    +      },
    +      "engines": {
    +        "node": "^20.17.0 || >=22.9.0"
    +      }
    +    },
    +    "node_modules/node-gyp/node_modules/isexe": {
    +      "version": "4.0.0",
    +      "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
    +      "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
    +      "license": "BlueOak-1.0.0",
    +      "engines": {
    +        "node": ">=20"
    +      }
    +    },
    +    "node_modules/node-gyp/node_modules/undici": {
    +      "version": "6.25.0",
    +      "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
    +      "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
    +      "license": "MIT",
    +      "engines": {
    +        "node": ">=18.17"
    +      }
    +    },
    +    "node_modules/node-gyp/node_modules/which": {
    +      "version": "6.0.1",
    +      "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
    +      "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
    +      "license": "ISC",
    +      "dependencies": {
    +        "isexe": "^4.0.0"
    +      },
    +      "bin": {
    +        "node-which": "bin/which.js"
    +      },
    +      "engines": {
    +        "node": "^20.17.0 || >=22.9.0"
    +      }
    +    },
         "node_modules/node-readable-to-web-readable-stream": {
           "version": "0.4.2",
           "resolved": "https://registry.npmjs.org/node-readable-to-web-readable-stream/-/node-readable-to-web-readable-stream-0.4.2.tgz",
    @@ -8430,6 +8526,21 @@
           "optional": true,
           "peer": true
         },
    +    "node_modules/nopt": {
    +      "version": "9.0.0",
    +      "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
    +      "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==",
    +      "license": "ISC",
    +      "dependencies": {
    +        "abbrev": "^4.0.0"
    +      },
    +      "bin": {
    +        "nopt": "bin/nopt.js"
    +      },
    +      "engines": {
    +        "node": "^20.17.0 || >=22.9.0"
    +      }
    +    },
         "node_modules/nth-check": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
    @@ -8936,7 +9047,6 @@
           "version": "4.0.4",
           "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
           "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
    -      "dev": true,
           "license": "MIT",
           "engines": {
             "node": ">=12"
    @@ -9022,6 +9132,15 @@
             "url": "https://github.com/prettier/prettier?sponsor=1"
           }
         },
    +    "node_modules/proc-log": {
    +      "version": "6.1.0",
    +      "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz",
    +      "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==",
    +      "license": "ISC",
    +      "engines": {
    +        "node": "^20.17.0 || >=22.9.0"
    +      }
    +    },
         "node_modules/process-nextick-args": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
    @@ -9225,6 +9344,21 @@
             "node": ">= 0.10"
           }
         },
    +    "node_modules/re2": {
    +      "version": "1.24.0",
    +      "resolved": "https://registry.npmjs.org/re2/-/re2-1.24.0.tgz",
    +      "integrity": "sha512-pBm6cMaOb0Yb0kg0Sfw/k4LwDMkPScb/NVd7GrEllDwfsPZstsZIo93A6Nn0wZuWJw3h57GdzkrOk81EofKY/g==",
    +      "hasInstallScript": true,
    +      "license": "BSD-3-Clause",
    +      "dependencies": {
    +        "install-artifact-from-github": "^1.4.0",
    +        "nan": "^2.26.2",
    +        "node-gyp": "^12.2.0"
    +      },
    +      "funding": {
    +        "url": "https://github.com/sponsors/uhop"
    +      }
    +    },
         "node_modules/readable-stream": {
           "version": "2.3.8",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
    @@ -10048,7 +10182,6 @@
           "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz",
           "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==",
           "license": "BlueOak-1.0.0",
    -      "peer": true,
           "dependencies": {
             "@isaacs/fs-minipass": "^4.0.0",
             "chownr": "^3.0.0",
    @@ -10241,7 +10374,6 @@
           "version": "0.2.15",
           "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
           "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
    -      "dev": true,
           "license": "MIT",
           "dependencies": {
             "fdir": "^6.5.0",
    @@ -10910,7 +11042,6 @@
           "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
           "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
           "license": "BlueOak-1.0.0",
    -      "peer": true,
           "engines": {
             "node": ">=18"
           }
    
  • src/session-manager.ts+2 1 modified
    @@ -14,6 +14,7 @@ import { promisify } from 'node:util';
     const execFileAsync = promisify(execFile);
     import * as http from 'node:http';
     import { createRequire } from 'node:module';
    +import RE2 from 're2';
     
     const _require = createRequire(import.meta.url);
     function getPluginVersion(): string {
    @@ -469,7 +470,7 @@ export class SessionManager {
       ): Promise<Array<{ time: string; type: string; content: string }>> {
         const managed = this._getSession(name);
         const history = managed.session.getHistory(GREP_HISTORY_FETCH);
    -    const regex = new RegExp(pattern, 'i');
    +    const regex = new RE2(pattern, 'i');
         return history
           .filter((ev) => regex.test(JSON.stringify(ev)))
           .slice(0, limit)
    
  • src/__tests__/validation.test.ts+12 2 modified
    @@ -106,9 +106,9 @@ describe('sanitizeCwd', () => {
     // ─── validateRegex ──────────────────────────────────────────────────────────
     
     describe('validateRegex', () => {
    -  it('returns RegExp for valid pattern', () => {
    +  it('returns a regex-like object for valid pattern', () => {
         const result = validateRegex('hello');
    -    expect(result).toBeInstanceOf(RegExp);
    +    expect(typeof result.test).toBe('function');
         expect(result.test('HELLO')).toBe(true); // case insensitive
       });
     
    @@ -129,6 +129,16 @@ describe('validateRegex', () => {
       it('throws for unbalanced parentheses', () => {
         expect(() => validateRegex('(abc')).toThrow('Invalid regex pattern');
       });
    +
    +  it('runs catastrophic-backtracking patterns in linear time (no ReDoS)', () => {
    +    // (a+)+$ against 'a'*30 + '!' would hang a backtracking engine for seconds.
    +    // RE2 evaluates this in microseconds.
    +    const regex = validateRegex('(a+)+$');
    +    const input = 'a'.repeat(30) + '!';
    +    const start = Date.now();
    +    expect(regex.test(input)).toBe(false);
    +    expect(Date.now() - start).toBeLessThan(100);
    +  });
     });
     
     // ─── validateName ───────────────────────────────────────────────────────────
    
  • src/validation.ts+6 5 modified
    @@ -9,6 +9,7 @@
     import * as path from 'node:path';
     import * as fs from 'node:fs';
     import * as os from 'node:os';
    +import RE2 from 're2';
     
     // ─── Blocked Path Prefixes ─────────────────────────────────────────────────
     
    @@ -85,15 +86,15 @@ export function sanitizeCwd(cwd: string | undefined): string | undefined {
     // ─── validateRegex ──────────────────────────────────────────────────────────
     
     /**
    - * Validate that a string is a syntactically valid regular expression.
    + * Compile a user-supplied pattern into an RE2 regex.
      *
    - * Returns the compiled RegExp if valid, throws on invalid syntax.
    - * Note: this validates syntax only — it does not detect catastrophic
    - * backtracking (ReDoS) patterns.
    + * RE2 runs in linear time and never backtracks, so it is immune to ReDoS
    + * patterns like `(a+)+$`. Some PCRE features (lookbehind, backreferences)
    + * are not supported and will throw at compile time.
      */
     export function validateRegex(pattern: string): RegExp {
       try {
    -    return new RegExp(pattern, 'i');
    +    return new RE2(pattern, 'i');
       } catch (err) {
         throw new Error(`Invalid regex pattern: ${(err as Error).message}`);
       }
    

Vulnerability mechanics

Root cause

"The `validateRegex` function does not adequately protect against inefficient regular expression complexity."

Attack vector

An attacker can remotely initiate an attack by sending a POST request to the `/session/grep` endpoint. This request must include a JSON payload with a `pattern` field containing a malicious regular expression designed to cause catastrophic backtracking. The vulnerability is present in versions up to 3.7.0 [ref_id=2].

Affected code

The vulnerability resides within the `validateRegex` function located in `claw-orchestrator/src/embedded-server.ts` and `src/validation.ts`. Specifically, the code at `src/embedded-server.ts#L301C6-L310C8` and `src/validation.ts#L87C0-L100C0` in version 3.7.0 is impacted [ref_id=2].

What the fix does

The patch, identified by `3f970a974c65a94555c25af9f2796f11315e4584`, resolves the vulnerability by enhancing the validation of regular expression patterns. This prevents the server from entering a state of catastrophic backtracking, thereby mitigating the denial-of-service risk. The advisory recommends upgrading to version 3.7.1 to address this issue [ref_id=2].

Preconditions

  • inputThe request payload must contain a `body.pattern` argument with an inefficient regular expression.
  • networkThe attack can be initiated remotely.
  • authThe vulnerability requires low privileges (PR:L).

Reproduction

```bash curl -X POST http://127.0.0.1:18796/session/grep \ -H 'Content-Type: application/json' \ -d '{ "name": "existing-session", "pattern": "(a+)+$" }' ``` [ref_id=2]

Generated on Jun 1, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.