Introduction
The security community was recently shaken by React2Shell, a critical unauthenticated Remote Code Execution (RCE) vulnerability. It was initially tracked under two CVEs:
CVE-2025-55182– ReactCVE-2025-66478– Next.js
With a CVSS score of 10.0, this vulnerability impacts Next.js 15/16 (App Router) and any framework relying on React 19’s RSC implementation.
Wazuh published a great blog explaining how to detect this vulnerability:
👉 https://wazuh.com/blog/detecting-next-js-cve-2025-66478-rce-vulnerability-with-wazuh/
However, after doing deeper research, I found a critical limitation that applies not only to this CVE, but to most modern application stacks.
The Detection Gap: Why SIEMs Are Blind to Local Projects
Standard vulnerability detectors (including Wazuh’s default modules) excel at finding packages installed via system managers like apt or npm install -g. But modern development happens elsewhere:
Node.js: Most projects install dependencies locally in the project folder.
Python: Virtual environments (venv, uv) isolate packages from the system.
Docker: Packages are buried inside container layers (/var/lib/docker/...).
If your SIEM isn't looking at these local paths, you are flying blind.
So if a vulnerable version of Next.js or React exists inside a project directory, Wazuh will miss it.
I explained this problem earlier here:
👉 https://dev.to/0xdolan/why-you-shouldnt-rely-solely-on-detectors-373g
Wazuh provides several features that help, but none fully solve this problem.
IT Hygiene / Vulnerability Detection
👉 https://documentation.wazuh.com/current/getting-started/use-cases/it-hygiene.html#it-hygiene
👉 https://documentation.wazuh.com/current/user-manual/capabilities/vulnerability-detection/index.html
- Detects globally installed packages only
Misses:
- Local Node.js projects
- Python venvs
- Docker containers
File Integrity Monitoring (FIM)
👉 https://documentation.wazuh.com/current/user-manual/capabilities/file-integrity/index.html
- Detects file changes
Does NOT extract:
- Package names
- Versions
- Vulnerability status
Command Monitoring
- Can run scripts from the Wazuh manager
- High risk
- If manager is compromised → full agent compromise
- Not recommended for frequent scans
So I needed a safer and more accurate approach.
The Solution: A Custom Inventory Workflow
We can’t rely on static scanners. Instead, we need a proactive script that:
- Crawls the filesystem for
package.jsonfiles. - Extracts version data for specific high-risk libraries (
React/Next.js). - Logs these to a
JSONfile. - Feeds that file into
Wazuhfor real-time alerting.
Step 1: The Inventory Script
We use a Python script designed to be lightweight. Crucially, it does not exclude /var/lib/docker, allowing us to find vulnerable libraries inside container images.
Save as
/usr/local/bin/scan_react2shell.py
#!/usr/bin/env python3
import json
import os
import sys
import time
# --- CONFIGURATION ---
LOG_FILE = "/var/log/react2shell_scan.json"
SEARCH_PATHS = ["/"]
# Excludes (Docker paths are NOT excluded so we can scan them)
EXCLUDE_DIRS = {"/proc", "/sys", "/dev", "/run", "/tmp", "/snap", "/boot"}
def scan_system():
print(f"[*] Starting Inventory Scan on: {SEARCH_PATHS}")
# Initialize Log File
if not os.path.exists(LOG_FILE):
try:
with open(LOG_FILE, "w") as f:
pass
print(f"[*] Created new log file at {LOG_FILE}")
except IOError as e:
print(f"[!] Error creating log file: {e}")
return
count_inspected = 0
for root_dir in SEARCH_PATHS:
for dirpath, dirnames, filenames in os.walk(root_dir, followlinks=True):
# Skip excluded directories
if any(exclude in dirpath for exclude in EXCLUDE_DIRS):
dirnames[:] = []
continue
if "package.json" in filenames:
parent_dir = os.path.basename(dirpath)
grandparent_dir = os.path.basename(os.path.dirname(dirpath))
pkg_name = None
if grandparent_dir == "node_modules":
if parent_dir == "react":
pkg_name = "react"
elif parent_dir == "next":
pkg_name = "next"
if pkg_name:
full_path = os.path.join(dirpath, "package.json")
try:
with open(
full_path, "r", encoding="utf-8", errors="ignore"
) as f:
data = json.load(f)
ver = data.get("version", "0.0.0")
print(f"[+] Found {pkg_name} v{ver} at {full_path}")
count_inspected += 1
# --- DOCKER TAGGING LOGIC ---
display_path = full_path
if (
"/var/lib/docker" in full_path
or "/var/lib/containerd" in full_path
):
display_path = f"[DOCKER] {full_path}"
log_entry = {
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"),
"scan_type": "react_inventory",
"package": pkg_name,
"version": ver,
"path": display_path,
}
with open(LOG_FILE, "a") as lf:
lf.write(json.dumps(log_entry) + "\n")
except Exception as e:
pass
print(f"[*] Scan complete. Total Packages Logged: {count_inspected}")
if __name__ == "__main__":
scan_system()
Note: This script scans for package.json and specifically flags packages like react and next while identifying if they are inside a Docker path.
Sample Output:
{"timestamp":"2025-12-11T13:15:07","scan_type":"react_inventory","package":"react","version":"19.2.1","path":"[DOCKER]/var/lib/docker/.../node_modules/react/package.json"}
{"timestamp":"2025-12-11T13:15:08","scan_type":"react_inventory","package":"next","version":"15.4.5","path":"/home/user/app/node_modules/next/package.json"}
Step 2: Wazuh Integration
Once the script generates /var/log/react2shell_scan.json, we need Wazuh to read it.
1. Configure the Agent:
Add this to the Wazuh agent or via centralized configuration (ossec.conf):
<localfile>
<log_format>json</log_format>
<location>/var/log/react2shell_scan.json</location>
</localfile>
Pushing the Configuration Centrally
To avoid manually editing the ossec.conf file on every server, we utilize Wazuh’s Centralized Configuration feature. By modifying the agent.conf file on the Manager, we can instruct all agents to start "watching" our custom inventory log.
On the Wazuh Manager:
Edit the shared configuration file for the default group:
vim /var/ossec/etc/shared/default/agent.conf
Add the following block inside the tags:
<agent_config>
<!--
This tells every agent to monitor the output of our inventory script.
Wazuh will collect these JSON logs and send them to the manager for analysis.
-->
<localfile>
<log_format>json</log_format>
<location>/var/log/react2shell_scan.json</location>
</localfile>
</agent_config>
How it works:
-
Synchronization: Once you save this file on the Manager, it automatically calculates a new
MD5 checksum. -
Propagation: The next time your agents check in (heartbeat), they will detect the configuration change, download the updated
agent.conffile, and restart their log collector engine. -
Visibility: From this point forward, any time your Python script (run via cron) updates
/var/log/react2shell_scan.json, the data is instantly streamed to the Wazuh Manager to be checked against your customReact2Shellrules.
2. Create the Detection Rules:
In your Wazuh Manager (/var/ossec/etc/rules/local_rules.xml), add rules to flag specific vulnerable versions based on the Vercel and React security bulletins.
<group name="react_inventory,cve_2025_55182,cve_2025_66478,vulnerability,">
<!-- INVENTORY: Log EVERY React/Next package found-->
<rule id="100500" level="3">
<decoded_as>json</decoded_as>
<field name="scan_type">react_inventory</field>
<description>Inventory: Found $(package) version $(version) at $(path).</description>
</rule>
<!-- Detect Vulnerable React.js -->
<rule id="100501" level="15">
<if_sid>100500</if_sid>
<field name="package">react</field>
<field name="version" type="pcre2">
^19\.0\.0$|^19\.1\.[0-1]$|^19\.2\.0$
</field>
<info type="cve">CVE-2025-55182</info>
<info type="link">https://nvd.nist.gov/vuln/detail/CVE-2025-55182</info>
<description>Vulnerable React2Shell detected: React $(version) at $(path)</description>
<mitre><id>T1190</id></mitre>
</rule>
<!-- Detect Vulnerable Next.js -->
<rule id="100502" level="15">
<if_sid>100500</if_sid>
<field name="package">^next$</field>
<field name="version" type="pcre2">^15\.0\.[0-4]$|^15\.1\.[0-8]$|^15\.4\.[0-7]$|^16\.0\.[0-6]$</field>
<info type="cve">CVE-2025-66478</info>
<info type="link">https://nvd.nist.gov/vuln/detail/CVE-2025-66478</info>
<description>Vulnerable React2Shell detected: Next.js $(version) at $(path)</description>
<mitre><id>T1190</id></mitre>
</rule>
</group>
Step 3: Deployment via Ansible
How do you get this onto 100 servers? Don't use Wazuh's "Command Monitoring" for script execution. Giving a SIEM manager the right to run arbitrary scripts on all agents is a major security risk if the manager is compromised.
Instead, use Ansible. Create a playbook to:
- Copy the Python script.
- Set up a Cron job (e.g., daily at 2 AM).
- Ensure the log file permissions are correct.
Notifications / Messenger Integration
To receive real-time alerts in your favorite messenger app, you can use these community integrations:
Telegram: https://github.com/0xdolan/wazuh-telegram-integration
Microsoft Teams: https://github.com/0xdolan/wazuh-teams-integration
Conclusion
By shifting from "System Inventory" to "Application Inventory," we caught a CVSS 10.0 vulnerability that otherwise would have stayed hidden in a local node_modules folder.

Top comments (0)