Metadata-Version: 2.4
Name: patchrank
Version: 1.0.1
Summary: Rank Microsoft Patch Tuesday by evidence of real-world exploitation, not vendor severity.
Author: PatchRank contributors
License: MIT
Project-URL: Homepage, https://github.com/Ax1zz/patchrank
Project-URL: Repository, https://github.com/Ax1zz/patchrank
Project-URL: Issues, https://github.com/Ax1zz/patchrank/issues
Keywords: patch-tuesday,msrc,cve,epss,cisa-kev,vulnerability-management,sysadmin,security,patch-management,triage
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

<div align="center">

# PatchRank

**Patch Tuesday, ranked by who's actually being attacked — not by how scary the label looks.**

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Ax1zz/patchrank/blob/main/LICENSE)
[![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-3776AB.svg)](https://www.python.org/)
[![Dependencies: none](https://img.shields.io/badge/dependencies-none-brightgreen.svg)](https://github.com/Ax1zz/patchrank/blob/main/pyproject.toml)
[![Tests](https://img.shields.io/badge/tests-181%20passing-brightgreen.svg)](https://github.com/Ax1zz/patchrank/blob/main/tests/)
[![Status: stable](https://img.shields.io/badge/status-stable-brightgreen.svg)](#)

English · [Русский](https://github.com/Ax1zz/patchrank/blob/main/README.ru.md)

</div>

---

```console
$ patchrank triage --top 6

  PatchRank · 2026-Jul   6 of 2070 CVEs
  ACT NOW 4 │ SCHEDULED 2

SCORE  BAND       CVE               SEVERITY    CVSS      EPSS  SIGNALS   TITLE
   91  ACT NOW    CVE-2026-50522    Critical     9.8    75.76%  KEV       Microsoft SharePoint Remote Code Execution …
   87  ACT NOW    CVE-2026-58644    Critical     9.8     5.99%  KEV EXPL  Microsoft SharePoint Remote Code Execution …
   85  ACT NOW    CVE-2026-56164    Moderate     5.3    22.44%  KEV EXPL  Microsoft SharePoint Server Elevation of Pr…
   79  ACT NOW    CVE-2026-56155    Important    7.8     2.33%  KEV EXPL  Active Directory Federation Services Elevat…

   35  SCHEDULED  CVE-2026-50518    Critical     9.8    11.06%  -         Windows DHCP Server Remote Code Execution V…
   28  SCHEDULED  CVE-2026-55944    Critical     9.8     1.28%  -         Microsoft Dynamics NAV and Microsoft Dynami…

  sources: MSRC CVRF 2026-Jul  EPSS v2026.06.15  CISA KEV 2026.08.03
```

Look at row three. **Moderate. CVSS 5.3.** And in CISA's actively-exploited catalogue.

---

## The 30-second version

Microsoft's July 2026 security document contained **2,070 CVEs**. You have a morning to decide what to deploy first.

Everyone sorts by severity and starts with "Critical". Here is what that actually gets you, computed from the real data:

- **106 of the 117 Critical-rated CVEs (91%)** have an exploit probability under 1%, appear in no CISA exploitation catalogue, and are not flagged as exploited by anyone.
- **Four CVEs were confirmed exploited in the wild. Two of them were not rated Critical.** One was rated **Moderate**, CVSS 5.3.
- So a "Critical only" filter hands you 117 mostly-irrelevant items and **misses half the bugs attackers were actually using**.

That Moderate-rated one — `CVE-2026-56164`, a SharePoint privilege escalation in the CISA KEV catalogue with a 22% 30-day exploit probability — sits at position **#1090 out of 2070** if you sort by CVSS. And that is its *best possible* position, because 18 other CVEs share its exact score.

Nobody scrolls to #1090.

### It is not a one-month fluke

Run `patchrank verify` on any cycle. Four consecutive months, computed from live data:

| Cycle | CVEs | "Critical" with no exploitation signal | Confirmed exploited | ...of those, **not** rated Critical |
|---|---:|---:|---:|---:|
| 2026-Apr | 701 | 86% | 5 | **5 of 5** |
| 2026-May | 1,128 | 79% | 4 | **3 of 4** |
| 2026-Jun | 991 | 84% | 1 | **1 of 1** |
| 2026-Jul | 2,070 | 91% | 4 | **2 of 4** |

**Across those four cycles, 14 CVEs were confirmed exploited in the wild. Exactly 3 of them were rated Critical.**

In April, a "Critical only" filter would have missed *every single one*.

Severity answers *"how bad would this be if someone did it?"*. Triage needs *"is anyone doing it?"*. Those are different questions, and the free public data to answer the second one already exists — scattered across three feeds that nothing joins.

PatchRank joins them.

## Install

```bash
pip install patchrank
patchrank triage
```

No dependencies. Nothing to configure. No API key, no account, no telemetry.
The first run downloads the current cycle and prints a ranked list.

<details>
<summary>Run without installing anything</summary>

```bash
git clone https://github.com/Ax1zz/patchrank && cd patchrank
PYTHONPATH=src python3 -m patchrank triage
```

Python 3.9+ and the standard library are the only requirements.
</details>

## What it does

Joins three free, unauthenticated public sources and ranks the result:

| Source | Contributes |
|---|---|
| [Microsoft MSRC CVRF](https://api.msrc.microsoft.com/cvrf/v3.0/updates) | CVEs, severity, CVSS, affected products, KB numbers, Microsoft's own exploited / publicly-disclosed flags and Exploitability Index |
| [FIRST.org EPSS](https://www.first.org/epss/) | Probability of exploitation in the next 30 days, and percentile |
| [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) | Confirmed known-exploited catalogue, federal remediation deadline, ransomware-campaign association |

## Usage

```bash
patchrank triage                          # this month, ranked
patchrank triage 2026-Jul                 # a specific cycle
patchrank triage --top 10                 # just the ones that matter
patchrank triage -p "Windows Server 2022" # only what you actually run
patchrank triage -p Exchange -p SharePoint
patchrank triage --kev-only               # only confirmed-exploited
patchrank triage --min-epss 0.05          # only >5% exploit probability

patchrank explain CVE-2026-56164          # why did this rank here?
patchrank verify                          # re-run the analysis above yourself
patchrank weights                         # show the scoring model
patchrank products --grep Exchange        # discover --product patterns
patchrank months                          # list available cycles
patchrank cache                           # inspect / --clear the local cache
```

Month formats are forgiving: `2026-Jul`, `2026-07`, `Jul-2026`, `July 2026`, `202607`, or `latest`.

### Built to be piped

```bash
patchrank triage --format json  | jq '.vulnerabilities[] | select(.in_kev)'
patchrank triage --format csv   > cycle.csv
patchrank triage --format markdown --top 10 > change-ticket.md
```

Exit codes: `0` success · `1` runtime error · `2` bad usage · `3` nothing matched. So this works:

```bash
if patchrank triage --kev-only --format json > kev.json; then
    slack-notify "Confirmed-exploited CVEs this cycle" < kev.json
fi
```

### Justify the decision

Nobody gets to reorder a patch cycle without explaining why. Every score is fully attributable:

<div align="center">
  <img src="docs/media/explain.png" alt="patchrank explain output showing the signal breakdown for one CVE" width="780">
</div>

### Check the premise yourself

The claim this tool rests on is falsifiable, so it ships with the test. `patchrank verify` recomputes it against live data for any month. If Microsoft's severity ratings ever start tracking real exploitation, this command will say so and PatchRank will have argued itself out of a job.

<div align="center">
  <img src="docs/media/verify.png" alt="patchrank verify output analysing severity versus exploitation" width="800">
</div>

## The scoring model

Deterministic, documented, and weighted so that **evidence of exploitation always beats theoretical severity**. No model calls, no heuristics you can't inspect.

| Signal | Points | Why |
|---|---:|---|
| Listed in CISA KEV | +40 | Confirmed exploited. The strongest signal that exists. |
| KEV ransomware campaign use | +15 | Escalates from theoretical to operational. |
| Microsoft flags `Exploited:Yes` | +25 | Vendor confirms exploitation detected. |
| EPSS probability | +0…30 | `sqrt(EPSS) × 30` — square root so a 4% probability still registers instead of rounding to nothing. |
| Exploitability Index "More Likely" | +10 | Microsoft's own forward-looking forecast. |
| Publicly disclosed pre-patch | +8 | Exploit details already in the open. |
| CVSS base score | +0…12 | `CVSS × 1.2`. **Deliberately the smallest lever** — severity is the least predictive input. |
| Impact is Remote Code Execution | +3 | Mild adjustment for attack-chain value. |

One non-additive rule, and it earns its place: **a CVE with confirmed exploitation is never ranked below URGENT (50)**, whatever its severity. That is exactly the CVE-2026-56164 case — Moderate, CVSS 5.3, actively used against SharePoint servers. Pure addition would have filed it under "schedule it later".

Two invariants, both enforced by tests:

1. Confirmed exploitation always lands in `URGENT` or above.
2. **Severity alone can never reach `ACT NOW`.** A perfect CVSS 10.0 RCE with no exploitation evidence maxes out at 25 points.

| Band | Score | Meaning |
|---|---|---|
| `ACT NOW` | ≥ 70 | Exploited or near-certain. Emergency change. |
| `URGENT` | 50–69 | Strong exploitation signal. This cycle, prioritised. |
| `SCHEDULED` | 25–49 | Normal cycle. |
| `ROUTINE` | < 25 | Batch it. |

## How it compares

| | PatchRank | [MsrcSecurityUpdates](https://www.powershellgallery.com/packages/MsrcSecurityUpdates) | [PatchaPalooza](https://github.com/xaitax/PatchaPalooza) | [patch-tuesday](https://github.com/ghostbyt3/patch-tuesday) | Vendor blogs |
|---|:---:|:---:|:---:|:---:|:---:|
| MSRC data | ✅ | ✅ | ✅ | ✅ | ✅ |
| **+ EPSS probability** | ✅ | ❌ | ❌ | ❌ | sometimes |
| **+ CISA KEV join** | ✅ | ❌ | ❌ | ❌ | sometimes |
| Ranked, not just listed | ✅ | ❌ | partial | partial | prose |
| Per-CVE explainability | ✅ | ❌ | ❌ | ❌ | prose |
| Cross-platform | ✅ | PowerShell only | ✅ | ✅ | n/a |
| JSON / CSV / Markdown out | ✅ | HTML report | ❌ | partial | ❌ |
| Installable from a package index | ✅ | ✅ | ❌ | ❌ | n/a |
| Runtime dependencies | **none** | PowerShell + auth setup | several | several | n/a |
| Offline / air-gapped mode | ✅ | ❌ | partial | ❌ | ❌ |

*Verified against the GitHub and PowerShell Gallery APIs on 2026-08-03. PatchaPalooza: 222 stars, last release Feb 2026. patch-tuesday: 28 stars, active. Immersive-Labs-Sec/msrc-api: 119 stars, last push Jan 2024.*

The honest summary: several tools read the MSRC feed. **None of them join it to EPSS and CISA KEV**, which is the join that changes the answer.

## Why this exists

This keeps getting built and then abandoned. `Immersive-Labs-Sec/msrc-api` last moved in January 2024. Individual practitioners write a script for themselves each Patch Tuesday and stop maintaining it, because free public data supports no SaaS margin — too small for a company, right-sized for a maintained open-source tool.

Meanwhile the problem is getting worse, not better. From a practitioner writing about triage workflow:

> "Microsoft drops 150-plus CVEs on a Tuesday morning. By Wednesday afternoon, the security team wants a deployment plan, the change board wants a ring schedule, and someone in Slack is asking whether 'that CLFS thing' is the one they should care about. You have half an hour before the next meeting."
>
> — [patchdayalert.com](https://patchdayalert.com/blog/triage-patch-tuesday-30-minutes/)

And on the specific failure mode PatchRank exists to fix:

> "This is the phase most triage workflows skip, and it is where the actual exploited bugs tend to hide… None of those [exploited EoP zero-days] would have surfaced on a Critical-only filter. None would have made it on a CVSS-9-or-higher cut."
>
> — [patchdayalert.com](https://patchdayalert.com/blog/triage-patch-tuesday-30-minutes/)

Even the vendor's own forward-looking rating is under strain:

> "What this means is that our way of looking at Patch Tuesday has changed, because the exploitability index is centered around humans, not AI tools, and as these tools continue to improve, defense needs to improve alongside it."
>
> — Satnam Narang, Tenable, quoted in [Krebs on Security](https://krebsonsecurity.com/2026/07/microsoft-patches-a-record-570-security-flaws/)

Related discussion threads that informed the design: [r/sysadmin on patch approval workflow](https://www.reddit.com/r/sysadmin/comments/7d9hgk/patching_best_practices/), [r/patchmanagement](https://www.reddit.com/r/patchmanagement/). *(Linked as context — Reddit blocks automated access, so nothing from these threads is quoted here.)*

## Design decisions

**Zero dependencies, on purpose.** The target user is deciding whether to install something on a privileged workstation. A tool with no supply chain is easier to get approved. Even the coloured table renderer is hand-written rather than pulling in `rich`.

**It never touches a machine.** PatchRank is decision support. It has no deployment capability, needs no privileges, and makes no network calls beyond the three documented read-only feeds. That boundary is deliberate and permanent.

**Cached and air-gap friendly.** Everything is cached under `$XDG_CACHE_HOME/patchrank`. Warm the cache on a connected machine, copy the directory to an isolated change-management workstation, and run with `--offline`.

**Degrades loudly, not silently.** If EPSS or KEV is unreachable, you still get a ranked list, with a warning telling you which signal is missing. A worse ranking beats no ranking at Patch Tuesday 09:00.

## Not in scope

Deploying patches. Non-Microsoft vendors. A web UI. Inventory awareness (`--product` filtering is manual by design). Historical trend analytics. Writing to your ticketing system — emit Markdown or JSON and pipe it.

## Roadmap

- [ ] `--diff` against the previous cycle: what's new, what changed severity
- [ ] Optional NVD enrichment for CVEs missing a CVSS vector
- [ ] `--format sarif` for security-tooling pipelines
- [ ] Configurable weights via `~/.config/patchrank/weights.toml` for teams with a different risk appetite
- [ ] Adobe and Chrome advisory feeds behind a `--vendor` flag

## Contributing

See [CONTRIBUTING.md](https://github.com/Ax1zz/patchrank/blob/main/CONTRIBUTING.md). Two hard rules: the tool keeps zero runtime dependencies, and any change to the scoring model must come with tests proving the two invariants above still hold.

```bash
git clone https://github.com/Ax1zz/patchrank && cd patchrank
pip install -e ".[dev]"
python -m pytest
```

## Data sources and licensing

PatchRank reads only public data. MSRC CVRF is published by Microsoft; EPSS is published by FIRST.org under [their terms](https://www.first.org/epss/); the KEV catalogue is published by CISA as a US Government work. PatchRank is not affiliated with Microsoft, FIRST.org, or CISA.

## License

MIT — see [LICENSE](https://github.com/Ax1zz/patchrank/blob/main/LICENSE).
