CVE-2025-11953 - React Native CLI Metro command injection

CVE-2025-11953 is a critical command-injection issue in the Metro Development Server started by React Native Community CLI. Vulnerable development servers bind to external interfaces by default and expose an /open-url path that can pass attacker-controlled input to the open package. An unauthenticated network attacker can send a POST request to the dev server and run arbitrary executables; on Windows, the attacker can execute shell commands with controlled arguments.

This recipe is for repository owners who control React Native package pins, dev-server scripts, devcontainer/Codespaces exposure, CI workbenches, or agent workspaces. A mergeable fix does more than bump a dependency: it proves whether Metro is reachable beyond a single trusted local developer, removes exposed defaults, and documents credential cleanup when an affected server was online.

When to use it

  • A repository installs, vendors, builds, or documents React Native Community CLI or @react-native-community/cli-server-api in a potentially affected range.
  • Metro can be started by scripts, mobile run commands, devcontainers, Codespaces, remote IDEs, CI workbenches, or agent workspaces.
  • Metro may bind beyond loopback, publish a Docker/devcontainer port, use a tunnel, or run on Windows/shared workstations with reachable secrets.
  • You need a bounded PR or triage note that upgrades the CLI and proves Metro dev servers are loopback-only by default.

Inputs

  • package.json, lockfiles, React Native scripts, Metro config, mobile platform launchers, Docker/devcontainer/Codespaces config, CI jobs, SBOMs, generated dependency reports, and runbooks.
  • Host binding defaults, forwarded ports, wrapper routes, /open-url handling, Windows/macOS launcher behavior, secret-bearing environment, and credential rotation ownership.
  • Available package install, lockfile integrity, policy tests, mobile smoke tests, devcontainer render/build, lint/typecheck, SBOM, and security scan commands.

Affected versions

  • Affected package: @react-native-community/cli-server-api
  • Common bundle: @react-native-community/cli
  • Vulnerable: 4.8.0 through 20.0.0-alpha.2
  • Fixed: 20.0.0+
  • Affected runtime: Metro Development Server launched by React Native Community CLI commands such as npm start, npx react-native start, npx react-native run-android, npx react-native run-ios, npx react-native run-windows, or matching package-manager scripts.

Not every React Native repository is exposed. Projects that use a framework or toolchain that does not start Metro through the vulnerable Community CLI path should produce a documented TRIAGE.md or suppression note instead of a speculative dependency PR.

Indicator-of-exposure

  • The repository resolves @react-native-community/cli-server-api or @react-native-community/cli in the vulnerable range.
  • Scripts, runbooks, devcontainers, Codespaces configs, CI jobs, desktop launchers, or agent workbenches start Metro with npm start, react-native start, run-android, run-ios, run-windows, run-macos, or wrapper commands.
  • Metro binds beyond loopback, publishes a Docker/devcontainer port, exposes a remote IDE or Codespaces forwarded port, uses a tunnel, runs on a shared LAN workstation, or is reachable from browser-agent or mobile-device networks.
  • The dev-server process can read .env files, mobile signing keys, package tokens, repository tokens, cloud credentials, emulator/device data, SSH keys, local source trees, generated bundles, or internal network services.
  • The repository contains platform-specific React Native Windows/macOS launch paths, where Windows exposure is especially dangerous because shell command arguments can be controlled.

Quick checks:

rg -n "@react-native-community/cli|cli-server-api|react-native (start|run-android|run-ios|run-windows|run-macos)|metro|--host|0\\.0\\.0\\.0|127\\.0\\.0\\.1|open-url|devcontainer|forwardPorts|ports:" .
npm ls @react-native-community/cli @react-native-community/cli-server-api
pnpm why @react-native-community/cli @react-native-community/cli-server-api
yarn why @react-native-community/cli @react-native-community/cli-server-api

Windows:

rg -n "@react-native-community/cli|cli-server-api|react-native (start|run-android|run-ios|run-windows|run-macos)|metro|--host|0\.0\.0\.0|127\.0\.0\.1|open-url|devcontainer|forwardPorts|ports:" .
npm ls @react-native-community/cli @react-native-community/cli-server-api
pnpm why @react-native-community/cli @react-native-community/cli-server-api
yarn why @react-native-community/cli @react-native-community/cli-server-api

Do not validate exposure by POSTing to /open-url, launching executables, spawning shell commands, opening URLs against a real Metro server, or using a developer workspace with live secrets.

Remediation strategy

  • Upgrade every controlled @react-native-community/cli-server-api, @react-native-community/cli, React Native CLI bundle, package lock, devcontainer image, CI cache, generated dependency report, and SBOM to 20.0.0+ or a vendor-confirmed fixed release line.
  • Bind Metro to loopback by default. Add --host 127.0.0.1 to local scripts and remove accidental 0.0.0.0, Docker published port, devcontainer, Codespaces, tunnel, and LAN exposure defaults.
  • Keep development servers out of secret-bearing release, deploy, package publish, and signing jobs. Run Metro in a low-privilege development profile with minimal environment variables and no broad home-directory mounts.
  • If this repository owns wrappers around Metro, reject or remove any route that forwards untrusted URL values into open, shell launchers, child_process.exec, cmd /c, PowerShell, or OS-specific opener commands.
  • For React Native Windows and shared workstations, treat exposure as higher risk and require explicit proof that Metro is loopback-only after the fix.
  • If a vulnerable Metro server was reachable by untrusted clients, rotate mobile signing credentials, package tokens, repository tokens, cloud keys, model-provider keys, and other long-lived secrets that were present in the process environment.

The prompt

Model context: this prompt was generated by GPT 5.5 Extra High reasoning.

You are remediating CVE-2025-11953, a critical known-exploited command
injection vulnerability in React Native Community CLI / Metro Development
Server, also known as Metro4Shell. Produce exactly one output:

- A reviewer-ready PR/change request that upgrades every affected React Native
  Community CLI package, hardens Metro dev-server exposure, adds safe
  regression checks, refreshes generated artifacts, and documents operator
  credential cleanup, or
- TRIAGE.md if this repository does not control an affected React Native
  Community CLI package, Metro launch path, devcontainer/workbench exposure,
  image, script, lockfile, or safe containment path.

## Rules

- Scope only CVE-2025-11953 and directly related React Native Community CLI,
  Metro dev-server, `/open-url`, host binding, devcontainer, CI/workbench,
  script, lockfile, logging, and credential-boundary changes.
- Treat `.env` files, source code, mobile signing keys, emulator/device data,
  package tokens, repository tokens, cloud credentials, model-provider keys,
  SSH keys, browser profiles, command output, and logs as sensitive.
- Do not POST to `/open-url`, launch executables, spawn shells, execute
  proof-of-concept commands, or test against a real secret-bearing developer
  workspace.
- Do not remove tests, mobile platform support, signing checks, authentication,
  or CI gates just to silence the advisory.
- Do not preserve `0.0.0.0`, forwarded ports, tunnels, shared workbench
  ingress, or public Metro access as default behavior.
- Do not auto-merge.

## Steps

1. Inventory every JavaScript/TypeScript workspace, package manifest,
   lockfile, React Native CLI wrapper, Metro config, Dockerfile, devcontainer,
   Codespaces config, CI job, mobile platform script, generated dependency
   report, SBOM, and runbook controlled by this repository.
2. Resolve every `@react-native-community/cli-server-api` and
   `@react-native-community/cli` version. A target is vulnerable if it resolves
   from `4.8.0` through `20.0.0-alpha.2`, including transitive CLI bundles,
   global installs, image layers, and unpinned `npx` launch paths.
3. Inventory every Metro launch path: `npm start`, `react-native start`,
   `run-android`, `run-ios`, `run-windows`, `run-macos`, package-manager
   scripts, IDE launchers, devcontainer tasks, CI workbenches, and agent
   workspace commands.
4. Classify each Metro runtime boundary: single-user loopback, Windows local
   developer, emulator/device-only workflow, devcontainer forwarded port,
   Codespaces, LAN/shared workstation, remote IDE, browser-agent workbench,
   tunnel, CI runner, or internet/network-exposed service.
5. Determine which credentials and files the Metro process can reach:
   `.env` files, mobile signing keys, package tokens, cloud credentials,
   repository tokens, SSH agents, emulator/device data, browser profiles,
   generated bundles, local source, Docker sockets, and internal services.
6. If the repository only uses React Native through a framework that does not
   start Metro through the vulnerable Community CLI path, stop with
   `TRIAGE.md` listing checked files, resolved versions, why the vulnerable
   runtime is absent, owner, and recheck command.
7. Upgrade controlled package references to `@react-native-community/cli` and
   `@react-native-community/cli-server-api` `20.0.0+`, or to the vendor-fixed
   line this repository can consume. Refresh package locks, package-manager
   metadata, SBOMs, generated dependency reports, image layers, devcontainer
   artifacts, and docs.
8. Harden Metro exposure:
   - add `--host 127.0.0.1` or equivalent loopback binding to default scripts;
   - remove default `0.0.0.0`, Docker published port, devcontainer forwarded
     port, Codespaces public visibility, tunnel, LAN, and remote IDE exposure;
   - require an explicit reviewed exception for any non-loopback Metro server;
   - keep Metro out of release, publish, deploy, and signing jobs.
9. If this repository owns a Metro wrapper, route, or platform extension,
   ensure untrusted URL values are not passed to `open`, shell launchers,
   `child_process.exec`, `cmd /c`, PowerShell, or OS opener commands. Prefer
   strict URL allow-lists and structured APIs that do not invoke a shell.
10. Add safe regression checks:
    - dependency resolution rejects vulnerable CLI/server-api versions;
    - scripts/config default Metro to loopback;
    - config tests fail on public devcontainer/Codespaces/CI exposure unless a
      reviewed exception file exists;
    - wrapper tests use inert strings to prove disallowed URL shapes are
      rejected before any opener path;
    - logs do not include secrets, full environment values, command output, or
      sensitive local paths.
11. Add a PR body section named `CVE-2025-11953 operator actions` that states:
    - package versions before and after;
    - every Metro launch path changed;
    - whether any Metro server was reachable beyond loopback;
    - whether Windows, shared workstations, devcontainers, Codespaces, CI, or
      agent workbenches were in scope;
    - which credentials or files may have been reachable by the Metro process;
    - whether tokens and signing material were rotated, or why rotation is not
      required;
    - which validation commands passed.
12. Run available validation: package install, lockfile integrity, unit tests,
    mobile smoke tests, Metro local start with loopback binding, lint,
    typecheck, devcontainer render/build, CI config lint, container build,
    SBOM refresh, dependency/security scans, and a non-secret local smoke test
    that does not call `/open-url`.
13. Use PR title:
    `fix(sec): remediate CVE-2025-11953 in React Native CLI`

## Stop conditions

- No affected React Native Community CLI package, Metro launch path, wrapper,
  lockfile, image, devcontainer, CI/workbench config, or deployment artifact is
  controlled by this repository.
- The fixed React Native CLI line cannot be consumed without a broader mobile
  framework migration outside the current change.
- The only affected runtime is supplied by another team, platform image, or
  developer workstation owner; document owner, required version, exposure, and
  credential cleanup in `TRIAGE.md`.
- Product requirements intentionally depend on remote Metro access; require a
  product/security decision and a documented exception before preserving it.
- Safe verification would require live command execution, `/open-url`
  exploitation, production traffic, real device data, signing credentials, or
  other secrets.
- Validation fails for unrelated pre-existing reasons; document those failures
  instead of broadening scope.

Verification - what the reviewer looks for

  • No controlled manifest, lockfile, image, SBOM, generated report, global install, or launch path resolves @react-native-community/cli-server-api or @react-native-community/cli in the vulnerable range.
  • Metro dev-server scripts bind to 127.0.0.1 by default, and any non-loopback exception has an explicit owner and reason.
  • Devcontainers, Codespaces, CI jobs, shared workbenches, and Docker Compose do not publish Metro ports by default.
  • Tests or policy checks catch stale package pins, unpinned CLI launch paths, accidental exposed hosts, and unsafe opener wrappers without executing commands.
  • Operator notes cover Windows/shared-workstation exposure and credential or signing-key rotation when a vulnerable server was reachable.

Output contract

  • Reviewer-ready PR upgrading all controlled React Native Community CLI packages and refreshed locks, images, generated reports, SBOMs, and docs.
  • Metro defaults bound to 127.0.0.1, with Docker/devcontainer/Codespaces/CI exposure removed or documented through an explicit reviewed exception.
  • Safe regression checks for vulnerable package pins, public Metro bindings, unpinned CLI launch paths, and unsafe opener wrappers without executing commands or calling /open-url.
  • TRIAGE.md when the affected runtime, workbench, image, or credential cleanup owner is outside this repository.

Watch for

  • Updating package.json while package-lock.json, pnpm-lock.yaml, yarn.lock, Docker layers, devcontainer caches, or SBOMs still resolve a vulnerable CLI/server-api package.
  • Treating Metro as safe because it is a dev server while it is actually forwarded through a remote IDE, Codespace, tunnel, Docker port, LAN, or browser-agent workbench.
  • Fixing start while leaving run-android, run-ios, run-windows, run-macos, IDE tasks, or package-manager aliases exposed.
  • Ignoring Windows developer workflows, where attacker-controlled command arguments make exploitation more severe.
  • Adding diagnostics that print .env values, signing paths, emulator data, command output, local usernames, or full shell/open commands.

References