We recently used Jev to help classify requests hitting a Caddy-fronted website. That’s a fine, small use-case, but there are many more useful places we can leverage the capabilities of decision/system one models to make the lives of defenders much easier and more productive. One such place is triaging vendor patch releases.
Thanks to greedy bug bounty hunters, terrified internal product teams, and — most importantly — egregiously incompetent and clueless employees at (mostly U.S.-homed) foundation model providers (and the idiot vendors and so-called experts who took a quick buck from them to help teach their models all the tricks of the black hats), defenders have been deluged with a regular cadence of massive patch releases that need triage and addressing.
Take yesterday’s (2026-10-14) Juniper patch avalanche for Junos OS and Junos OS Evolved kit. The list has over 100 CVEs addressing a wide range of weaknesses with wildly varying raw severity levels.

The advisory says the issues came from “extensive internal product security testing,” which is the polite description of AI-assisted code review, and it is about to or has already become the new normal. Meaning, the same technique that finds one bug now finds fifty.
For comparison, Microsoft shipped over 1,400 patches on the 2026-10-13 Patch Tuesday avalanche, and Chrome Stable (weekly) releases are regularly shipping patches for over 200+ CVEs (at least you don’t have to think about those patches).
These avalanches break what used to be standard workflows. If your shop is on the low side of the maturity curve, you read the bulletin and patch everything scored 9.0 or higher, which often means you patch a lot of things that cannot affect you, and you may miss the sneaky 6.5 that actually can.
Why a CVSS score cannot make this decision
The score is computed for an abstract worst case, but your network is most assuredly not abstract.
Two examples from that same bulletin:
- CVE-2026-101990, CVSS 9.8. A flaw in the BGP routing daemon, reachable by a configured BGP peer. If you run no BGP, this is not a 9.8 for you. It is zero.
- CVE-2026-101992, CVSS 6.5. Only reachable by an attacker already on the same network segment. On a switch in a shared rack, that attacker is a real attacker.
So, just in the same bulletin we have opposite conclusions, all masked by a score we honestly should have retired ages ago. But, since we cannot have nice things, we do have said raw score with the missing input of what your environment looks like. That includes which features you run, which ones an attacker can reach, and what is exposed to the internet or other sensitive networks.
Many traditional vulnerability/exposure management systems do try to help you get closer to your internal ground truth, but most if not all fail miserably at it, and working through each CVE by hand does not scale to a hundred or a thousand or nearly two thousand CVEs per vendor per patch release.
A new approach: ask a model to score facts and let code help make your decisions
If you ask Claude (et al.) “is this critical?” you receive a wall of inauditable Claudish prose.
Instead, consider the following approach:
- Describe one CVE and your environments to a decision model.
- Ask it a fixed set of small factual questions, each with a closed answer.
- Apply your own rules to the answers, in code.
A “decision model” here means one built for this: it takes a set of typed questions and returns a typed answer plus a confidence score for each. Not a chatbot writing essays, but a structured answer you can threshold.
We’ll work through the setup and an example of what this might look like, but you will need to decide on your own what matters for your shop.
The questions
I assert we can start with five q’s about the CVE itself:
| Question | Answer |
|---|---|
| Does exploitation give code execution or root control? | yes/no |
| What is the worst impact if reachable? | one of six classes |
| What technique does it use? | one of nine classes |
| How hard is it to exploit? | score on a 5-level scale |
| Does reaching it require a non-default configuration? | yes/no |
and, two about each of your environments:
| Question | Answer |
|---|---|
| Does this environment run the specific feature or configuration this CVE depends on? | yes/no |
| If so, how does an attacker reach the affected component? | one of five levels, from “not reachable” to “reachable from anywhere, no authentication” |
We end up with eleven questions per CVE and one call to the model to provide scored answers that you post-process.
Doing it this way ensures that the verdict (i.e., patch/don’t patch) stays yours. The model never answers “should we patch this?”; it answers questions of fact, and your thresholds live in code you curate, so you can retune them without paying for another run.
Also, every decision is auditable, because each row records the tier, the rule that fired, and the answers behind it.
A missing answer is visible as well: if the model skips a question, the row is marked unresolved instead of quietly defaulting to “fine.”
Scenario-based answers
The model requires context of your environment to have even a remote chance of choosing and scoring the right answers.
If you have a large/diverse shop with Juniper kit spread across environments, we can define a scenario for each one and pass it along with the given CVE info from the advisory.
I made up three environments: an internet-edge service-provider router (BGP, EVPN, SRv6), a data-centre leaf/spine fabric (EVPN-VXLAN, MC-LAG, DHCP), and a branch firewall cluster (J-Web, SZTP, IPsec).
This is readable “pseudocode” that we’ll beef up with proper JSON in short order):
CVE: CVE-2026-101990
CVSS v3.1: 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
CVSS v4.0: 9.3 (AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:L)
Description: A Stack-based Buffer Overflow vulnerability in the routing protocol daemon (rpd) of Juniper Networks Junos OS and Junos OS Evolved allows a remote configured BGP peer to cause an rpd crash, or on Junos OS remote code-execution, via sending a flow-spec update which is selected for re-advertisement.
Components: bgp, rpd
Scenario S1: Internet-edge service-provider router
Release: Junos OS 24.4R2
Features: BGP, EVPN, SRv6, MPLS-TE, RSVP, LDP, IS-IS, MVPN, FlowSpec, BMP, NSR, BFD, rpd, SNMP, AAA
Management: Management plane reachable on a dedicated out-of-band network only; J-Web not deployed; SNMPv3 enabled; AAA via TACACS+.
Scenario S2: Data-centre leaf/spine fabric switch
Release: Junos OS Evolved 25.2R2
Features: EVPN, VXLAN, MC-LAG, ICCP, LLDP, l2ald, l2cpd, DHCP, OSPF, BFD, SNMP
Management: In-band management VLAN reachable from the server fabric; J-Web not deployed; SNMP enabled for telemetry; DHCP local-client in use.
Scenario S3: Branch firewall cluster
Release: Junos OS Evolved 23.4R2
Features: J-Web, SZTP, DHCP, IPsec, CFM, LLDP, 802.1X, snmpd, eventd
Management: J-Web published on the WAN interface for remote administration; SZTP DHCP provisioning in use; SNMP enabled; no full BGP table.
(We’ll come back to why you likely shouldn’t rely on vulnerability summaries from your vendor in a moment.)
The questions map that accompanies the scenarios looks like JSON, below (this text is the same in all 106 requests).
Expand for full request/question JSON
{
"S1_exposure": {
"type": "choice",
"instructions": "In scenario S1 (Internet-edge service-provider router), how does an attacker reach the specific component this CVE affects? If this scenario does not run that component, answer \"not_reachable\".",
"criteria": {
"adjacent_only": "the affected component is reachable only from a directly connected segment or peer",
"management_only": "the affected component is reachable only from the dedicated management network",
"not_reachable": "the scenario does not run the affected component, or the affected component has no network path from an attacker",
"reachable_authenticated": "the affected component is reachable remotely but requires valid credentials or peer authentication",
"reachable_unauthenticated": "the affected component is reachable remotely, from any peer, with no authentication"
}
},
"S1_feature_enabled": {
"type": "noul",
"instructions": "In scenario S1 (Internet-edge service-provider router, running Junos OS 24.4R2), is the specific feature or configuration this CVE depends on enabled?",
"criteria": {
"true": "the scenario runs the affected feature with the enabling configuration",
"false": "the scenario does not run the affected feature, or runs it without the enabling configuration"
}
},
"S2_exposure": {
"type": "choice",
"instructions": "In scenario S2 (Data-centre leaf/spine fabric switch), how does an attacker reach the specific component this CVE affects? If this scenario does not run that component, answer \"not_reachable\".",
"criteria": {
"adjacent_only": "the affected component is reachable only from a directly connected segment or peer",
"management_only": "the affected component is reachable only from the dedicated management network",
"not_reachable": "the scenario does not run the affected component, or the affected component has no network path from an attacker",
"reachable_authenticated": "the affected component is reachable remotely but requires valid credentials or peer authentication",
"reachable_unauthenticated": "the affected component is reachable remotely, from any peer, with no authentication"
}
},
"S2_feature_enabled": {
"type": "noul",
"instructions": "In scenario S2 (Data-centre leaf/spine fabric switch, running Junos OS Evolved 25.2R2), is the specific feature or configuration this CVE depends on enabled?",
"criteria": {
"true": "the scenario runs the affected feature with the enabling configuration",
"false": "the scenario does not run the affected feature, or runs it without the enabling configuration"
}
},
"S3_exposure": {
"type": "choice",
"instructions": "In scenario S3 (Branch firewall cluster), how does an attacker reach the specific component this CVE affects? If this scenario does not run that component, answer \"not_reachable\".",
"criteria": {
"adjacent_only": "the affected component is reachable only from a directly connected segment or peer",
"management_only": "the affected component is reachable only from the dedicated management network",
"not_reachable": "the scenario does not run the affected component, or the affected component has no network path from an attacker",
"reachable_authenticated": "the affected component is reachable remotely but requires valid credentials or peer authentication",
"reachable_unauthenticated": "the affected component is reachable remotely, from any peer, with no authentication"
}
},
"S3_feature_enabled": {
"type": "noul",
"instructions": "In scenario S3 (Branch firewall cluster, running Junos OS Evolved 23.4R2), is the specific feature or configuration this CVE depends on enabled?",
"criteria": {
"true": "the scenario runs the affected feature with the enabling configuration",
"false": "the scenario does not run the affected feature, or runs it without the enabling configuration"
}
},
"attack_complexity": {
"type": "score",
"instructions": "How hard is this CVE to exploit, assuming the affected feature is enabled?",
"criteria": [
"no special conditions: a single message reaches the bug",
"a single crafted packet, no prior state required",
"a specific protocol message with an established session",
"a specific non-default configuration must be present",
"multiple conditions must align before the bug is reachable"
]
},
"code_execution": {
"type": "noul",
"instructions": "Does successful exploitation of this CVE let an attacker run code or take full control of the affected device?",
"criteria": {
"true": "the CVE leads to command execution or root-level control",
"false": "the worst credible outcome is a crash, denial of service, or data disclosure"
}
},
"impact_class": {
"type": "choice",
"instructions": "What is the worst security impact of this CVE if it is reachable?",
"criteria": {
"code_execution": "the attacker can run code or gain root control on the device",
"denial_of_service": "the attack stops or degrades a service only",
"information_disclosure": "the attack discloses data without changing device behaviour",
"integrity_loss": "the attack changes device configuration or routing state",
"none": "no security impact is credibly reachable",
"privilege_escalation": "the attacker gains privileges it did not hold"
}
},
"requires_nondefault_config": {
"type": "noul",
"instructions": "Does reaching this CVE require a specific non-default configuration of the affected feature?",
"criteria": {
"true": "the feature must be configured in a particular, non-default way",
"false": "the bug is reachable under default or typical configuration of the feature"
}
},
"technique_class": {
"type": "choice",
"instructions": "Which technique does this CVE use?",
"criteria": {
"auth_bypass": "a check that can be bypassed",
"code_injection": "pre-authentication command or script execution",
"config_injection": "injection of configuration or protocol data",
"control_plane_crash": "a routing or control daemon crash triggered by peer traffic",
"data_plane_crash": "a forwarding-plane fault triggered by traffic",
"info_disclosure": "a read of data the attacker should not see",
"memory_corruption": "an out-of-bounds or malformed-packet memory bug",
"other": "a technique outside the listed classes",
"resource_exhaustion": "memory or CPU exhaustion"
}
}
}
All 106 CVEs were triaged against all three (so, 318 decisions per pass).
Sorting each CVE by its worst environment:
| Result | CVEs |
|---|---|
| Patch now | 29 |
| Schedule | 47 |
| Watch | 1 |
| Cannot affect this environment | 29 |
Split by environment, the same CVEs rank differently:
| Environment | Patch now |
|---|---|
| Internet-edge router | 18 |
| Branch firewall | 11 |
| Data-centre fabric | 2 |
In the code I built, there are six (patch_now has two gates) possible outcomes for each CVE+environment:
unresolved if a required answer is missing or mistyped (a gap, never a verdict)
not_applicable if the model reports the enabling configuration is absent here
monitor if the feature is present but has no attacker path
patch_now if reachable with code execution or unauthenticated remote access
patch_now if reachable on a critical CVSS v3.1 score (>= 9.0)
schedule if reachable but adjacent-only, complex, or non-default config
monitor otherwise (reachable, limited impact)
How good were the answers?
Scored against the official CVE records, on the 100 of 106 whose impact a keyword classifier could pin down (six were ambiguous):
- 18 CVEs that genuinely allow code execution: mean Jev confidence 0.93, lowest 0.82
- 82 that do not: mean 0.05, highest 0.09
I set the threshold at 0.50, which means nothing in the second group came close to it (i.e., zero false positives on this corpus).
If you were to just use raw CVSS scores as a rule (i.e., ignoring the environment, with “patch now” at 9.0+ and “schedule” at 7.0+), the outcome would agree with the environment-aware result on just 11 of 106.
Two “gotchas” to consider
Remember the “beware of vendor summaries” from above?
Three CVEs came back as denial-of-service only. They were not. The official CVE records say otherwise:
| CVE | The advisory said | The CVE record said |
|---|---|---|
| CVE-2026-101990 | ”causes an rpd crash" | "…or on Junos OS remote code-execution” |
| CVE-2026-102052 | ”causes jdhcpd to crash" | "…or execute arbitrary code with root privileges” |
| CVE-2026-33789 | ”results in denial of service" | "…and potential root-level command execution” |
Most advisory summaries compress what’s in the CVE record, and the model worked with what it had (lossy input) and initially produced some very “meh” results.
Switching the input to the official CVE record description fixed it (the final post-Jev scores for those three moved to 0.78, 0.94 and 0.84 from below threshold).
So, if you’re going to try this at home, feed the model the CVE record, not the one-line vendor bulletin entries.
You might be thinking “why not give the model both texts?” It turns out that is worse than giving it only the CVE record. The problem lies in the phrasing: the summary says “An attacker causes…” or “A BGP peer causes…”, while the record says “an unauthenticated adjacent-network attacker” or “a configured external BGP neighbor”. That vagueness pulls the model toward over-estimating who can reach the bug — and it changes real decisions. On the CVEs where the choice matters, swapping the input changed 27% of decisions — four times the 7% same-input floor measured on that same subset. So, the summary is most definitely not harmless extra context. If anything, it’s a serious distractor. Across the whole bulletin, the summary-only run put 59 decisions in “patch now”; the record-only run put 31.

Try this at home
- Build the environment profiles first. For each distinct role in your fleet — edge router, core switch, branch firewall, management server — list the features it runs and how each is reachable. This is the input that does the work. Be specific: “BGP” is not enough if the bug needs “BGP with automatic VPLS site-ID.”
- Feed the CVE record, not the bulletin summary. The record is longer and more precise. It costs about the same per call.
- Ask for facts, never verdicts. If you find yourself asking “is this critical,” you have moved the judgement into the model where you cannot audit it.
- Keep the thresholds in your own code. Then a policy change is a re-run of arithmetic, not another 100 API calls.
- Record the rule and the answers with every decision. When someone asks why a CVE was deferred, you need the reason, not just the verdict.
- Measure your noise floor. Re-run the identical input and count how many decisions move. On this corpus, re-running the whole bulletin moved 11 of 318 decisions — 3.5% — while a smaller 23-CVE subset moved 5 of 69 — 7% — because a smaller sample runs noisier. Any difference smaller than the floor is not a finding.
- Never let a missing answer look like an “all clear.” Make gaps loud.
The aforewlinked previous post (way up at the top) came with a fully working example project you can modify to work with this patch triage scenario (IMO it’s pretty straightforward).
If you are a defender in a modest-sized org or a researcher not affiliated with a SaaS/product vendor, you can reach out to me directly for the fully scaffolded project. I cannot justify releasing the project outright as I’m not willing to help security vendors for free.