Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
To run a vulnerability remediation campaign, pick one class of finding (a vulnerable dependency, a leaked secret pattern, an unsafe helper), define what "done" looks like, and scope every affected repository. Then work it to closure with named owners, a paced flow of fixes and a progress count.
A campaign replaces dozens of disconnected tickets with one objective. The point is not to close more tickets. It is to make one class of risk disappear and stay gone.
This guide covers what a remediation campaign is, when to run one, nine steps from scope to closure, a plan template and a checklist.
A vulnerability remediation campaign (sometimes called a security campaign) is a time-bound effort to fix one class of vulnerability everywhere it appears. It has a single objective, a defined scope, named owners, a pace, a deadline and a measurable finish line.
A typical objective reads like "upgrade every service off the vulnerable version of this library."
Here's how a campaign differs from the normal remediation queue:
| Ticket queue | Remediation campaign | |
|---|---|---|
| Unit of work | One finding | One class of finding |
| Goal | Close tickets | Reach a defined end state |
| Fix | Written separately each time | One fix pattern, applied everywhere |
| Progress | Ticket count | Resolved vs. remaining, by team |
| Ends when | Never | The end state is reached and verified |
Campaigns are worth the setup when the same fix applies in many places. Common triggers:
If a fix applies in one or two places, a normal ticket is faster. Campaigns pay off when you need to fix vulnerabilities at scale.
When several candidates qualify, start with the class that is actively exploited, reachable in deployed code and present in internet-facing services. Instance count matters less than exposure. A small campaign on a known-exploited CVE beats a large one on a theoretical finding.
Write the objective as a result, not an activity. "No production service runs library below version X" is an end state, and "work on dependency upgrades" isn't.
Run several narrow campaigns over time rather than one campaign for everything. A narrow campaign is easier to staff, track and finish.
Define the scope as a filter you can re-run: CVE or CWE, finding type, repositories, teams and severity. Then record the starting count.
The baseline is what makes progress visible. The re-runnable filter catches new instances that appear mid-campaign.
Before anyone writes a fix, remove noise from the scope. Check whether each instance is reachable, deployed and exposed, and whether it lives in test or dead code.
Decide up front how out-of-scope items will be recorded (accepted risk, not reachable, mitigated) and who signs off. Mitigations are not remediations, so track them separately and give them a review date.
A campaign moves fast when the fix is the same everywhere: one version bump, one shared helper replaced, one config change.
Prove the fix on two or three representative repositories first. Include at least one hard case, such as an old service, a pinned transitive dependency or a monorepo. Write down the pattern and the known exceptions before you roll it out.
Each affected repository or service needs an owner who will review and merge the fix. Take owners from CODEOWNERS, commit history and service metadata rather than guessing.
Name one campaign manager who answers questions, handles exceptions and reports progress. Don't move work across team boundaries without the receiving team agreeing.
Before the first fix lands, tell owners what is coming: the objective, why it matters now, the deadline, and who to ask. Developers review a PR they expected far faster than one that arrives unannounced.
The fastest way to fail a campaign is to open 200 pull requests in one afternoon. Developers learn to ignore a wall of PRs.
Instead, cap the number of open fix PRs per repository and per campaign, and take each owner's current workload into account when assigning. A steady flow that gets merged beats a flood that gets closed.
Each fix should arrive as a pull request with a minimal change, a short explanation of the vulnerability and what changed, and a passing build and test run. If the build fails, fix it before asking for review.
A human should approve every merge.
Report resolved versus remaining for the whole campaign and for each team, not just a ticket count.
Set an SLA for how long a campaign PR can sit unreviewed. Escalate stalls to the owner and their lead in the channel they actually read, with the risk and the deadline stated.
A campaign isn't done when the PRs merge. Re-scan to confirm every in-scope instance is gone, and confirm the fix is deployed, not just merged.
Then stop it coming back. Add a pull request check or rule that blocks the vulnerable version or pattern, and record what you learned for the next campaign.
Fill this in before step 4. If you can't fill a row, the campaign isn't ready to start. The example is illustrative.
| Field | Example |
|---|---|
| Objective | No production service depends on example-lib below 4.2.1 (CVE affecting request parsing). |
| Why now | The CVE was added to the CISA KEV catalog. |
| Scope filter | CVE = the listed CVE; all repositories; dependency findings only. |
| Baseline | 38 instances in 31 repositories across 6 teams. |
| Out of scope | Instances in archived repositories or unreachable test tooling, recorded with reason and owner sign-off. |
| Fix pattern | Upgrade to 4.2.1; for services pinned by framework X, upgrade the framework to its next minor version. |
| Owners | From CODEOWNERS and recent commit history; campaign manager named. |
| Kickoff | Message to all owners with objective, deadline and contact. |
| Pace | At most a few open campaign PRs per repository at a time. |
| Review SLA | Campaign PRs reviewed within an agreed number of business days, then escalated in Slack. |
| Deadline | Set from the risk tier and the number of instances in scope. |
| Done when | Re-scan shows zero in-scope instances and fixes are deployed. |
| Prevention | Pull request check blocks versions below 4.2.1. |
Campaigns are part of Nullify's security program management. Security leaders set the objective, the scope and how much Nullify automates.
Campaigns are built from real findings and can target code, dependency, secret and pentest findings, filtered by type, CWE, CVE, repository, team, individual or severity. Owners come from CODEOWNERS, commit history and service metadata, and assignment accounts for each person's open PRs and tickets.
Where a code change resolves the issue, Nullify opens a minimal, build-validated fix and manages it through review. It keeps at most 3 open fix PRs per repository and 5 per remediation plan.
Stalled PRs and tickets are escalated in Slack with the risk and SLA stated, and campaign reporting shows resolved versus remaining for each team. Developers approve every merge. Nullify never auto-merges.
A vulnerability remediation plan usually covers everything a team intends to fix over a period. A campaign is one focused part of that plan: a single class of vulnerability, a defined scope, owners and a finish line. Many teams run their remediation plan as a series of campaigns.
It depends on the risk and the number of instances. An actively exploited vulnerability in internet-facing services needs days. A dependency upgrade across dozens of internal services can run for several weeks. Set the deadline from the risk tier and the scope, then check it stays achievable as the campaign progresses.
Fewer than you think. Cap open fix PRs per repository and across the campaign, and consider each owner's current workload. A steady flow that gets reviewed and merged finishes sooner than a large batch that developers ignore.
Track resolved versus remaining against the baseline, by team, plus time to merge for campaign PRs. The campaign succeeds when a re-scan confirms zero in-scope instances, the fixes are deployed, and a pull request check stops the class from coming back.