FJAN Logo

EPSS in Azure DevOps: Fix the Alerts Likely to Be Exploited

Publicatiedatum

Leestijd

10 minuten

Delen

Illustration of a large stack of dependency alerts filtered down to a few prioritized items on a board
Profile photo of Funs Janssen

Geschreven door Funs Janssen

Software Consultant

Ik ben Funs Janssen. Ik ontwikkel software en schrijf over de beslissingen die daarbij komen kijken: architectuur, ontwikkelmethoden, AI-tools en de zakelijke impact van technische keuzes. Deze blog is een verzameling praktische aantekeningen van echte projecten: wat schaalbaar is, wat misgaat en wat in blogvriendelijke voorbeelden vaak over het hoofd wordt gezien.

Open the Advanced Security Alerts page in most Azure DevOps organizations and you will find the same picture: hundreds of open dependency alerts, a worrying share of them marked High or Critical, and no clear owner. Sorting by severity does not help much, because when everything is urgent, nothing gets planned. The alerts sit outside the board, nobody estimates them, and the list quietly grows every time a pipeline runs.

The October 2026 sprint update gives you a better sorting key. Dependency scanning alerts now show an EPSS percentile, an estimate of how likely a vulnerability is to be exploited in the wild, and you can filter the Alerts page on it.

In this post I'll show you how to turn that number into a working process: a triage matrix you can copy, a way to get fixes onto the board as plannable work, a Definition of Done that keeps the backlog from growing back, and the platform quirks that catch teams out.

Why your dependency alert backlog keeps growing

Severity alone makes everything look urgent

Severity in Azure DevOps dependency scanning comes from the CVSS score in the GitHub Advisory Database. CVSS answers one question well: how bad would it be if someone exploited this? It does not answer the question your sprint planning actually needs: how likely is it that someone will?

The result is predictable. A transitive package with a Critical deserialization flaw that no attacker has ever touched gets the same red badge as a High in a web framework that is being exploited this week. Teams either try to fix everything and stall, or give up and fix nothing.

Alerts that live outside the board never get planned

The second problem is organizational. Alerts live on the Advanced Security tab in Repos. Your team plans from Azure Boards. Unless someone deliberately moves work from one place to the other, security fixes compete with nothing, because they never reach the backlog. "Fix vulnerabilities" ends up as a single vague task that rolls over sprint after sprint.

EPSS fixes the first problem. The rest of this post is about fixing the second.

What EPSS adds to Azure DevOps dependency scanning

EPSS vs CVSS severity in one minute

The Exploit Prediction Scoring System is maintained by FIRST and estimates the probability that a CVE will be exploited in the wild during the next 30 days. It is updated daily from real-world exploitation data.

  • CVSS / severity tells you the impact if exploited.
  • EPSS tells you the likelihood that exploitation is happening or about to happen.

They are complementary. A Critical with a tiny EPSS is a real risk you can schedule. A Medium with a very high EPSS can deserve attention this sprint.

Where the EPSS percentile shows up and how to filter on it

According to the Sprint 280 release notes for GitHub Advanced Security for Azure DevOps, the EPSS percentile now appears in the details of each dependency scanning alert, and the Alerts page gets an EPSS percentile filter with inclusive range boundaries. The feature rolls out over a few weeks, so it may appear in your organization slightly later than in others.

It works with either GitHub Advanced Security for Azure DevOps or the standalone GitHub Code Security for Azure DevOps, as long as the AdvancedSecurity-Dependency-Scanning@1 task runs in your pipelines.

What the percentile bands mean

The dependency scanning documentation on Microsoft Learn groups the percentile into four bands:

  • Very high: 90 to 100th percentile
  • High: 70 to 90th percentile
  • Medium: 50 to 70th percentile
  • Low: 0 to 50th percentile

A percentile is a ranking, not a probability. An alert in the 95th percentile is more likely to be exploited than 95% of all scored CVEs. That's exactly what you want for prioritizing, because it tells you where to look first.

A severity × EPSS triage matrix you can copy

Four tiers with fix-by deadlines

This is the matrix I recommend as a starting point. Adjust the deadlines to your release rhythm, but keep the structure.

  • Tier 1, fix now: Critical or High severity and EPSS Very high. Fix within the current sprint, ideally within days. This is the short list that matters most.
  • Tier 2, next sprint: Critical or High with EPSS High, or any severity with EPSS Very high. Plan into the next sprint.
  • Tier 3, scheduled: Critical or High with EPSS Medium or Low. Batch them into a regular dependency upgrade slot, for example one item per sprint or a monthly maintenance week.
  • Tier 4, routine hygiene: Medium and Low severity with EPSS Medium or Low. Handle them with normal package upgrades, without dedicated work items.

In practice, Tier 1 is usually a handful of alerts even when the total is in the hundreds. That's the point: the team gets a list it can actually finish.

Adjust for scope and exposure

Two filters on the same Alerts page sharpen the matrix:

  • Dependency scope: a runtime dependency in a public API is not the same as a development dependency used only during a build. Drop development-only dependencies one tier unless they touch your build agents or secrets.
  • Exposure: an internet-facing service deserves stricter deadlines than an internal batch job. If you have both in one project, use the pipeline or branch filter to separate them.

When EPSS is missing for an advisory

EPSS data is not available for every advisory, and Microsoft Learn notes that missing EPSS metadata does not change an alert's severity, state or remediation guidance. Don't treat "no EPSS" as "low EPSS". My rule: no score means you triage on severity alone, so a Critical without EPSS goes into Tier 2 until someone has looked at it.

It's also worth checking whether a CVE is on the CISA Known Exploited Vulnerabilities catalog. If it is, it goes straight to Tier 1, whatever its EPSS score.

From alert to work item: making the fix plannable

One work item per package upgrade, not per alert

The most common mistake I see is creating one work item per alert. Ten alerts often have the same fix: bump one package to a patched version. So group by fix, not by finding:

  • Title: "Upgrade System.Text.Json to 8.0.5 in OrderService (Tier 1)"
  • Description: the alert links, the EPSS percentile and severity at triage time, and the affected pipelines.
  • Acceptance criteria: the vulnerable version is no longer resolved in the dependency graph, the build passes, and the related alerts show as closed.
  • Tags: security, tier-1, so you can query and chart them on a dashboard.

Use a Bug or a dedicated work item type, whichever your process already uses for unplanned work. What matters is that it lands on the board with a deadline derived from the tier.

Getting deadlines into sprint planning without killing feature work

Tie the tiers to your planning ritual, as I described in Best Practices for Agile Sprint Planning in Azure DevOps:

  • Tier 1 items bypass the queue and are pulled into the current sprint.
  • Tier 2 items are mandatory candidates in the next planning session.
  • Tier 3 gets a fixed capacity slice, for example 10% of the sprint.

Visible, bounded capacity keeps product owners on board, because they can see exactly what security costs instead of watching it leak into every story.

Definition of Done for vulnerability fixes

What has to be true before a security item moves to Done

"Merged the upgrade" isn't Done. A vulnerability fix is only done when the risk is gone from what you ship. My checklist for a security work item:

  1. The vulnerable package version is no longer in the dependency graph of the default branch.
  2. The pipeline with the dependency scanning task has run on that branch after the merge.
  3. The related alerts show as Closed on the Alerts page, not dismissed.
  4. Any transitive dependency that still pins the old version is documented with a follow-up item.
  5. If an alert was closed as Risk accepted, the reason and the approver are recorded in the work item.
  6. The fix is deployed to every environment that ran the vulnerable version, or a follow-up exists for the ones that aren't.

Enforcing it with a checklist that blocks the state change

Writing this down in a wiki page doesn't make anyone follow it. This is the problem I built the Checklist Extension for Azure DevOps to solve. You define the security Definition of Done once as a reusable checklist template, attach it to your security work items, and configure it to block the transition to Done until every item is checked. Every check is recorded, so when an auditor asks how you know a Tier 1 alert was really resolved, the answer is in the work item history.

I wrote more about this pattern in Make "Done" Mean Done in Azure DevOps Boards.

A release-readiness gate for open Tier 1 alerts

The same idea works one level up. Add one line to your release checklist: "No open Tier 1 dependency alerts on the release branch, or a documented exception." It takes a minute to verify with the EPSS and severity filters, and it stops a release from shipping a known, actively exploited vulnerability. My release checklist guide for Azure DevOps shows how to set that up.

The gotchas I'd warn any team about

Alerts only close after the scanning pipeline runs on that branch

An alert closes automatically when the vulnerable component is no longer detected in the latest build of the pipelines that run the dependency scanning task. If your scanning pipeline only runs nightly, or only on main, a fix merged to a release branch won't close anything. Teams then think the fix failed, or worse, start dismissing alerts by hand. Make sure scanning runs on every branch you ship from.

Dismissing an alert closes it everywhere

Closing an alert as Risk accepted or False positive applies across all branches. That's convenient for a genuine false positive. For risk acceptance it's dangerous, because a decision made for one internal tool also hides the finding on the branch that feeds your public API. Microsoft's own guidance is not to dismiss alerts without a fix. My rule: risk acceptance needs a work item, an approver and a review date.

EPSS is a probability, not a guarantee

A low EPSS score means exploitation is unlikely based on current data. It doesn't mean impossible, and scores change daily. Re-run your Tier 1 and Tier 2 filters at the start of every sprint, not just once. A Tier 3 item can jump to Tier 1 overnight when an exploit is published.

Agentic Copilot Autofix is not a triage strategy

The same sprint update moved Copilot Autofix for code scanning alerts to an agentic foundation, but it's in limited preview and not accepting new participants. It also targets code scanning, not dependency upgrades. Useful to watch, but it doesn't replace the triage loop above. For broader guidance on controls for AI-written code, see my post on AI-generated code safety for DevOps leaders.

Conclusion: a short list your team can finish

EPSS doesn't make your alert backlog smaller. It makes it sortable. The real gain comes from what you build around it:

  • A severity × EPSS matrix that turns hundreds of alerts into a short Tier 1 list.
  • Work items grouped by fix, with deadlines tied to your sprint rhythm.
  • A Definition of Done that requires the scanner to confirm the fix, not just a merged PR.
  • A release gate that stops actively exploited vulnerabilities from shipping.

The weakest link in that chain is almost always the Definition of Done, because it depends on people remembering to check. That's the part worth automating. If you want security work items that can't move to Done until the alert is really closed, install the Checklist Extension for Azure DevOps. It's free, and you can have a security DoD template running on your board in a few minutes.

Frequently asked questions

Reacties

Nog geen reacties. Wees de eerste om te reageren.

Plaats een reactie

Profile photo of Funs Janssen

Geschreven door Funs Janssen

Software Consultant

Ik ben Funs Janssen. Ik ontwikkel software en schrijf over de beslissingen die daarbij komen kijken: architectuur, ontwikkelmethoden, AI-tools en de zakelijke impact van technische keuzes. Deze blog is een verzameling praktische aantekeningen van echte projecten: wat schaalbaar is, wat misgaat en wat in blogvriendelijke voorbeelden vaak over het hoofd wordt gezien.

Inhoud