Charlie KrugThe Build Log

← All posts

Every CVE ships a risk vector. CVE Radar reads it in English, no LLM.

CVE Radar turns the raw NVD feed into a skimmable bulletin: every CVSS vector decoded into one plain-English sentence by a pure function, no language model, so the static build stays reproducible.

Every disclosed vulnerability carries a string that looks like this: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. It is the CVSS vector, and it is precise: it tells you exactly how a bug is reached and what it costs you. It is also unreadable at a glance, which is a problem when the question in your inbox is "are we affected by this, and how much should I care?" That vector says: exploitable over the network, no privileges, no user interaction, full loss of confidentiality, integrity, and availability. In other words, drop everything.

What it is

CVE Radar is a normalization and presentation layer over the National Vulnerability Database feed. The NVD is the authoritative public record of software vulnerabilities, but it is built for machines: deeply nested JSON, four coexisting generations of CVSS scoring (v2, v3.0, v3.1, v4.0) that use different metric keys in the same feed, affected-product data buried inside cpe:2.3: match strings, and descriptions written for scanners rather than for the engineer triaging their morning. CVE Radar ingests recent CVEs, flattens every one into a single clean schema, and renders a fast, filterable static bulletin. It is for engineers and small-team admins who get asked "are we affected?" and have to answer without a dedicated threat-intel analyst.

The vector, decoded by a pure function

The headline feature is the plain-English sentence, and the interesting design decision is what generates it: not a language model, a pure function. The CVSS specification already defines exactly what each metric means, so the vector is a total description of the risk shape, not a summary that needs interpreting. AV:N is network reachability, PR:N is no privileges required, UI:N is no user interaction, C:H/I:H/A:H is full impact across the triad. CVE Radar parses those fields and assembles a sentence from them deterministically. Same vector in, same sentence out, every time.

That choice buys three things at once. The build is reproducible: rerun it and nothing shifts under you. It costs nothing and calls nothing: no API key, no per-request bill, no runtime dependency on a service that might change its mind. And it never hallucinates, because there is no room to: the sentence is a mechanical translation of a formal spec, not a guess. A model would be flashier and, for this job, strictly worse.

Taste in the messy middle

The part a naive "just wrap the API" clone gets wrong is the normalization. When a record carries multiple CVSS versions, which score do you show? CVE Radar applies a version precedence and prefers the Primary metric over any Secondary one, so the number on the card is the one NVD itself would lead with. Affected software comes from parsing CPE match strings into readable vendor and product tags. Records with no CVSS data at all degrade gracefully instead of crashing the batch, which matters because a live feed is messy: normalize.py is fully covered and fuzzed with Hypothesis, so a malformed record drops one card rather than the whole build.

The core is Python standard library only, urllib and json, no heavy dependencies. The frontend is hand-written HTML, CSS, and vanilla JS with relative asset paths, which is why it hosts under a subpath like apps.charliekrug.com/cve-radar with no server behind it. Filtering by severity, vendor, product, and free text all happens client-side over a bounded snapshot, so it is instant.

The honest limitation is right there in the word "snapshot." The build bakes a point-in-time slice of the NVD into one directory of static files; it is a bulletin, not a live monitor, and the footer says so and links back to the source. There is no alerting, no saved searches, and no EPSS or KEV enrichment yet. A static page that is honest about being point-in-time beats a dynamic one that pretends to be current and quietly goes stale.

Try it

Open CVE Radar, sort by severity, and read the top card's sentence before you read anything else: you will know in one line whether it is a remote no-interaction disaster or a local low-severity nuisance. Filter to a vendor you actually run, then copy the URL, the filtered view is a shareable deep link. If you would rather read the vector-to-prose logic than trust it, the code is on GitHub, and the build emits a clean data.json you can build your own thing on.

CVE Radar is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…