Skip to content
Security

How we check, analyse and triage a CVE, and the 53-page handbook behind it

·11 min read

The short version

A new CVE gives you a number, a description and a clock. Most of what you need to decide is not in the number. We have written the method we use down as a handbook, and this note is its outline, with the parts you can use this afternoon.

Cover page of The CVE Triage HandbookFree with registration · 53 pagesThe CVE Triage HandbookFifty-three pages for whoever has to decide, today, whether a new CVE matters to their estate: the vocabulary, how to read a record, how to notice a change, how to find the precondition, a six-outcome triage method, mitigation, verification, the look-back, five scripts, six templates and four checklists. Free: register with a work email.Register to get the handbook →

1. Check: read the primary source, and all of it

Start from the vendor advisory for anything you run as a product, and from the CVE record for everything else. A single record can be fetched without credentials, and it holds more than the description: the assigner's affected spans, every CVSS block, and, in the ADP containers, what other publishers added. Three signals sit next to it: the CISA Known Exploited Vulnerabilities catalogue, the FIRST EPSS score, and the vendor's own severity.

Here is the whole of it for one CVE, from a 41-line script that uses only the Python standard library (it is in the handbook), run on 28 October 2026:

$ python3 cvecheck.py CVE-2026-46300
== CVE-2026-46300  state=PUBLISHED  assigner=Linux
   published 2026-05-23T11:44:02Z   updated 2026-10-28T19:59:23Z
   cvssV3_1: 7.8 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
   SSVC (CISA-ADP): Exploitation=active, Automatable=no, Technical Impact=total
   KEV: yes, added 2026-10-28, due 2026-10-31, ransomware=Unknown
   EPSS: 0.102 (percentile 0.957) on 2026-10-28

A local 7.8, and an exploitation notice with a three-day deadline. If you triage by the score, this one waits. If you read the signals, it is the most urgent item on a shared build host.

2. Notice when it changes

Triage has a half-life. The Adobe Commerce flaw was patched on 14 July and on the exploited list on 27 October. Dell revised its advisory twice in under three weeks. We keep a small watchlist and a script that fingerprints the fields that matter (state, every score, the affected spans, the references, the description, each ADP block) and prints only what changed. A quiet run is the normal result.

One trap worth knowing: the record's last-updated date moves when any publisher adds anything. On the kernel record it moved at 19:59 UTC on 28 October, and the CISA container carries the exploitation decision and the KEV marker, stamped 19:50. The likely reading is that CISA added to the record and the kernel project changed nothing. Read which container changed before you conclude the technical facts did.

3. Analyse: preconditions first, then versions

The most useful sentence in an advisory is the one that says when the flaw does not apply. The ten CVEs in the handbook contain all five kinds:

  • A configuration the operator chose: Kafka Connect, CVE-2026-115644, only when admin.listeners is set to a separate listener.
  • A lifecycle state: Juniper Session Smart Router, CVE-2026-103409, only before the device is onboarded.
  • A feature in use: jackson-databind, CVE-2026-110879, only with class-name polymorphic typing enabled. It is also an incomplete fix, so the earlier upgrade does not cover you.
  • A position the attacker must hold: the kernel flaw needs local code execution, and the VMware logging flaw needs an administrator.
  • A combination: MLflow, CVE-2026-116692, needs authentication state, a writable package directory and the affected version together.

Versions are spans, not a single number. jackson-databind's record lists three affected spans on the 2.x line with three fixes, and no fix at all for 2.19 or 2.20. The right test is membership in a span:

def parse(v): return tuple(int(x) for x in v.split("."))
def affected(version, spans):
    v = parse(version)
    return any(parse(lo) <= v < parse(hi) for lo, hi in spans)

JACKSON_2 = [("2.10.0", "2.18.12"), ("2.19.0", "2.21.8"), ("2.22.0", "2.22.4")]
# 2.18.11 True   2.18.12 False   2.19.3 True   2.21.8 False   2.22.3 True

And for anything with a kernel or a library inside it, installed is not running. Check uname -r after the reboot, and the version bundled in the runtime, not the one in the package manager.

4. Triage: five questions, six outcomes

We borrowed the structure of SSVC, the decision-tree approach from CISA and Carnegie Mellon, and made it small enough to apply by hand. The questions, in order, stopping as soon as one settles it:

  • Do we run an affected version, in a configuration the flaw needs? Yes, no or unknown.
  • Is it being exploited? Active, public proof of concept, or none known.
  • Who can reach it on our estate? Internet, internal, local untrusted, local trusted, or a precondition not met.
  • What does the attacker get? Control, data, disruption or minor.
  • Does the asset hold something we cannot afford to lose? Backups, identities, secrets, money.

The outcomes are NOT AFFECTED, FIND OUT, ACT NOW, THIS WEEK, SCHEDULED and TRACK. Not affected and find out are states rather than priorities. They are in the list so that no triage ends with an unrecorded shrug. Unknown is never allowed to stand: it means inventory the asset today and treat it as affected until you have.

Applied to the week's CVEs, the method disagrees with the scores in a useful way. A 2.7 on the exploited list earns an investigation of who held the administrator role. Four unauthenticated 9.8s on a backup appliance land at this week, with a firewall change available in an hour. A 9.8 in a service that never enables the feature is tracked, with the grep that proves it. The decisive fact is almost always about the estate, not the flaw.

5. Close: mitigate, prove, look back

Patching is the fifth rung, not the first. Remove the precondition, cut the reach, disable the component, add detection, then patch inside the deadline. For Fragnesia the stop-gap in our note is blocking the esp4, esp6 and rxrpc modules where you do not need them, with the removal written into the change record.

Proof comes in three strengths: the version, the configuration, the behaviour. Take the strongest you can afford and say which it is. And for a flaw that was exploited while your system was reachable, look back across the exposure window, using sources the attacker could not reach from the host. A flaw built to avoid being logged is why the host's own log cannot clear the host.

What is in the handbook

  • Part I, foundations: the vocabulary, how a CVE comes to exist and why the feeds disagree, and how to decode a CVSS 3.1 and 4.0 vector. The Adobe 9.6 against 9.3 difference turns out to be one metric, availability.
  • Part II, checking and updating: where to read what, a field-by-field reading of a real record, and watching for change.
  • Part III, analysis: preconditions, versions and ranges, and exposure.
  • Part IV, triage: the five questions, eight worked cases and the awkward ones.
  • Part V, acting: the mitigation ladder, proving a fix, and the look-back.
  • Part VI, running it: a thirty-minute daily routine, the records and the numbers worth keeping, and how to tell people.
  • Part VII, AI agents: what to hand one, what to keep, and the four questions that work on an assistant or an analyst.
  • Five scripts, six blank templates, four checklists, a glossary and the sources.

What we do not know, and what we did not do

The scripts fetch public data and compare it. They do not find affected systems on your estate, and nothing in them knows what your assets hold. The estate facts in the worked cases are hypothetical, marked as such, because the point is the reasoning. The outcomes are our triage judgements, not rulings on any vendor, and where a source did not render or two sources disagreed, the handbook says so. The claim that the kernel flaw chains with the PHP2Shell CVEs is in CISA's entry only; we found no independent write-up.

Read next

Does this touch your business?

A new vulnerability is a question about what you run, who can reach it and what it would cost you. If you want a person to answer it for your own systems, start with your IT setup.

Also worth reading

Start with one piece of work.

Give us one piece of work and read access to one system. We map it and come back with what we found, what we would build and what it would take — before any commitment. You decide using our output, not our pitch.

Talk to us