Last active
December 8, 2025 23:21
-
-
Save matrosov/8472b1672a48554223e7448c02c09092 to your computer and use it in GitHub Desktop.
Static analysis rules for detecting React2Shell(CVE-2025-55182) exposure in JS/TS codebases.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| rules: | |
| # ======================================================================== | |
| # Detection of CVE-2025-55182 (React2Shell) exposure and exploitability | |
| # | |
| # TIER 1: Inventory-level (vulnerable deps or at-risk Next.js) | |
| # TIER 2: Exposure-level (server imports of RSC libraries) | |
| # TIER 3: Exploit chain (taint from HTTP → decodeReply/decodeAction/etc.) | |
| # ======================================================================== | |
| # ------------------------------------------------------------------------ | |
| # TIER 1: Scope vulnerable RSC deps (package.json) | |
| # Fast inventory check drives bulk patching | |
| # ------------------------------------------------------------------------ | |
| - id: react2shell-vulnerable-rsc-dependency | |
| languages: [json] | |
| message: >- | |
| VULNERABLE REACT SERVER COMPONENTS: This project depends on a | |
| React Server Components package version affected by CVE-2025-55182 | |
| (React2Shell). Upgrade react-server-dom-* to 19.0.1 / 19.1.2 / 19.2.1 | |
| or later. | |
| severity: ERROR | |
| paths: | |
| include: | |
| - "package.json" | |
| # Match react-server-dom-{webpack,parcel,turbopack} with vulnerable | |
| # versions (19.0.0, 19.1.0, 19.1.1, 19.2.0). Adjust as needed. | |
| pattern-regex: > | |
| "react-server-dom-(webpack|parcel|turbopack)"\s*:\s*"[~^]?(19\.0\.0|19\.1\.0|19\.1\.1|19\.2\.0)" | |
| metadata: | |
| category: security | |
| cwe: "CWE-502: Deserialization of Untrusted Data" | |
| owasp: "A08:2021 - Software and Data Integrity Failures" | |
| signal: inventory # inventory | exposure | exploit_chain | |
| likelihood: high | |
| impact: high | |
| technology: [react, rsc] | |
| product_area: web_frontend | |
| ci_blocker: false # do not fail CI on inventory alone | |
| # ------------------------------------------------------------------------ | |
| # TIER 1: Next.js Major Versions at Risk | |
| # Identify projects using Next.js major versions known to embed RSC | |
| # in ways affected by React2Shell (15.x/16.x). | |
| # ------------------------------------------------------------------------ | |
| - id: react2shell-nextjs-major-version-at-risk | |
| languages: [json] | |
| message: >- | |
| NEXT.JS 15.x / 16.x IN USE: This project uses Next.js in a major | |
| version line affected by CVE-2025-55182 (React2Shell) in the App Router. | |
| Verify whether the specific version is a patched release. | |
| severity: WARNING | |
| paths: | |
| include: | |
| - "package.json" | |
| # Any 15.x.y or 16.x.y — this is a coarse filter for "at-risk". | |
| pattern-regex: > | |
| "next"\s*:\s*"[~^]?(15\.[0-9]+\.[0-9]+|16\.[0-9]+\.[0-9]+)" | |
| metadata: | |
| category: security | |
| cwe: "CWE-502: Deserialization of Untrusted Data" | |
| signal: inventory | |
| likelihood: medium | |
| impact: high | |
| technology: [react, nextjs, rsc] | |
| product_area: web_frontend | |
| ci_blocker: false | |
| # ------------------------------------------------------------------------ | |
| # TIER 2: Smart Import Detection | |
| # Detect server-side imports of React Server Components libraries: | |
| # react-server-dom-{webpack,parcel,turbopack}/server*. | |
| # Find custom RSC integrations. | |
| # ------------------------------------------------------------------------ | |
| - id: react2shell-import-detection | |
| languages: [javascript, typescript] | |
| message: >- | |
| RSC SERVER IMPORT: Detected import of a React Server Components | |
| server library (react-server-dom-* /server*). This indicates potential | |
| exposure to CVE-2025-55182. Verify that this module is not processing | |
| untrusted input, and confirm you are using patched versions. | |
| severity: WARNING | |
| # Avoid noise from compiled artifacts, node_modules, and tests. | |
| paths: | |
| exclude: | |
| - "node_modules/**" | |
| - "dist/**" | |
| - "build/**" | |
| - "out/**" | |
| - "coverage/**" | |
| - "test/**" | |
| - "tests/**" | |
| - "**/__tests__/**" | |
| - "**/__mocks__/**" | |
| patterns: | |
| - pattern-either: | |
| # ESM imports (named, default, or namespace) | |
| - pattern: import ... from $LIB | |
| # CommonJS require | |
| - pattern: const ... = require($LIB) | |
| # Dynamic import() | |
| - pattern: const ... = await import($LIB) | |
| metavariable-regex: | |
| metavariable: $LIB | |
| # Match webpack / parcel / turbopack RSC server entrypoints: | |
| # 'react-server-dom-webpack/server', '.../server.edge', etc. | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| metadata: | |
| category: security | |
| cwe: "CWE-502: Deserialization of Untrusted Data" | |
| signal: exposure | |
| likelihood: high | |
| impact: medium | |
| technology: [react, rsc, nextjs] | |
| product_area: web_frontend | |
| ci_blocker: false # usually not a CI blocker by itself | |
| # ------------------------------------------------------------------------ | |
| # TIER 3: Deep Reachability Analysis | |
| # Confirm an actual exploit chain: untrusted HTTP data flows into | |
| # RSC deserialization primitives: decodeReply/decodeAction/requireModule | |
| # from react-server-dom-*. | |
| # ------------------------------------------------------------------------ | |
| - id: react2shell-reachability-proof | |
| mode: taint | |
| languages: [javascript, typescript] | |
| message: >- | |
| CRITICAL EXPLOIT CHAIN: Untrusted network data is flowing into | |
| React Server Components deserialization (decodeReply/decodeAction/ | |
| requireModule) from react-server-dom-*. This is a CONFIRMED RCE | |
| vector for CVE-2025-55182 (React2Shell). | |
| Short term: Add strict validation, schema checks, or | |
| signature/allowlist enforcement before these calls. | |
| Roll out patched React/Next.js RSC runtimes across environments. | |
| Long term: Centralize all RSC decoding behind a shared, vetted helper | |
| and forbid raw decodeReply/decodeAction/requireModule usage. | |
| severity: ERROR | |
| # Same path filters as import rule: focus on real application code. | |
| paths: | |
| exclude: | |
| - "node_modules/**" | |
| - "dist/**" | |
| - "build/**" | |
| - "out/**" | |
| - "coverage/**" | |
| - "test/**" | |
| - "tests/**" | |
| - "**/__tests__/**" | |
| - "**/__mocks__/**" | |
| # -------------------------------------------------------------------- | |
| # SOURCES: Common HTTP / server entrypoints for untrusted input | |
| # -------------------------------------------------------------------- | |
| pattern-sources: | |
| # Express / Node-style | |
| - pattern: req.body | |
| - pattern: req.rawBody | |
| - pattern: req.query | |
| - pattern: req.params | |
| - pattern: req.headers | |
| # Koa / generic context | |
| - pattern: ctx.request.body | |
| - pattern: ctx.query | |
| - pattern: ctx.params | |
| # Next.js / Fetch-style Request API | |
| - pattern: request.body | |
| - pattern: request.json() | |
| - pattern: await request.json() | |
| - pattern: request.text() | |
| - pattern: await request.text() | |
| - pattern: request.formData() | |
| - pattern: await request.formData() | |
| - pattern: request.nextUrl.searchParams.get(...) | |
| # Generic URLSearchParams | |
| - pattern: searchParams.get(...) | |
| # Lambda / serverless payloads | |
| - pattern: event.body | |
| # FormData helpers used directly | |
| - pattern: req.formData() | |
| - pattern: await req.formData() | |
| - pattern: formData.get(...) | |
| - pattern: formData.getAll(...) | |
| # -------------------------------------------------------------------- | |
| # SINKS: decodeReply/decodeAction/requireModule from | |
| # react-server-dom-{webpack,parcel,turbopack}/server* | |
| # | |
| # Coverage: | |
| # - ESM named imports (direct + aliased) | |
| # - ESM namespace imports (import * as RSC ...) | |
| # - CommonJS require (destructured + namespace) | |
| # -------------------------------------------------------------------- | |
| pattern-sinks: | |
| # ESM: aliased decodeReply | |
| - patterns: | |
| - pattern: $DECODE(...) | |
| - pattern-inside: | | |
| import { ..., decodeReply as $DECODE, ... } from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # ESM: aliased decodeAction | |
| - patterns: | |
| - pattern: $DECODE(...) | |
| - pattern-inside: | | |
| import { ..., decodeAction as $DECODE, ... } from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # ESM: direct named decodeReply | |
| - patterns: | |
| - pattern: decodeReply(...) | |
| - pattern-inside: | | |
| import { ..., decodeReply, ... } from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # ESM: direct named decodeAction | |
| - patterns: | |
| - pattern: decodeAction(...) | |
| - pattern-inside: | | |
| import { ..., decodeAction, ... } from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # ESM: namespace imports (import * as RSC from '...') | |
| - patterns: | |
| - pattern: $RSC.decodeReply(...) | |
| - pattern-inside: | | |
| import * as $RSC from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| - patterns: | |
| - pattern: $RSC.decodeAction(...) | |
| - pattern-inside: | | |
| import * as $RSC from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # CommonJS: destructured require with alias | |
| - patterns: | |
| - pattern: $DECODE(...) | |
| - pattern-inside: | | |
| const { decodeReply: $DECODE, ... } = require($LIB) | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| - patterns: | |
| - pattern: $DECODE(...) | |
| - pattern-inside: | | |
| const { decodeAction: $DECODE, ... } = require($LIB) | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # CommonJS: namespace require | |
| - patterns: | |
| - pattern: $RSC.decodeReply(...) | |
| - pattern-inside: | | |
| const $RSC = require($LIB) | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| - patterns: | |
| - pattern: $RSC.decodeAction(...) | |
| - pattern-inside: | | |
| const $RSC = require($LIB) | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| # Optional: requireModule as an additional sink (seen in some PoCs) | |
| - patterns: | |
| - pattern: $REQMOD(...) | |
| - pattern-inside: | | |
| import { ..., requireModule as $REQMOD, ... } from $LIB | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| - patterns: | |
| - pattern: $RSC.requireModule(...) | |
| - pattern-inside: | | |
| const $RSC = require($LIB) | |
| ... | |
| metavariable-regex: | |
| metavariable: $LIB | |
| regex: '^[''"]react-server-dom-(webpack|parcel|turbopack)(/server[^''"]*)?[''"]$' | |
| metadata: | |
| category: security | |
| cwe: "CWE-502: Deserialization of Untrusted Data" | |
| owasp: "A08:2021 - Software and Data Integrity Failures" | |
| signal: exploit_chain | |
| likelihood: high | |
| impact: high | |
| technology: [react, rsc, nextjs] | |
| product_area: web_frontend | |
| ci_blocker: true # safe to fail CI when this rule fires |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment