# Fix Vulnerable Dependencies in a React App

Yes. An agent audits your React app's lockfile, applies the smallest upgrade that clears each advisory, reruns the tests and opens a pull request with both audits.

[Run this on Rush](https://cloud.getrush.ai/run/fix-vulnerable-dependencies/react/), in a Project that has your repository.

## What the agent does

The agent clones the repository into its own cloud sandbox and first works out which Yarn it is dealing with, because the audit command depends on it. `yarn npm audit` is the Yarn 2+ (Berry) command. A React app like cypress-io/cypress-realworld-app declares `"packageManager": "yarn@1.22.22"` in `package.json`, keeps a `# yarn lockfile v1` `yarn.lock` and a `.yarnrc` rather than a `.yarnrc.yml`, so the agent runs `yarn audit --json` instead, and says in the pull request that it did. On a Berry project it runs `yarn npm audit --all --recursive --json`. It also pins Node to what the repository asks for: `.nvmrc` and `.node-version` both say `22.20.0`, and `engines` allows `^22.0.0 || ^24.0.0`.

It saves that first audit as the baseline and sorts each advisory by how the vulnerable package arrives. Some are direct: the app pins `axios` to `0.28.1` and `express` to `4.20.0` exactly, and `yarn.lock` also carries `axios@^0.21.1` and `axios@^1.11.0` for other packages that depend on it. A direct dependency gets the smallest version that the advisory lists as patched, kept inside the same major where one exists. A transitive one gets an upgrade of the parent that pulls it in when such a release exists. Only when no parent release fixes it does the agent add an entry to a `resolutions` field in `package.json` (Yarn 1's form of an override; Berry also reads `resolutions`), and it writes down which advisory each entry is for. The project has no `resolutions` today, so every one it adds is new and visible in the diff.

Some upgrades are not free in this app, and the agent checks them before choosing. `react-virtualized` `9.22.5` is patched by `patches/react-virtualized+9.22.5.patch`, applied by `patch-package` in the `postinstall` script, so moving that package means the patch no longer applies and the install fails. `renovate.json` holds `@types/express` and `@types/express-serve-static-core` to `4.17.2` and groups packages such as React, React Router and MUI. The agent keeps those groupings intact and leaves an advisory open, with the reason, when the only fix is a major upgrade of a grouped library like `react-router-dom` `5.3.4`.

Then it runs what the repository's own CI runs in its `install` job: `yarn types` (`tsc --noEmit`), `yarn lint`, `yarn test:unit:ci` (Vitest over the files in `src/__tests__`) and `yarn build:ci`. When Cypress can start in the sandbox it also runs `yarn test:api`, the Cypress API specs in `cypress/tests/api` against the Express backend, after `yarn start:ci`. It reruns the audit, and only when the advisories it chose to fix are gone and the checks pass does it open the pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Audit this repository's dependencies and fix the advisories. Read packageManager in package.json first: on Yarn 1 (a v1 yarn.lock) run yarn audit --json, on Yarn 2 or later run yarn npm audit --all --recursive --json, and save the output. For each advisory apply the smallest upgrade that fixes it, staying inside the current major where a patched version exists. Fix transitive packages by upgrading the parent that pulls them in, and add a resolutions entry only where no parent release fixes it, with the advisory it is for. Keep patch-package patches applying and respect the groupings in renovate.json. Run yarn types, yarn lint, yarn test:unit:ci and yarn build:ci, then audit again. Open a pull request with the audit output before and after, one line per upgrade with the advisory it fixes, and every advisory left open with the reason.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on a new branch that changes `package.json` and `yarn.lock`, plus a `resolutions` block when a transitive fix needed one. The description holds the audit summary before and after, a table with one row per upgrade (package, old version, new version, the advisory it closes, direct or transitive), and a list of advisories left open with why: a fix only in a new major, a package held by `renovate.json`, or a patched package whose patch file would need to be rewritten.

The checks on the pull request are the repository's own workflow, `.github/workflows/main.yml`, which runs the type check, lint, Vitest and build inside the `cypress/browsers:22.20.0` image and then the Cypress UI specs in parallel containers. The session's transcript keeps every command from the sandbox, including both audit runs, so the before and after numbers in the description can be checked against the raw output.

Before merging, a reviewer reads each `resolutions` entry, because forcing a version under a package that did not ask for it is the one change here that can break at runtime without failing a type check. They confirm that `patch-package` still reports the `react-virtualized` patch as applied in the install log, and that no upgrade crossed a major version the description does not call out.

## Run it on every new security advisory

Advisories land on their own schedule, so a weekly run catches most of them without anyone watching a feed. A scheduled job on your server can send this request every Monday for each React repository you own, each with its own `resources` URL and an `Idempotency-Key` built from the repository and the week, so a retried request returns the run that already started instead of opening a second pull request.

With a webhook URL on the API key, Rush posts `run.completed` or `run.failed` when each turn ends. Your handler can read the session from `session_url` and post the pull request link to the team channel, or open a ticket for advisories the agent left open.

## Why run it on Rush?

A dependency fix is only as good as the install and test run behind it. In a Rush sandbox the agent does a clean `yarn install` from the lockfile, with the `postinstall` hook running `husky install` and `patch-package` exactly as CI does, and runs the audit, the type check, the unit tests and the build on that tree. Nothing depends on what is already in someone's `node_modules` or global Yarn cache.

The record is the proof: the transcript keeps both audit outputs and each test command with its output, and the pull request runs the repository's own workflow. Several repositories can be audited at once, each in its own sandbox with its own branch, so one team can clear the same advisory across many apps in one morning instead of one laptop session per repository.

## Frequently asked questions

### My project is on Yarn 1. Does yarn npm audit still work?

No. `yarn npm audit` exists only in Yarn 2 and later. The agent reads `packageManager` and the lockfile header, runs `yarn audit` on Yarn 1, and uses `resolutions` for transitive fixes on either version.

### Will it upgrade React or React Router to a new major to clear an advisory?

Not without saying so. The prompt asks for the smallest upgrade inside the current major, so an advisory whose only fix is a new major of a library like `react-router-dom` is listed as left open with the version that would fix it. You can send a follow-up message asking for that upgrade as its own change.

### What happens to packages fixed with patch-package?

A patch file is named for one exact version, such as `react-virtualized+9.22.5.patch`, so upgrading that package stops the patch from applying and the install fails. The agent either leaves that package where it is and says why, or rewrites the patch for the new version and explains the change in the pull request.

### Does it also run the Cypress end-to-end suite?

It runs the checks the CI `install` job runs, and the Cypress API specs when Cypress can start in its sandbox. The full UI suite runs on the pull request in the repository's own workflow, which is where its results count.
