How is our attack-surface risk score calculated, and can we defend it? Inputs, aggregation functions, coverage, and the score that moves when nothing changed

DedSec Security Research ·

Picture a quarterly review where the attack-surface risk score is the first number on the slide, and it went down. Someone on the board asks the reasonable follow-up: what did we fix? In this hypothetical, the honest answer is not much. A discovery run attributed a batch of parked marketing domains to the organisation, none of them with a finding, and the score is an average. A mail host timed out on the last scan, and its findings dropped out. Two findings were marked as accepted risk. The one critical on the VPN gateway is still open.

Nothing was calculated wrongly. But nobody in the room could say which formula was used, so nobody could tell a reduction in risk from a reduction in the number.

This post is for whoever has to present a risk score, or defend one when an engineering team or an auditor pushes back. It covers the inputs, the unit problem of combining ordered categories, how each common aggregation function behaves and is gamed, double counting, coverage, trend, risk acceptance and explainability, and ends with questions to ask of any risk score.

It builds on earlier posts rather than repeating them: CVSS, EPSS and KEV for what each per-finding signal means, finding identity for deduplication, diffing discovery runs for "disappeared" versus "unreachable", suppression at scale for what a suppression decision is, the champions metrics post for Goodhart's law and composite scores, and evidence-attached findings for why a score that cannot be decomposed loses the argument.

Sources: the CVSS v4.0 specification and user guide published by FIRST (first.org/cvss). Every other number below is illustrative arithmetic on an invented estate, not a measurement of any organisation or tool, ours included.

What goes into a score

Attack-surface and application risk scores are usually built from four kinds of input, whatever they are called on the dashboard.

Severity and exploitability come from public feeds and scanner authors, exposure from observation, and criticality from you. Criticality is usually the input with the least data behind it.

The CVSS v4.0 user guide is direct about the first input. It has a section headed "CVSS Base Score (CVSS-B) Measures Severity, not Risk". A score that uses only CVSS is a severity summary with a risk label on it.

The unit problem

CVSS produces a number from 0.0 to 10.0, and the specification maps it to optional qualitative bands: None for 0.0, Low for 0.1 to 3.9, Medium for 4.0 to 6.9, High for 7.0 to 8.9, Critical for 9.0 to 10.0. Many risk scores work from the band, not the number, and then turn the band back into a number so it can be multiplied or added.

That second step makes an assumption nobody wrote down. The bands are ordered, and assigning them 1, 2, 3 and 4 asserts much more than order. It says the gap from Medium to High is the same size as the gap from High to Critical, and that two Mediums equal one Critical. Nothing in the bands says that; whoever wrote the scoring code chose it. Criticality scales and exposure tiers are the same: orderings encoded as numbers, and the product of two encoded orderings depends on the encoding.

A worked example, illustrative. Finding A is a Critical on a low-criticality asset. Finding B is a Medium on the most critical asset you have. Score each as severity × criticality.

Both encodings preserve every ordering the inputs carry, and they reverse the output. The priority came from the encoding, not the findings. Since the ranking decides what gets fixed first, the encoding is a policy decision and should be written down and reviewed as one.

Two consequences follow.

Multiplying a probability by an ordinal score gives a number with no unit. EPSS is a probability; a CVSS score is a severity rating, not a quantity of loss. The CVSS, EPSS and KEV post notes that FIRST's EPSS guidance says not to multiply the two. The product may still sort usefully, but should not be presented as "expected" anything.

Inputs can be counted twice without anyone noticing. CVSS v4.0 already has a place for asset importance: the Environmental metrics include Confidentiality, Integrity and Availability Requirements, and the specification treats those as High when they are not set. It also has Exploit Maturity, which is treated as Attacked when not set. So a score that takes a CVSS-BTE value and then multiplies by its own criticality weight counts criticality twice. A score that takes a base-only v4.0 value and then adds a KEV bonus is adding "exploited" on top of a number that already assumed the worst. These are bugs in the composition, not in CVSS, and only show up if someone lists which input enters where.

Aggregation: from many findings to one number

The per-finding number is the easy half. The hard half is turning thousands of findings across hundreds of assets into one figure for an asset, a business unit or the organisation. The aggregation function decides what the number rewards.

The worked estate for the rest of this section is invented. It has four assets. Each finding has been given illustrative points: Critical 10, High 5, Medium 2, Low 1.

That is fifteen findings and 41 points in total.

Already, under a per-asset sum, www (15) outranks vpn (10), although vpn carries the only Critical. Addition lets many small findings outweigh one large one. Sometimes that is right, since eight findings may mean a neglected host; the score never says which.

Sum

The organisation score is 41.

What it gets right. Every finding counts, closing any finding moves the number, and it decomposes exactly into assets and findings.

How it fails. It grows with the estate. Comparing business units by sum punishes the larger one, whatever its hygiene. Within a unit, the fastest way to reduce the sum is to make assets leave it: take them out of scope, stop scanning them, or let attribution drop them. And it depends on how findings are counted, as the double-counting section shows.

Mean

The natural fix for "sum punishes size" is to divide by the number of assets. The worked estate scores 41 / 4 = 10.25.

How it fails: dilution. Discovery attributes 36 more assets to the organisation, all parked or static, none with a finding. The estate is now 40 assets carrying the same 41 points. The mean is 41 / 40 = 1.025. The score fell by a factor of ten and nothing was fixed. This is the opening scenario.

It also runs backwards. Decommission 20 of those clean hosts, which is good hygiene, and the mean becomes 41 / 20 = 2.05. Retiring unused assets doubled the risk score.

A mean answers "how clean is a typical asset", not "how exposed are we". Attackers do not attack the typical asset.

Max

Take the worst single finding: the organisation scores 10, for the vpn Critical.

What it gets right. One reachable critical is one way in, and a thousand clean hosts do not close it. It cannot be diluted.

How it fails: it is blind to progress. Fix every finding on www, staging and mail, which is 15 + 14 + 2 = 31 of the 41 points, and the score is still 10. A team can do three quarters of the work and show no movement. In the other direction, one new critical anywhere sends the score to the ceiling on the day it is found, with no indication of whether it is one asset or forty.

There is a variant: max over per-asset sums, "our worst asset". It scores 15, for www. It has its own gaming route. Split www into two hostnames, one carrying the High and two Mediums (5 + 4 = 9), the other carrying one Medium and four Lows (2 + 4 = 6). The worst asset is now staging at 14. The score fell because a name was split.

Top-k

Sum the k worst findings. With k = 3, the worked estate scores 10 + 5 + 5 = 20. Close everything on staging, and the top three become the vpn Critical, the www High and a www Medium: 10 + 5 + 2 = 17.

It moves when the worst findings are fixed and cannot be diluted. But k is arbitrary and the tail is invisible by design: six Lows or six thousand score the same, and six thousand can mean a host nobody owns. With ties, closing one of several equal Highs may move nothing.

Weighted and saturating curves

A bounded 0 to 100 score usually passes an unbounded total S through a curve that flattens out. An illustrative one: score = 100 × S / (S + 20).

Doubling the exposure of the large estate moved the dial by 4, less than half of what a fourteen-point fix moved it in the small one. A saturating curve makes the marginal value of a fix depend on how much else is open. A team with a large backlog sees little movement for real work; that is the curve, not the team, and it should be stated on the dial.

What to do with this

No aggregation function is right; each answers a different question. "Is there any way in?" is max. "How much work is there?" is a sum of deduplicated findings. "How well is a typical asset maintained?" is a mean. A defensible score either picks one question and says so, or reports two or three numbers rather than folding them into one.

Double counting: when one problem is thirty findings

Every aggregation above assumes each finding is one problem. In practice the count depends on how a scanner and a deduplication step decide that two findings are the same one.

An illustrative case. Thirty hostnames sit behind one load balancer, and the load balancer offers a weak TLS configuration. An external scanner that tests each hostname reports thirty Medium findings: 30 × 2 = 60 points under the illustrative weights. The whole four-asset estate above was 41. One change to one configuration closes all sixty points.

Both counts are defensible: thirty is how many names reach the weakness, one is how many things need changing. A sum treats thirty as thirty times as bad unless somebody decides otherwise.

The same problem appears when two scanners report the same issue under different rule identifiers, when the same CVE is reported once per package path in a dependency tree, or when a scan re-reports a finding under a new key after a cosmetic change. The finding identity post covers how those keys are chosen.

So decide whether the score counts exposures (per reachable asset) or root causes (per thing to fix), say which, and ideally show both. And treat a change to deduplication as a change to the score: turn on cross-scanner matching or change an identity key and the total can drop overnight with nothing fixed. That total is more accurate, but it is a methodology change and belongs on the trend chart as one.

Coverage: an asset nobody scanned scores as clean

A score computed from findings has a blind spot built in. An asset with no findings scores zero, and so does an asset nobody looked at. Under every aggregation above, "unassessed" and "clean" are the same number.

Unknown is not zero. A defensible score keeps them apart:

A useful sanity check: if the score improves in a period where coverage fell, assume the improvement is the coverage until shown otherwise.

Trend: the score moved, but what changed?

A score can move for three different kinds of reason.

  1. The estate changed. Findings were fixed or introduced, assets were added or retired. This is the movement the team caused.
  2. The world changed. A CVE was added to KEV, a public exploit was released, or exploitation was reported. Nothing in your estate moved, but the risk genuinely did, because the threat picture did.
  3. The instrument changed. A scanner gained a new check, a rule pack was updated, an EPSS model version changed, an advisory was re-scored, a weight was retuned, deduplication was improved, coverage changed. Neither the estate nor the threat moved; the measurement did.

The first is the team's work. The second should move the score but not be credited or charged to the team. The third is noise for any trend, and a frequent source of a step.

When a new check ships and flags a large share of the estate, the score steps up overnight. Nothing was introduced; it was there all along and is now visible. A chart that shows that as deterioration tells the board the team did something wrong.

The remedy is to decompose the delta. An illustrative quarter on the four-asset estate, starting at 41:

The total is −14 + 5 + 7 + 10 − 2 = +6, so the score went from 41 to 47. Presented as one number, the quarter looks like a deterioration. Decomposed, the team closed fourteen points, five points were introduced, seventeen points became visible through better discovery and a new check, and two points left the score for the wrong reason, which should not have happened under the rule in the coverage section.

To make that decomposition possible, record with every score snapshot:

When the formula itself changes, either recompute history under the new formula, so the trend compares like with like, or mark the change on the chart as a vertical line and do not draw a single trend across it. The CVSS, EPSS and KEV post makes the same point about EPSS model versions: a time series across a version boundary shows a methodology change.

Risk acceptance and suppression take findings out of the score

Most scores exclude findings closed as false positive or accepted as risk. For a false positive that is correct, but it means a score can fall by decision rather than by fix, and those decisions need to be as visible as the score.

Three different things tend to share a "closed" bucket:

General practice for a score that will be challenged:

The suppression post argues that at scale the decisions are the work. They are also the cheapest way to move a score.

Explainability: what makes a score defensible

The evidence post puts it plainly: risk scores get challenged, and a score you cannot decompose loses that argument whether or not it was right. Defensible means three concrete things.

Decomposable. The organisation score breaks down into per-asset contributions, and each asset into per-finding contributions, and each finding shows the inputs that produced it: severity and its source, exploitability signals and their dates, exposure, criticality and who set it. A sum decomposes exactly. A max decomposes to one finding, which is useful and should be shown. A saturating curve does not decompose on the curve itself, so show the underlying total and the curve separately.

Reproducible. Given an export of the findings, assets, coverage and formula version, someone outside the tool should be able to recompute the score; otherwise it is an assertion. That is the most direct test of any vendor's score, ours included.

Stated. The definition fits on a page: which inputs, which encodings, which aggregation, what bounds, what is excluded and why, what coverage it rests on. That page is what an auditor can be handed. For a board, the useful slide is not the number alone, but the number with its coverage, the delta decomposition from the trend section, and the residual accepted exposure.

Why one number invites gaming

Goodhart's law, covered in the champions metrics post, applies to risk scores with particular force, because each aggregation function has its own cheapest lever:

None requires bad faith; they are simply the fastest moves, and a team measured on the number will find them. The defence is not a cleverer formula but putting the levers on the same page as the score: coverage, criticality changes, re-ratings and acceptances in the period, each with a name attached.

Where Maphra fits

In Maphra, risk is derived from the observed exposure and its reachability, and the inputs stay visible. Its risk scoring lists reachability-weighted scoring, trend over time and a per-asset breakdown, and its findings are deduplicated across scans and tracked through new, reopened and fixed states.

That is the whole claim. The encodings, aggregation functions, decomposition and record-keeping in this post are general practice, not a description of Maphra's formula. Nothing here claims which aggregation function Maphra uses, how it weights severity, exploitability or criticality, whether or how its score is bounded, how it treats unassessed assets, accepted risk or duplicates across hostnames, which versions it records per snapshot, or how accurate its score is. Put the questions below to us as you would to anyone else.

Questions to ask of any risk score

  1. Which inputs enter the score: severity, exploitability, exposure, criticality? Where does each come from, and who sets criticality?
  2. How are ordered categories turned into numbers, and who chose the encoding? Would a different, equally reasonable encoding change the top ten?
  3. Is any input counted twice, such as criticality in a CVSS Environmental score and again as a separate weight, or a KEV bonus on top of a base score that already assumes exploitation?
  4. Is any probability multiplied by an ordinal score, and if so, is the result presented as anything more than a sort key?
  5. What is the aggregation function, per asset and for the organisation? Which question does it answer?
  6. Can the score be improved by adding clean assets, retiring clean assets, splitting an asset or removing one from scope?
  7. Does the score count exposures or root causes, and what happens to it when deduplication rules change?
  8. How does an unassessed asset score? Is coverage shown beside the score every time it appears?
  9. What happens to an asset's findings when a scan of it fails or the asset does not answer?
  10. Are scanner, check set, feed snapshot and scoring formula versions recorded with every score snapshot?
  11. When a new check ships, is the resulting step marked on the trend, and can a period's delta be split into fixed, introduced, newly visible, re-rated and coverage?
  12. Do accepted risks and false positives leave the score? Are they shown as their own component, with owners, reasons, expiries and a second approver?
  13. Given an export and the formula, can someone outside the tool reproduce the score?
  14. Is there a one-page definition an auditor can be handed?

Back to the board. The score went down. Before anyone says risk went down, put coverage beside it, list the assets that joined the denominator, show the findings that left through decisions rather than fixes, and name the open critical on the VPN gateway. If that critical is the only thing that matters this quarter, a max would have said so all along. If the work done elsewhere matters too, show both numbers and say which question each one answers.

More about Maphra · All posts