2025年11月4日,软件供应链安全企业JFrog的安全研究团队披露了React生态中的一个致命漏洞。该漏洞CVSS评分高达9.8(最高风险等级),存在于React Native核心依赖组件中,每周下载量超200万次,可能导致全球数百万开发者的开发环境被非法控制,引发代码泄露、恶意程序植入等严重后果。

漏洞核心详情拆解

  • 漏洞标识:编号CVE-2025-11953,属于远程代码执行(RCE)漏洞,是当前网络安全领域危害等级最高的漏洞类型之一。
  • 涉及组件:聚焦@react-native-community/cli NPM包的cli-server-api模块,该组件是React Native跨平台开发的核心工具,广泛应用于移动应用、前端项目开发场景。
  • 漏洞根源:组件的/open-url端点存在输入校验缺陷,未对用户提交的数据进行安全净化,直接将原始输入传递给第三方NPM包“open”的open()函数,形成命令注入通道。
  • 受影响版本:覆盖@react-native-community/cli-server-api 4.8.0至20.0.0-alpha.2版本,官方已在20.0.0及后续版本中完成补丁修复。
  • 传播范围:由于组件每周下载量超200万次,且React Native是全球主流跨平台开发框架,漏洞影响覆盖个人开发者、中小企业及大型科技公司的开发链路。

风险危害:本地开发环境也难逃攻击

  • 攻击门槛极低:攻击者无需身份验证,仅需通过网络访问目标开发服务器,即可利用漏洞执行任意操作系统命令。
  • 覆盖全场景部署:不仅公网暴露的开发服务器面临风险,即使是本地部署的Metro开发环境,也可能被外部网络渗透,打破“本地环境安全”的认知误区。
  • 多系统受波及:Windows系统受影响最为直接,攻击者可直接通过漏洞运行calc.exe等恶意程序;macOS和Linux系统虽存在防护机制,但仍存在理论攻击路径。
  • 危害连锁反应:开发环境被控制后,攻击者可窃取项目源代码、配置文件、密钥信息,甚至通过开发设备横向渗透企业内部网络,引发更大范围的数据泄露。

应急响应:两步实现漏洞防御

1. 临时缓解方案(无法立即升级时)

对于暂未完成版本升级的项目,可通过命令绑定限制服务器访问范围,快速阻断攻击路径:

npx react-native start --host 127.0.0.1

该命令将Metro开发服务器强制绑定到本地回环地址(127.0.0.1),禁止外部网络连接,从访问源头规避漏洞利用风险。

2. 彻底修复方案(优先推荐)

  • 版本查询:先通过命令确认项目是否使用受影响版本:
    # 局部项目查询(npm)
    npm list @react-native-community/cli-server-api
    # 全局安装查询(npm)
    npm list -g @react-native-community/cli-server-api
    # yarn项目查询
    yarn list @react-native-community/cli-server-api
    
  • 升级操作:将组件更新至20.0.0及以上版本:
    # npm用户
    npm install @react-native-community/cli-server-api@latest --save-dev
    # yarn用户
    yarn add @react-native-community/cli-server-api@latest --dev
    
  • 验证效果:升级后重新执行版本查询命令,确认组件版本≥20.0.0,且项目编译、运行无异常。

React项目依赖漏洞自动化扫描脚本(批量检测+自动报告)

针对企业级多项目场景,以下脚本支持批量遍历项目目录、检测目标组件版本风险,并自动生成结构化升级报告,大幅提升漏洞排查效率。

一、脚本核心功能

  1. 批量扫描指定目录下所有React/React Native项目,自动识别npm/yarn项目类型。
  2. 精准检测@react-native-community/cli-server-api组件版本,判断是否处于漏洞影响范围。
  3. 生成JSON+HTML双格式报告,包含项目名称、风险等级、当前版本、升级建议等关键信息。
  4. 支持自定义扫描目录、报告输出路径,兼容Linux/macOS/Windows三大操作系统。

二、跨平台脚本代码(复制即用)

1. Linux/macOS版本(Bash脚本)
#!/bin/bash
# 脚本名称:react-vuln-scanner.sh
# 功能:批量检测CVE-2025-11953漏洞,生成升级报告
# 使用方式:chmod +x react-vuln-scanner.sh && ./react-vuln-scanner.sh [扫描目录] [报告输出目录]

# 参数配置
SCAN_DIR=${1:-"./projects"}  # 默认扫描当前目录下projects文件夹
REPORT_DIR=${2:-"./vuln-reports"}  # 默认报告输出目录
REPORT_JSON="$REPORT_DIR/react-vuln-report-$(date +%Y%m%d%H%M%S).json"
REPORT_HTML="$REPORT_DIR/react-vuln-report-$(date +%Y%m%d%H%M%S).html"
TARGET_PACKAGE="@react-native-community/cli-server-api"
VULN_MIN_VERSION="4.8.0"
VULN_MAX_VERSION="20.0.0-alpha.2"
SAFE_VERSION="20.0.0"

# 创建报告目录
mkdir -p $REPORT_DIR

# 初始化JSON报告
echo '{"scan_info": {"scan_dir": "'$SCAN_DIR'", "scan_time": "'$(date +%Y-%m-%d\ %H:%M:%S)'", "target_vulnerability": "CVE-2025-11953", "vulnerable_version_range": "'$VULN_MIN_VERSION' - '$VULN_MAX_VERSION'"}, "projects": []}' > $REPORT_JSON

# 遍历项目目录
for PROJECT_DIR in $(find $SCAN_DIR -maxdepth 2 -type d -name "node_modules"); do
  PROJECT_ROOT=$(dirname $PROJECT_DIR)
  PROJECT_NAME=$(basename $PROJECT_ROOT)
  echo "正在扫描项目:$PROJECT_NAME(路径:$PROJECT_ROOT)"

  # 判断项目包管理工具(npm/yarn)
  if [ -f "$PROJECT_ROOT/package-lock.json" ]; then
    PM_TOOL="npm"
  elif [ -f "$PROJECT_ROOT/yarn.lock" ]; then
    PM_TOOL="yarn"
  else
    echo "警告:$PROJECT_NAME 不是npm/yarn项目,跳过"
    continue
  fi

  # 查询组件版本
  if [ "$PM_TOOL" = "npm" ]; then
    VERSION=$(npm list $TARGET_PACKAGE --prefix $PROJECT_ROOT 2>/dev/null | grep -oE "$TARGET_PACKAGE@[^ ]+" | awk -F '@' '{print $2}' | head -n 1)
  else
    VERSION=$(yarn list $TARGET_PACKAGE --cwd $PROJECT_ROOT 2>/dev/null | grep -oE "$TARGET_PACKAGE@[^ ]+" | awk -F '@' '{print $2}' | head -n 1)
  fi

  # 版本判断逻辑
  if [ -z "$VERSION" ]; then
    RISK_LEVEL="无风险"
    STATUS="未使用目标组件"
    UPGRADE_CMD="无"
  elif [ "$VERSION" = "$SAFE_VERSION" ] || [[ "$VERSION" > "$VULN_MAX_VERSION" && "$VERSION" != *"alpha"* ]]; then
    RISK_LEVEL="低风险"
    STATUS="已使用安全版本"
    UPGRADE_CMD="无"
  elif [[ "$VERSION" >= "$VULN_MIN_VERSION" && ( "$VERSION" <= "$VULN_MAX_VERSION" || "$VERSION" == *"alpha"* ) ]]; then
    RISK_LEVEL="极高风险"
    STATUS="处于漏洞影响范围"
    UPGRADE_CMD="$PM_TOOL install $TARGET_PACKAGE@latest --save-dev"
  else
    RISK_LEVEL="低风险"
    STATUS="版本不受影响"
    UPGRADE_CMD="无"
  fi

  # 写入JSON报告
  jq --arg name "$PROJECT_NAME" --arg path "$PROJECT_ROOT" --arg pm "$PM_TOOL" --arg ver "$VERSION" --arg risk "$RISK_LEVEL" --arg status "$STATUS" --arg cmd "$UPGRADE_CMD" \
    '.projects += [{"project_name": $name, "project_path": $path, "package_manager": $pm, "current_version": $ver, "risk_level": $risk, "status": $status, "upgrade_command": $cmd}]' \
    $REPORT_JSON > $REPORT_JSON.tmp && mv $REPORT_JSON.tmp $REPORT_JSON
done

# 生成HTML报告
cat > $REPORT_HTML << EOF
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>React CVE-2025-11953漏洞扫描报告</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    h1, h2 { color: #2c3e50; }
    .scan-info { background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
    table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
    th { background: #3498db; color: white; }
    .high-risk { background: #ffebee; color: #c62828; font-weight: bold; }
    .low-risk { background: #e8f5e9; color: #2e7d32; }
    .no-risk { background: #f5f5f5; color: #424242; }
  </style>
</head>
<body>
  <h1>React CVE-2025-11953漏洞批量扫描报告</h1>
  <div class="scan-info">
    <p><strong>扫描目录:</strong>$(echo $SCAN_DIR | sed 's/\/$//')</p>
    <p><strong>扫描时间:</strong>$(date +%Y-%m-%d\ %H:%M:%S)</p>
    <p><strong>漏洞编号:</strong>CVE-2025-11953(CVSS 9.8 高危)</p>
    <p><strong>影响版本:</strong>$VULN_MIN_VERSION - $VULN_MAX_VERSION</p>
    <p><strong>安全版本:</strong>$SAFE_VERSION 及以上</p>
  </div>
  <h2>项目扫描结果</h2>
  <table>
    <tr>
      <th>项目名称</th>
      <th>项目路径</th>
      <th>包管理工具</th>
      <th>当前组件版本</th>
      <th>风险等级</th>
      <th>状态描述</th>
      <th>升级命令</th>
    </tr>
EOF

# 填充HTML表格数据
jq -r '.projects[] | [.project_name, .project_path, .package_manager, .current_version, .risk_level, .status, .upgrade_command] | @tsv' $REPORT_JSON | while IFS=$'\t' read -r name path pm ver risk status cmd; do
  if [ "$risk" = "极高风险" ]; then
    ROW_CLASS="high-risk"
  elif [ "$risk" = "低风险" ]; then
    ROW_CLASS="low-risk"
  else
    ROW_CLASS="no-risk"
  fi
  echo "    <tr class=\"$ROW_CLASS\">" >> $REPORT_HTML
  echo "      <td>$name</td>" >> $REPORT_HTML
  echo "      <td>$path</td>" >> $REPORT_HTML
  echo "      <td>$pm</td>" >> $REPORT_HTML
  echo "      <td>$ver</td>" >> $REPORT_HTML
  echo "      <td>$risk</td>" >> $REPORT_HTML
  echo "      <td>$status</td>" >> $REPORT_HTML
  echo "      <td><code>$cmd</code></td>" >> $REPORT_HTML
  echo "    </tr>" >> $REPORT_HTML
done

# 结束HTML报告
cat >> $REPORT_HTML << EOF
  </table>
  <h3>操作建议</h3>
  <ul>
    <li>标记为「极高风险」的项目:立即执行升级命令,或使用临时缓解方案(npx react-native start --host 127.0.0.1)</li>
    <li>升级后需验证项目编译、运行是否正常,避免依赖冲突</li>
    <li>定期执行本脚本,持续监控依赖版本安全</li>
  </ul>
</body>
</html>
EOF

echo "扫描完成!报告已生成:"
echo "JSON格式:$REPORT_JSON"
echo "HTML格式:$REPORT_HTML"
2. Windows版本(PowerShell脚本)
<#
脚本名称:React-Vuln-Scanner.ps1
功能:批量检测CVE-2025-11953漏洞,生成升级报告
使用方式:.\React-Vuln-Scanner.ps1 -ScanDir [扫描目录] -ReportDir [报告输出目录]
#>

param(
  [string]$ScanDir = ".\projects",
  [string]$ReportDir = ".\vuln-reports"
)

# 配置参数
$TargetPackage = "@react-native-community/cli-server-api"
$VulnMinVersion = [version]"4.8.0"
$VulnMaxVersion = [version]"20.0.0"
$VulnMaxAlpha = "20.0.0-alpha.2"
$SafeVersion = [version]"20.0.0"
$ScanTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$ReportJson = "$ReportDir\react-vuln-report-$(Get-Date -Format 'yyyyMMddHHmmss').json"
$ReportHtml = "$ReportDir\react-vuln-report-$(Get-Date -Format 'yyyyMMddHHmmss').html"

# 创建报告目录
if (-not (Test-Path $ReportDir)) { New-Item -ItemType Directory -Path $ReportDir | Out-Null }

# 初始化JSON报告
$reportData = @{
  scan_info = @{
    scan_dir = $ScanDir
    scan_time = $ScanTime
    target_vulnerability = "CVE-2025-11953"
    vulnerable_version_range = "$VulnMinVersion - $VulnMaxAlpha"
  }
  projects = @()
}

# 遍历项目目录
$nodeModulesDirs = Get-ChildItem -Path $ScanDir -Recurse -Depth 2 -Filter "node_modules" -Directory
foreach ($nodeDir in $nodeModulesDirs) {
  $projectRoot = $nodeDir.Parent.FullName
  $projectName = $nodeDir.Parent.Name
  Write-Host "正在扫描项目:$projectName(路径:$projectRoot)"

  # 判断包管理工具
  if (Test-Path "$projectRoot\package-lock.json") {
    $pmTool = "npm"
  } elseif (Test-Path "$projectRoot\yarn.lock") {
    $pmTool = "yarn"
  } else {
    Write-Warning "$projectName 不是npm/yarn项目,跳过"
    continue
  }

  # 查询组件版本
  try {
    if ($pmTool -eq "npm") {
      $versionOutput = npm list $TargetPackage --prefix $projectRoot 2>&1
    } else {
      $versionOutput = yarn list $TargetPackage --cwd $projectRoot 2>&1
    }
    $versionMatch = [regex]::Match($versionOutput, "$TargetPackage@([\d\.a-zA-Z-]+)")
    $currentVersion = if ($versionMatch.Success) { $versionMatch.Groups[1].Value } else { $null }
  } catch {
    $currentVersion = $null
    Write-Warning "$projectName 版本查询失败,跳过"
    continue
  }

  # 版本风险判断
  if (-not $currentVersion) {
    $riskLevel = "无风险"
    $status = "未使用目标组件"
    $upgradeCmd = "无"
  } elseif ($currentVersion -match "alpha") {
    if ($currentVersion -le $VulnMaxAlpha) {
      $riskLevel = "极高风险"
      $status = "处于漏洞影响范围(Alpha版本)"
      $upgradeCmd = "$pmTool install $TargetPackage@latest --save-dev"
    } else {
      $riskLevel = "低风险"
      $status = "Alpha版本不受影响"
      $upgradeCmd = "无"
    }
  } else {
    $currentVerObj = try { [version]$currentVersion } catch { $null }
    if (-not $currentVerObj) {
      $riskLevel = "未知风险"
      $status = "版本格式异常"
      $upgradeCmd = "$pmTool install $TargetPackage@latest --save-dev"
    } elseif ($currentVerObj -ge $SafeVersion) {
      $riskLevel = "低风险"
      $status = "已使用安全版本"
      $upgradeCmd = "无"
    } elseif ($currentVerObj -ge $VulnMinVersion -and $currentVerObj -le $VulnMaxVersion) {
      $riskLevel = "极高风险"
      $status = "处于漏洞影响范围"
      $upgradeCmd = "$pmTool install $TargetPackage@latest --save-dev"
    } else {
      $riskLevel = "低风险"
      $status = "版本不受影响"
      $upgradeCmd = "无"
    }
  }

  # 添加到报告数据
  $projectObj = @{
    project_name = $projectName
    project_path = $projectRoot
    package_manager = $pmTool
    current_version = $currentVersion ?? "未检测到"
    risk_level = $riskLevel
    status = $status
    upgrade_command = $upgradeCmd
  }
  $reportData.projects += $projectObj
}

# 导出JSON报告
$reportData | ConvertTo-Json -Depth 10 | Out-File -FilePath $ReportJson -Encoding UTF8

# 生成HTML报告
$htmlContent = @"
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>React CVE-2025-11953漏洞扫描报告</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    h1, h2 { color: #2c3e50; }
    .scan-info { background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
    table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
    th { background: #3498db; color: white; }
    .high-risk { background: #ffebee; color: #c62828; font-weight: bold; }
    .low-risk { background: #e8f5e9; color: #2e7d32; }
    .no-risk { background: #f5f5f5; color: #424242; }
    .unknown-risk { background: #fff3e0; color: #ef6c00; }
  </style>
</head>
<body>
  <h1>React CVE-2025-11953漏洞批量扫描报告</h1>
  <div class="scan-info">
    <p><strong>扫描目录:</strong>$ScanDir</p>
    <p><strong>扫描时间:</strong>$ScanTime</p>
    <p><strong>漏洞编号:</strong>CVE-2025-11953(CVSS 9.8 高危)</p>
    <p><strong>影响版本:</strong>$VulnMinVersion - $VulnMaxAlpha</p>
    <p><strong>安全版本:</strong>$SafeVersion 及以上</p>
  </div>
  <h2>项目扫描结果</h2>
  <table>
    <tr>
      <th>项目名称</th>
      <th>项目路径</th>
      <th>包管理工具</th>
      <th>当前组件版本</th>
      <th>风险等级</th>
      <th>状态描述</th>
      <th>升级命令</th>
    </tr>
"@

# 填充HTML表格
foreach ($proj in $reportData.projects) {
  switch ($proj.risk_level) {
    "极高风险" { $rowClass = "high-risk" }
    "低风险" { $rowClass = "low-risk" }
    "无风险" { $rowClass = "no-risk" }
    default { $rowClass = "unknown-risk" }
  }
  $htmlContent += @"
    <tr class="$rowClass">
      <td>$($proj.project_name)</td>
      <td>$($proj.project_path)</td>
      <td>$($proj.package_manager)</td>
      <td>$($proj.current_version)</td>
      <td>$($proj.risk_level)</td>
      <td>$($proj.status)</td>
      <td><code>$($proj.upgrade_command)</code></td>
    </tr>
"@
}

# 结束HTML报告
$htmlContent += @"
  </table>
  <h3>操作建议</h3>
  <ul>
    <li>标记为「极高风险」的项目:立即执行升级命令,或使用临时缓解方案(npx react-native start --host 127.0.0.1)</li>
    <li>升级后需验证项目编译、运行是否正常,避免依赖冲突</li>
    <li>定期执行本脚本,持续监控依赖版本安全</li>
  </ul>
</body>
</html>
"@

# 导出HTML报告
$htmlContent | Out-File -FilePath $ReportHtml -Encoding UTF8

Write-Host "扫描完成!报告已生成:"
Write-Host "JSON格式:$ReportJson"
Write-Host "HTML格式:$ReportHtml"

三、脚本使用指南

1. 前置要求
  • 已安装Node.js(npm/yarn已配置环境变量)。
  • Linux/macOS需安装jq工具(用于JSON处理),执行brew install jq(macOS)或apt install jq(Ubuntu)。
  • Windows需使用PowerShell 5.1及以上版本(默认预装在Windows 10/11中)。
2. 执行步骤
  • Linux/macOS:

    1. 将脚本保存为react-vuln-scanner.sh。
    2. 赋予执行权限:chmod +x react-vuln-scanner.sh。
    3. 运行脚本:./react-vuln-scanner.sh 待扫描项目目录 报告输出目录(参数可选,默认扫描当前目录下projects文件夹)。
  • Windows:

    1. 将脚本保存为React-Vuln-Scanner.ps1。
    2. 打开PowerShell,切换到脚本目录。
    3. 运行脚本:.\React-Vuln-Scanner.ps1 -ScanDir 待扫描项目目录 -ReportDir 报告输出目录(参数可选)。
3. 报告解读
  • JSON报告:适用于自动化集成(如CI/CD流程、安全管理平台),可通过程序解析后触发告警。
  • HTML报告:可视化展示,支持管理人员快速查看高风险项目,包含直接可复制的升级命令。
  • 风险等级定义:
    • 极高风险:组件版本在4.8.0-20.0.0-alpha.2之间,需立即处理。
    • 低风险:使用安全版本或未使用目标组件,无需额外操作。
    • 未知风险:版本格式异常,建议手动核查或直接升级。

四、脚本扩展建议

  1. 集成到CI/CD流程:在Jenkins、GitLab CI等工具中配置定时任务,每周自动扫描所有项目,高风险漏洞阻断构建流程。
  2. 新增漏洞订阅:扩展脚本功能,定期拉取NVD、JFrog漏洞库,自动检测React生态其他高危漏洞。
  3. 批量升级功能:基于扫描结果,新增批量执行升级命令的模块,支持一键修复多个高风险项目。

行业警示:开源依赖安全不可掉以轻心

此次漏洞再次暴露了软件供应链的安全短板——即使是Meta主导维护的顶级开源生态,也可能因第三方依赖的安全缺陷出现高危风险。JFrog研究人员强调,这类“将用户输入直接传递给系统shell”的基础漏洞,本可通过规范的编码实践和自动化扫描提前规避。

对开发者和企业而言,需建立三重防护意识:一是定期审计项目依赖,避免使用长期未更新的组件;二是将输入净化作为编码强制规范,杜绝未校验数据流入危险函数;三是在开发环境部署基础安全防护,避免因“开发环境无需防护”的疏忽留下安全隐患。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐