---
key: "dependency-response"
title: "Respond to an exploited or malicious dependency"
url: "https://softwaresecurity.com/domains/appsec/vulnerability-response/dependency-vulnerability-response/"
api: "https://softwaresecurity.com/api/v1/articles/dependency-response.json"
domain: "appsec"
topic: "Application security"
type: "Guide"
identifiers: ["CWE-1395","CWE-1104","CWE-506","A03:2025","V15.1.1","V15.1.2","V15.2.1"]
tags: ["Dependency management","Incident response","Vulnerability management"]
stages: ["respond"]
roles: ["appsec-engineer","platform-sre"]
license: "CC-BY-4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
code_license: "MIT"
---

# Respond to an exploited or malicious dependency

When a dependency vulnerability is being exploited, or a package version is malicious, triage gives way to incident response: find every running copy, contain it, patch and redeploy, hunt for exploitation, rotate secrets and close on deployed evidence.

Canonical URL: https://softwaresecurity.com/domains/appsec/vulnerability-response/dependency-vulnerability-response/

## In short

Treat a dependency as an incident when it is exploited in the wild or a version is malicious and you run it. Find every deployed copy from images and SBOMs, not repositories, and contain the internet-facing ones within hours. Patch to the fixed release, rebuild and redeploy, and keep checking the advisory for follow-ups. Assume exploitation until you have looked: hunt with the published indicators, rotate the secrets the process could read, and close only when every environment runs the fixed digest.

- The trigger is evidence of exploitation (a CISA KEV entry, a vendor or cloud provider report) or a malicious version, plus a copy you run.
- Scope from what is deployed: image scans and SBOMs find the forgotten site that a repository search misses.
- Provider WAF rules buy time; they are not a fix. For React2Shell, Next.js said there was no workaround.
- The first fixed release is not always the last: React shipped three more CVEs in the same code eight days after CVE-2025-55182.
- If the process was exposed and unpatched, rotate its secrets after patching, whether or not you found indicators.
- A malicious version ran at install or build time, so developer machines and CI runners are in scope too.

## Recommended defaults

| Setting | Value | Note | Source |
| --- | --- | --- | --- |
| Start the incident | At the first credible exploitation evidence for a component you run | A KEV listing, a vendor or cloud-provider report, or a malicious version in a lockfile. Do not wait for KEV. | CISA KEV catalog; vendor advisories |
| Internet-facing affected services | Patched or taken offline within hours | React2Shell was exploited within hours of disclosure (AWS, 2025-12-04). | AWS Security Blog |
| Temporary mitigations | Edge or WAF rules, feature flags, network isolation; dated and owned | A stopgap that does not change the vulnerable code. Remove it only after the patched digest runs everywhere. | React and Next.js advisories |
| Secrets on an exposed, unpatched process | Rotate after the patched build is deployed | Next.js advised rotation for apps online and unpatched as of 2025-12-04 13:00 PT. | Next.js CVE-2025-66478 advisory |
| EU CRA early warning (manufacturers) | Within 24 hours of becoming aware of an actively exploited vulnerability in your product | Applies since 2026-09-11; notification within 72 hours, final report within 14 days of a fix. | Regulation (EU) 2024/2847, Article 14 |

## Verify it

1. List every deployed image digest that contains an affected version, from image scans or stored SBOMs, and confirm each has an owner and a status.
2. Run `osv-scanner scan image` against each patched image and confirm the fixed version is the one that shipped.
3. Compare the digest running in every environment, including workers, previews and scheduled jobs, with the patched digest.
4. Check the advisory and the vendor's security page for follow-up CVEs before closing.
5. Confirm that every secret the exposed process could read was rotated after the patch and the old values are revoked.

## What you will learn

- Decide when a dependency finding is an incident rather than a backlog item.
- Scope every running copy from deployed evidence and contain the exposed ones first.
- Patch, redeploy and keep watching the advisory for follow-up fixes.
- Hunt for exploitation, rotate what the process could read, and close on deployed digests.

Audience: Service owners, on-call engineers and AppSec responders who must act within hours when a dependency they run is exploited in the wild or turns out to be malicious

Before you start:

- Find the image digest a service is running and the SBOM or lockfile it was built from.
- Know how your services get secrets and how to redeploy one quickly.
- For the commands: npm, jq and OSV-Scanner 2.x.

Time: About 29 minutes: 9 to read and 20 for the exercise

## When does a dependency finding become an incident?

Most dependency findings are routine: they go through triage, get a due date and are fixed by the next update. /domains/supply-chain/dependencies/evidence-based-triage/ covers that workflow. A finding becomes an incident when there is evidence that attackers are using it and you run an affected copy, or when the dependency itself is malicious. At that point the question is no longer which sprint, but which hosts and what has already happened.

The worked example on this page is CVE-2025-55182, called React2Shell: a pre-authentication remote code execution in React Server Components, rated CVSS 10.0. It was reported through Meta's bug bounty on 2025-11-29 and disclosed with fixed releases on 2025-12-03. AWS reported exploitation attempts by China-nexus groups within hours of disclosure, and CISA added it to the KEV catalog on 2025-12-05. Affected were `react-server-dom-webpack`, `react-server-dom-parcel` and `react-server-dom-turbopack` 19.0.0, 19.1.0, 19.1.1 and 19.2.0, and frameworks built on them, notably Next.js 15.x and 16.x applications using the App Router, which Next.js first tracked as CVE-2025-66478; that ID was later rejected as a duplicate of CVE-2025-55182.

- Exploited in the wild: a CISA KEV entry, a vendor advisory saying so, or a credible cloud-provider or incident-response report.
- Malicious version: an advisory of type malware (CWE-506) or a registry takedown of a version you resolved, as in /domains/supply-chain/dependencies/case-study-shai-hulud/.
- Vendor instruction to rotate credentials or assume compromise.
- For signals and due dates outside incidents (EPSS, CVSS 4.0, SSVC), use /frameworks/vulnerability-prioritization/.

## Hour one: find every running copy

Scope from deployed evidence. Repository searches find the services you remember; image scans and the SBOMs stored at release find the marketing site deployed last year and the worker nobody owns. For each affected copy, record the image digest, where it runs, whether it is reachable from the internet, and who can deploy it.

Then apply the advisory's own conditions, which are often narrower than the package match. For React2Shell, Next.js said that 13.x, 14.x stable, Pages Router applications and the Edge Runtime were not affected, while any App Router application on 15.x or 16.x was, even without Server Functions of its own. Next.js bundles its own copy of the React Server Components runtime, so for a Next.js app the `next` version decides, and a search for `react-server-dom-webpack` finds only standalone users such as React Router, Waku, Parcel or the Vite RSC plugin.

If the advisory is a malicious version, scope differently. The question is which machines installed it, because the code ran during `npm install` or the build: CI runners, developer laptops and build caches, not only the deployed service. /domains/supply-chain/dependencies/package-attack-response/ covers that path.

```bash
# 1. Every repository: which Next.js does each lockfile resolve, and does the app
#    use the App Router? (Pages Router-only apps were not affected.)
npm ls next --package-lock-only
test -d app || test -d src/app && echo "App Router present"

# 2. Standalone React Server Components runtimes (React Router, Waku, Parcel, Vite
#    plugin). Next.js vendors its own copy, so for Next.js the next version decides.
npm ls react-server-dom-webpack react-server-dom-turbopack react-server-dom-parcel --package-lock-only

# 3. What is actually running: scan the deployed image, not the repository
osv-scanner scan image registry.example.com/shop-web@sha256:4f1c...

# 4. Across the fleet: every SBOM in the store that lists an affected next version
for f in sboms/*.cdx.json; do
  jq -r --arg f "$f" '.components[]? | select(.name == "next")
    | select(.version | test("^(15|16)\\."))
    | "\($f)\t\(.version)"' "$f"
done

```

## Contain the exposed copies first

Order the work by exposure. An internet-facing service with an affected copy is patched first or taken offline; an internal one follows the same day, because an attacker with a foothold or a compromised laptop can reach it. Copies that the advisory's conditions rule out get a written not-affected decision rather than silence.

Use temporary mitigations for what they are. Hosting providers and CDNs deployed temporary WAF mitigations for React2Shell at disclosure, and React said plainly that you should not depend on them and should still update immediately; Next.js said there was no workaround. A WAF rule matches known request shapes and can be bypassed by encodings it does not normalize, and disabling a feature only helps when the advisory says the vulnerable code is unreachable without it. Give every mitigation an owner and an expiry, and remove it only after the patched digest runs everywhere.

## Patch every copy, then check for follow-up advisories

Upgrade to the fixed release in each line: for CVE-2025-55182, `react-server-dom-*` 19.0.1, 19.1.2 or 19.2.1 and Next.js 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7 or 16.0.7. Pin the exact version, rebuild without reusing a cached dependency layer, and confirm in the built image, not the lockfile, that the fixed version is what shipped. A transitive copy may need an `overrides` (npm) or `resolutions` (Yarn) entry until its parent releases.

Keep the advisory open after you patch. On 2025-12-11 React disclosed three more vulnerabilities in the same code, two denial-of-service issues (CVE-2025-55184, CVE-2025-67779) and a source-code exposure (CVE-2025-55183), and on 2026-01-26 a fourth (CVE-2026-23864). The React2Shell fix stayed effective against remote code execution, but the releases that fixed it were not the last ones teams needed. Researchers study a patched component closely after a critical fix, so subscribe to the project's advisories and recheck daily until the incident closes.

## Look for exploitation before you call it closed

A patch stops new exploitation; it says nothing about what happened before. Treat the exposure window as starting no later than public disclosure, earlier if the advisory or KEV entry says exploitation preceded it, and ending when the patched digest was running. Preserve the evidence before redeploying replaces the hosts: request logs, container logs, process telemetry and a copy of each affected image.

Hunt with the indicators the vendor and responders published. For React2Shell, AWS's guidance included the following, which suit any Node.js service once adapted to your log fields.

- HTTP `POST` requests to application routes carrying a `next-action` or `rsc-action-id` header, especially from unfamiliar sources.
- Request bodies containing `$@` or `"status":"resolved_model"`.
- Commands such as `whoami`, `id` or `uname` run as children of the Node.js process, reads of `/etc/passwd`, or new files in `/tmp`.
- Any outbound connection from the application process to an address it does not normally contact.
- If you find indicators, hand over to your incident process: /domains/runtime/detection/runtime-detection-response/ covers containment, and logs need the fields in /domains/runtime/detection/security-event-logging/.

## Rotate what the vulnerable process could read

Remote code execution in a web process exposes everything that process can read: environment variables, mounted secret files, cloud credentials from the instance or workload identity, database passwords and signing keys. Next.js advised every team whose app was online and unpatched as of 2025-12-04 13:00 PT to rotate its secrets, starting with the most critical. Absence of indicators is not a reason to skip this, because logs rarely record what a successful exploit read.

Rotate after the patched build is running, or the new secrets are exposed to the same flaw. List every secret the service had, rotate in order of blast radius, revoke the old values rather than leaving them valid, and check provider audit logs for use of the old credentials during the window. /domains/identity-crypto/secrets/leaked-secret-response/ gives the rotation runbook.

## Tell customers what is and is not affected

During an incident your customers and auditors ask the same question you asked in hour one. Publish the answer per product: affected and fixed in which release, or not affected with the reason. A Vulnerability Exploitability eXchange (VEX) statement makes the not-affected answer machine-readable, so scanners stop flagging it, and it carries the condition that would invalidate it. The OpenVEX document below records the decision for a Pages Router-only site that still contains an affected `next` version; /domains/supply-chain/vex/producer-and-consumer-decisions/ covers producing and consuming these statements.

If you manufacture a product with digital elements sold in the EU, an actively exploited vulnerability in that product can start a Cyber Resilience Act clock: an early warning to ENISA's single reporting platform within 24 hours of becoming aware, a notification within 72 hours and a final report within 14 days of a fix being available. /frameworks/cra-reporting-runbook/ walks through it.

```json
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "@id": "https://vex.example.com/2025-12-05/docs-site-cve-2025-55182",
  "author": "Example Product Security Team",
  "timestamp": "2025-12-05T16:00:00Z",
  "version": 1,
  "statements": [
    {
      "vulnerability": {"name": "CVE-2025-55182"},
      "products": [
        {
          "@id": "pkg:oci/docs-site@sha256%3A9b2e0c0000000000000000000000000000000000000000000000000000000000",
          "subcomponents": [{"@id": "pkg:npm/next@15.4.6"}]
        }
      ],
      "status": "not_affected",
      "justification": "vulnerable_code_not_in_execute_path",
      "impact_statement": "docs-site uses only the Pages Router; Next.js states Pages Router applications are not affected. Reassess if an app/ directory or Server Functions are added."
    }
  ]
}

```

## Close on deployed evidence and fix the gap that slowed you

Close the incident per artifact, not per repository. The record should list each affected digest, where it ran, the exposure window, the mitigation and when it was removed, the patched digest now running, the hunt performed and its result, the secrets rotated, and the customer statements issued. A fleet table with digest, location, owner and status prevents reporting 'patched' when only the main web process changed.

Then ask what made the response slow, and fix that. Common answers: no stored SBOM per release, so scoping took a day; no owner for an old site; secrets that could not be rotated without a code change; no way to redeploy a service quickly. /domains/supply-chain/sbom/generate-release-inventory/ is usually the first improvement.

## Where dependency incident responses go wrong



- The lockfile is fixed but the image is not: a cached build layer shipped the old copy. Check the built artifact.
- A WAF rule is treated as the fix, and the patch slips past the news cycle.
- Secrets are rotated before the patch is deployed, so the new ones are exposed to the same flaw.
- The first fixed release is pinned and the follow-up advisory a week later is missed.
- No indicators found is reported as no compromise, although the logs never recorded request bodies.
- A dormant worker, preview environment or old marketing site keeps running the vulnerable digest because it was not in the repository search.
- For a malicious version, the deployed service is cleaned but the CI runners and laptops that ran the install script are not.

## Guided practice

It is 2025-12-05, two days after the React2Shell disclosure, and CISA has just added CVE-2025-55182 to KEV. You lead the response for the five services below. Decide scope, containment, patching, hunting and rotation. The facts are given; treat them as true.

### Supplied material

```text
shop-web: Next.js 15.4.6, App Router, internet-facing; image built 2025-11-28; CDN provider enabled its React2Shell WAF rule on 2025-12-04; env holds STRIPE_SECRET_KEY, DATABASE_URL and a cloud role.
admin-portal: Next.js 15.5.2, App Router, reachable only over the corporate VPN; env holds an SSO client secret.
docs-site: Next.js 15.4.6, Pages Router only (no app/ directory), internet-facing.
legacy-site: Next.js 14.2.30 (stable), App Router, internet-facing.
edge-renderer: Waku app with react-server-dom-webpack 19.2.0, internet-facing.
Logs: shop-web request logs include headers but not bodies; one POST on 2025-12-04 carried a next-action header from an unfamiliar ASN, followed by a 500 response.
```

### 1. Scope the fleet

Decide which services are affected by CVE-2025-55182.

Worked answer: shop-web, admin-portal and edge-renderer are affected: two App Router apps on Next.js 15.x and a standalone `react-server-dom-webpack` 19.2.0. docs-site is not affected because it uses only the Pages Router, and legacy-site is not affected because Next.js 14.x stable was outside the affected range. Record both not-affected decisions with their reasons.

### 2. Order containment

Decide what happens first and whether the WAF rule is enough for shop-web.

Worked answer: Patch or take offline shop-web and edge-renderer now, because both are internet-facing; admin-portal the same day, because anyone on the VPN, including a compromised laptop, can reach it. The WAF rule is a stopgap with an owner and expiry, not a fix: the vendors said not to rely on such rules and that there was no workaround.

### 3. Choose the fixed versions

Name the minimum fixed release for each affected service and how you will confirm it shipped.

Worked answer: shop-web: `next` 15.4.8. admin-portal: `next` 15.5.7. edge-renderer: `react-server-dom-webpack` 19.2.1. Rebuild without cached dependency layers, scan the built images, and deploy those digests. Keep the advisory open: later fixes for the same code followed on 2025-12-11 and 2026-01-26.

### 4. Hunt and decide on rotation

Interpret the shop-web log entry and decide which secrets to rotate.

Worked answer: The `next-action` `POST` from an unfamiliar ASN is consistent with exploitation attempts but does not prove success, and without request bodies you cannot tell. Preserve the logs and image, check process and egress telemetry for the window, and escalate to the incident process. Rotate shop-web's Stripe key, database credentials and cloud role credentials, and admin-portal's SSO client secret, after the patched builds are running, because both were exposed and unpatched; edge-renderer's secrets likewise.

### 5. Close and communicate

State what must be true before the incident closes and what customers are told.

Worked answer: Every affected service runs a scanned, patched digest in every environment; mitigations are removed or re-dated; secrets are rotated and old values revoked; the hunt result is recorded. Publish affected-and-fixed statements for the three services and VEX `not_affected` statements for docs-site and legacy-site.

Deliverable: An incident record with a fleet table (service, digest, affected or not and why, exposure, mitigation, patched digest, secrets rotated), the hunt summary, and the customer statements.

Scope: The services, logs and secrets are invented; the advisory facts, versions and dates are real. The exercise does not model forensics depth: a real hunt needs process, network and cloud audit telemetry, and an indicator match is a lead to investigate, not proof of compromise.

Articles and teaching material: CC BY 4.0. Original executable code: MIT. Attribution: Software Security.

## Sources

- [React: Critical security vulnerability in React Server Components (2025-12-03)](https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components)
- [Next.js: Security advisory CVE-2025-66478](https://nextjs.org/blog/CVE-2025-66478). 3 December 2025.
- [React: Denial of service and source code exposure in React Server Components (2025-12-11)](https://react.dev/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components)
- [GitHub Advisory GHSA-fv66-9v8q-g76r (CVE-2025-55182)](https://github.com/advisories/GHSA-fv66-9v8q-g76r)
- [AWS Security Blog: China-nexus cyber threat groups rapidly exploit React2Shell vulnerability (CVE-2025-55182)](https://aws.amazon.com/blogs/security/china-nexus-cyber-threat-groups-rapidly-exploit-react2shell-vulnerability-cve-2025-55182/). 4 December 2025.
- [CISA: CISA Adds One Known Exploited Vulnerability to Catalog (CVE-2025-55182)](https://www.cisa.gov/news-events/alerts/2025/12/05/cisa-adds-one-known-exploited-vulnerability-catalog). 5 December 2025.
- [OWASP Vulnerable Dependency Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerable_Dependency_Management_Cheat_Sheet.html)
- [OSV-Scanner: usage](https://google.github.io/osv-scanner/usage/)
- [OpenVEX specification](https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md)
- [Regulation (EU) 2024/2847 (Cyber Resilience Act)](https://eur-lex.europa.eu/eli/reg/2024/2847/oj). OJ L, 20 November 2024.