# CVE Remediation

Turn open vulnerability scanner findings into merged fixes. One fix droid per fix triages it, plans it, opens a pull request, and merges after you approve.

<PrivatePreview>
  CVE remediation is in Private Preview with limited availability. Contact your Factory account team to request access.
</PrivatePreview>

CVE remediation turns open findings from your vulnerability scanner into merged fixes. It runs inside a [workstream](/workstreams/overview). A listener checks your scanner on a schedule and creates a task for each fix. The orchestrator starts a fix droid for each task. The fix droid triages the finding, writes a plan, makes the change, opens a pull request, and keeps CI passing. You approve plans, make decisions, and merge from the workstream's [Tasks tab](/workstreams/tasks).

## How it works

<PropertyList>
  <Property name='Listener'>
    An agent named **CVE remediation** that `/cve-remediation` adds to the workstream. You set it up in its own chat. After setup, it polls your scanner on a schedule and creates a task for each new fix.
  </Property>
  <Property name='Task'>
    One fix in one repository: one package upgrade, one base image update, or one code finding in one file. A task covers every vulnerability that its fix resolves. Task IDs look like `FIX-3f9a2c1b`.
  </Property>
  <Property name='Fix droid'>
    An agent the orchestrator starts for one task. It takes the task from triage to merge and records each step on the task.
  </Property>
  <Property name='Tasks tab'>
    A tab in the workstream's right pane that lists every task and shows which ones need you.
  </Property>
</PropertyList>

Each task moves through these steps:

1. **Triage.** The fix droid checks whether the finding affects what ships and what the safest fix is. When it needs you, the task waits for a decision or an answer.
2. **Plan.** For a clear fix, the fix droid writes a plan: the exact change, any breaking changes, the risk, how it will validate the fix, and how to roll it back. The plan waits for your approval.
3. **Pull request.** The fix droid makes the change on a new branch, builds and tests it, and opens a pull request.
4. **CI.** The fix droid watches the checks and fixes failures that its change caused.
5. **Merge.** When the checks pass, the task waits for you to select **Merge PR**. The fix droid then merges the pull request.
6. **Confirm.** The task waits for the scanner. When a later poll no longer reports the finding, the task is **Fixed**.

## Before you start

{/* sweep-allow: term-bullets */}

- **Access.** CVE remediation turned on for your organization, and a workstream with a [shared folder](/workstreams/shared-folder).
- **Machines.** The orchestrator, the listener, and the fix droids run on macOS or Linux machines with `python3` 3.9 or later and the `droid` CLI. Windows isn't supported.
- **Scanner access.** A credential the listener can read on its machine. See [Scanners](#scanners).
- **Repository access.** On the machines where fix droids run, Git access to clone each repository and push branches, plus the `gh` or `glab` CLI signed in with permission to open and merge pull requests.
- **Branch protection.** Required reviews and required status checks on the default branch of every repository in scope. See [Security and access](#security-and-access).
- **The Factory App.** The Tasks tab is in the desktop and web app, not in the CLI.

## Set up CVE remediation

<Steps>
  <Step title="Run the command in the orchestrator chat">
    In a workstream's orchestrator chat, run:

    ```text
    /cve-remediation
    ```

    You can add details after the command, such as the scanner, the scope, or the machine the listener should run on. The orchestrator creates a listener named **CVE remediation**. Each workstream has one, so running the command again shows status instead.
  </Step>

  <Step title="Open the listener's chat">
    Setup happens in the listener's chat, named **CVE remediation**. The listener first checks which credentials its machine already has: the `SNYK_TOKEN`, `GITHUB_TOKEN`, `GH_TOKEN`, and `GITLAB_TOKEN` environment variables, and `gh` or `glab` sign-ins. It never asks you to paste a secret into the chat.
  </Step>

  <Step title="Choose the scanner, scope, and severity floor">
    The listener asks three questions:

    - **Scanner and credential.** For example, Snyk with a token in `SNYK_TOKEN`, or GitHub alerts through the `gh` CLI. You can name a different environment variable.
    - **Scope.** The Snyk organization ID, the GitHub owner (an organization or a user), or the GitLab group or projects. You can limit it to some repositories.
    - **Severity floor.** Critical only, High and above (recommended), Medium and above, or All.
  </Step>

  <Step title="Let it check access">
    The listener reads your scanner once and reports how many fixes it found. If it can't read the scanner, it tells you why and how to fix it, such as setting the variable where the listener runs or running `gh auth login`. Reply in the chat once it's fixed.
  </Step>

  <Step title="Choose autonomy, pace, and schedule">
    The listener asks three more questions. See [Setup choices](#setup-choices). Also tell it anything fix droids should know about your repositories, such as reviewers to request, branch naming, or build steps.
  </Step>

  <Step title="Review the dry run and start">
    The listener lists the fixes its first poll would create, with each title, severity, and CVE ID. Choose **Start**, or choose **Change something** to adjust the setup and see a new dry run.
  </Step>

  <Step title="Watch the first tasks arrive">
    The listener schedules its polls and runs the first one right away. The orchestrator starts a fix droid for each new task and tells you which ones it started. The **Tasks** tab opens in the right pane once the first task exists.
  </Step>
</Steps>

## Setup choices

### Autonomy

| Option | What happens |
| --- | --- |
| **Review every plan** (recommended) | Every plan waits for your approval before any code changes. |
| **Full autonomy** | A plan for a patch or minor dependency upgrade with no breaking changes skips approval. Every other plan still waits for you. |

With either option, fix droids are instructed to merge only after a person selects **Merge PR**. Factory doesn't enforce this, so set up branch protection as described in [Security and access](#security-and-access).

### Pace

| Pace | New tasks per poll | Open tasks at most |
| --- | --- | --- |
| Trial | 2 | 5 |
| Standard (recommended) | 10 | 25 |
| Fast | 25 | 100 |

Each poll starts with fixes for vulnerabilities in the CISA Known Exploited Vulnerabilities catalog, then the most severe, then the oldest. Open tasks are counted across all repositories. When the limit is reached, new fixes wait for a later poll. Each fix droid is its own agent session and uses your Factory usage, so pace also sets how fast CVE remediation spends it. See [usage limits](/workstreams/settings#usage-limits).

### Schedule

Choose every hour (recommended), every 4 hours, once a day, or your own cron expression with runs at least 5 minutes apart.

Polls run where the listener runs. If the listener runs on your own computer, polls stop while the computer is off or the desktop app is closed. For unattended polling, run the listener on a cloud computer. See [where runs go and where they run](/workstreams/automations#where-runs-go-and-where-they-run).

## Scanners

### Snyk

- **Credential.** A Snyk API token in an environment variable. A service account token works best.
- **Scope.** The organization ID from your Snyk organization settings.
- Regional Snyk API hosts and bearer tokens are supported. If your Snyk project names differ from your repository names, tell the listener how they map.

### GitHub

The listener reads Dependabot alerts, code scanning alerts, or both.

- **Credential.** The `gh` CLI signed in, or a token in an environment variable. A fine-grained token needs read access to **Dependabot alerts** and **Code scanning alerts**. A classic token needs the `repo` and `security_events` scopes.
- **Scope.** An organization or a user account, optionally limited to some repositories.
- GitHub Enterprise Server is supported.

If an alert type is turned off for a repository, the listener skips it for that repository. A poll fails only when nothing can be read.

### GitLab

The listener reads GitLab vulnerabilities, which need GitLab Ultimate.

- **Credential.** The `glab` CLI signed in, or a token with the `read_api` scope.
- **Scope.** A group, or a list of projects.
- Self-managed GitLab is supported.

### Other scanners

For another scanner, choose **Another scanner** during setup and tell the listener how to read it: through a connector, an MCP server, or a file the scanner's CLI exports.

### What becomes a task

- Dependency, container base image, and code findings become tasks by default. You can also turn on infrastructure as code findings.
- Findings below your severity floor are skipped.
- A dependency finding that the scanner lists with no fixed version never becomes a task.
- You can skip findings the scanner marks as development-only dependencies, and limit the scope to some repositories.
- Vulnerabilities fixed by the same change share one task.

## Decisions and questions

Triage ends in one of four ways:

| Outcome | What you see in the Tasks tab |
| --- | --- |
| A clear, safe fix, such as a patch or minor upgrade, a base image update within the same distribution release, or a contained code change | **Plan needs approval**, unless Full autonomy skips approval |
| A choice only you can make | **Needs decision**, with a recommended choice |
| A fact only you know, such as which service ships a manifest | **Droid has a question** |
| No workable fix | **Fix failed** |

The fix droid offers a decision in these cases:

| Situation | Choices | Usually recommended |
| --- | --- | --- |
| The finding doesn't affect what ships | Not affected, Fix | Not affected |
| The only fix is a major upgrade with breaking changes | Fix, Accept risk, Defer, Won't fix | The one the evidence supports |
| No usable fix yet, such as a base image with no patched release, or a fixed version the scanner names that this repository can't use yet | Defer, Accept risk, Won't fix | Defer |
| Another task already covers the same fix | Won't fix, Fix | Won't fix |

**Fix** sends the task to planning. The other choices close the task. A leaked secret always comes to you as a decision, because rotating it and rewriting history are the owner's call.

After a merge, if the scanner still reports the finding 3 days later, the task comes back as **Needs a check**. You can change how long it waits in the listener's chat. The fix droid looks into why, for example another lockfile that still has the old version, and records what it found. You choose **Retry** or **Close as fixed**.

## Act on tasks

Act on tasks from the [Tasks tab](/workstreams/tasks). You can also tell the orchestrator in its chat, for example:

```text
approve the plan for FIX-3f9a2c1b
```

You can approve or request changes to a plan, merge or request changes to a pull request, answer a question, make a decision, discard a task, or reopen a closed one.

To see where things stand, run `/cve-remediation status` in the orchestrator chat. The orchestrator reports how many tasks are in each status, which ones are waiting on you, and when the last poll ran.

## Change the setup

Open the listener's chat and say what you want to change, or ask the orchestrator, which passes your request on. You can change the scanner, scope, severity floor, finding types, repositories, autonomy, pace, schedule, notes for fix droids, and how long to wait after a merge before a task needs a check. You can also add a second scanner, or pause, resume, or retire polling.

- After a change, the listener shows a new dry run.
- Changes apply from the next poll. Existing tasks keep their status, and a new autonomy setting applies at each task's next step.
- After you update Droid, ask the listener to update its installed files so new work uses the latest instructions.

## Security and access

<Warning>
  Factory doesn't enforce plan or merge approval. Fix droids are instructed to wait for you, but they merge with the source control credentials on the machine they run on. Branch protection is the control that stops a merge nobody reviewed. Before you start, turn on branch protection (protected branches in GitLab) for the default branch of every repository in scope, with required reviews and required status checks.
</Warning>

Fix droids are also instructed to never merge with an admin override, push to the default branch, or disable, skip, or weaken tests and required checks. If a repository has no checks at all, its pull requests go straight to **PR ready to merge**, so require status checks.

{/* sweep-allow: term-bullets */}

- **Scanner tokens.** Use a read-only token with the least access that works, preferably from a service account.
- **Source control credentials.** Run fix droids on machines whose credentials can't bypass branch protection, with no admin or bypass rights. A [Droid Computer](/droid-computers/overview) or an [environment](/environments/overview) signed in as a dedicated bot account keeps these credentials off your own computer. Describe that machine in your [Workstream Defaults](/workstreams/machines#choose-where-agents-run) so the orchestrator sends fix droids there.
- **Dependency scripts.** Fix droids install dependencies and run builds and tests to check each fix. Package installs can run scripts that ship with the dependencies. Run fix droids on isolated machines that hold only the credentials they need.
- **Untrusted content.** Fix droids are instructed to treat finding text, advisories, and pull request comments as data, not as instructions. Review every plan and pull request anyway.
- **Shared folder.** The listener's setup, the fix droids' instructions, and the task list live in the workstream's shared folder. Anyone who can write to it can change what the listener and fix droids do. Limit who can run agents in the workstream.
- **Shared sessions.** Anyone the orchestrator session is shared with can approve plans and merges from the Tasks tab. Share it only with people who should make those calls.
- **Requests from agents.** The orchestrator is instructed to refuse task actions that other agents send and to tell you about them.
- **Secrets.** Credentials stay in environment variables and CLI sign-ins. Fix droids are instructed to never put a secret in a plan, note, commit, pull request, or log.

## Known limitations

- Merging, closing, or reviewing a pull request directly on GitHub or GitLab updates the task only when its fix droid notices. Act from the Tasks tab when you can.
- **Accept risk** and **Defer** close the task for good. A date in the note, such as `until 2027-03-01`, isn't acted on. To revisit the task, select **Reopen**. Later polls don't create a new task for the same fix.
- Pace limits count open tasks across all repositories. There is no per-repository limit.
- A fix droid that stops responding isn't detected. If a task stays under **Agents working** for a long time, open the fix droid with **Watch session**, or ask the orchestrator.
- If any scanner in the setup can't be read, the whole poll fails and creates no tasks. The orchestrator tells you why, and the next poll tries again.
- Very large scopes can make every poll fail, because the listener stops paging through scanner results after a fixed limit. If that happens, narrow the scope.
- An installed skill named `cve-remediation` takes the place of the built-in one.

## Troubleshooting

<Troubleshooting>
  <TroubleshootingItem title="The orchestrator can't create the CVE remediation listener">
    The orchestrator shows the error it got. This usually means CVE remediation isn't enabled for your organization, or the workstream doesn't have listeners or a shared folder. Contact your Factory account team to request access.
  </TroubleshootingItem>

  <TroubleshootingItem title="The access check fails">
    The credential must be readable where the listener runs. Set the environment variable on that machine, or run `gh auth login` or `glab auth login` there, then reply in the listener's chat. For GitHub, check that the token can read Dependabot and code scanning alerts. For GitLab, check that the group or projects have GitLab Ultimate.
  </TroubleshootingItem>

  <TroubleshootingItem title="The orchestrator reports that a poll failed">
    The message includes the reason. Fix the setup in the listener's chat. The next poll tries again on schedule.
  </TroubleshootingItem>

  <TroubleshootingItem title="There's no Tasks tab">
    The tab appears only in the orchestrator of a workstream where `/cve-remediation` ran, after the first task exists. Check that the dry run found fixes and that you chose **Start**. You can also open the tab from **+** in the right pane.
  </TroubleshootingItem>

  <TroubleshootingItem title="A task stays in Waiting for rescan">
    The scanner hasn't scanned the repository since the merge. If it still reports the finding after the wait (3 days by default), the task comes back as **Needs a check**.
  </TroubleshootingItem>
</Troubleshooting>

<RelatedLinks>
  <RelatedLink href='/workstreams/tasks' title='Workstream Tasks'>
    Review tasks and act on the ones waiting on you.
  </RelatedLink>
  <RelatedLink href='/workstreams/overview' title='Workstreams'>
    How the orchestrator and its agents work together.
  </RelatedLink>
  <RelatedLink href='/workstreams/machines' title='Workstream Machines'>
    Choose where the listener and fix droids run.
  </RelatedLink>
  <RelatedLink href='/automations/security-review' title='Security Review'>
    Find vulnerabilities in pull requests and full-codebase audits.
  </RelatedLink>
</RelatedLinks>
