Vulnerability-Lookup 6.3.0 released
We are pleased to announce the release of Vulnerability-Lookup 6.3.0!
This is a large release. Two new scorecards grade the actors of the vulnerability ecosystem on what they publish: the CNA and GNA scorecard ranks assigners on the quality of their records, and the KEV catalogue scorecard ranks the known-exploited-vulnerability catalogues on how they back, situate, lead and maintain their assertions. The vulnerability page gains a timeline of everything dated the instance holds on a record, and the sighting correlation graphs are redrawn with Pivotick. Vulnerability-Lookup reads and renders GCVE-BCP-05-X-03, the handling and disclosure timeline extension, and corroborates its exploitation events with the KEV catalogues and the sightings it holds. CNAs publishing through the platform get an immutable publication audit history. The exploit hazard aggregates are corrected for the Weibull model. Also in this release: single sign-on through OpenID Connect, a bulk lookup endpoint, GCVE-0 identifiers accepted everywhere, a syntax-highlighting detection rules viewer, a statistics page that can finally draw every figure the API serves, and richer /recent feeds.
Upgrade notes
- This release requires three PostgreSQL migrations (
poetry run flask --app website.app db upgrade): the publication audit table, the removal of the hand-administered EUVD coordinated assigner setting, and a hash index onsighting.sourcebuiltCONCURRENTLY, so it does not lock the sightings table. - Reindex after upgrading:
poetry run index_vulnerabilities --source cvelistv5(and--source gna-<id>for every GNA with records). The reindex now writes the CVSS distributions without withdrawn records, the assigner scorecard tallies and the per-source lifecycle indexes of the GNAs. Then, once the KEV catalogues are imported,poetry run euvd --stats-scan --stats-source <name>for every source you import. - Two new hourly passes belong in cron beside the statistics jobs:
poetry run scorecard --window-onlyandpoetry run kev_scorecard. See the prepared statistics figures, the scorecard and the KEV scorecard documentation. An instance that ran the EUVD figures of 6.2.0 also needspoetry run euvd --migrate-stats-keysonce. - The CSAF feeders now pass a
User-Agenttocsaf_downloader, which needs version 2.1.0 or later. - The CNA partner catalogue is read from the GCVE client’s copy: run
poetry run flask --app website.app update_gcve_registry(or the maintenance-page button) once. Thedata/cna_assigner_roots.jsonan earlier instance wrote is no longer read and can be deleted. - The detection rules viewer bundles highlight.js at
npm citime; the step needs Node.js 18 or later and is skipped with a warning on an older Node, the viewer then falling back to plain text. - Instances holding sightings imported before the deduplication fix should run
poetry run flask --app website.app dedup_sightings(a dry run), then again with--yes. - A new Python dependency,
joserfc, comes withpoetry install. Single sign-on, the wall of shame of the assigner scorecard and the classification gate are all off by default.
What’s New
KEV catalogue scorecard
A KEV catalogue publishes assertions, not records, so the question the scorecard asks is not “how well is the record filled?” but: how well is the claim backed, how early did it come, and is the catalogue looked after. Each catalogue is graded on four pillars of 25 points over a rolling window:
- Substantiate weighs the evidence by its provenance. Derivative evidence, which declares another origin or names another catalogue, earns a quarter for presence and nothing inherited; a catalogue whose evidence is one constant triple is templated, and its signal and confidence credits are halved.
- Situate reads the BCP-07 timestamps, characteristics, scope and references.
- Lead scores the first-to-list share and the median lag against independent catalogues only.
- Maintain scores retractions within 30 days, unknown status reasons and freshness.
Mirrors are inferred from the data rather than declared (containment of at least 90%, a median lag of two days or less, downstream first in at most 5% of cases) and excluded from lead and corroboration; on the public data, the one relation found is Previdian over CISA. A catalogue sharing nothing has no lead and is scored over the 75 assessable points. Listing dates read asserted_at before first_seen_at, which may be estimated retroactively.
The ranking lives at /kev-catalogs/scorecard, with the inferred mirror relations and the rules on their own tabs. Each catalogue has a card with the credit per rule, the pillars (a lead that could not be measured reads “not assessable”), the context figures, the catalogues it mirrors and the ones mirroring it, and its score per listing year, plus an embeddable badge. Every card of the KEV catalogues page now carries its grade, the cards are ordered by subscribers, and signed-in users subscribe to a catalogue straight from its card. The figures are served by /api/stats/scorecard/catalogues and /api/stats/scorecard/catalogue, and the methodology is versioned and documented in docs/kev-scorecard.md.

The KEV catalogues page: each card now carries its grade beside its entry and subscriber counts, the cards are ordered by subscribers, and the bell at the bottom subscribes or unsubscribes without leaving the page.

The catalogue scorecard on the public instance: CISA, Shadowserver and ENISA at grade C, CIRCL and Previdian at grade D, over the last 180 days. Each row ends with the catalogue’s embeddable badge.

One catalogue’s card: the credit per rule with the points it is worth, and beside it the context that is not scored. CISA’s evidence is templated, one constant type, signal and confidence on every entry, so its signal and confidence credits are halved.

The one mirror relation inferred from the public data: Previdian holds every CISA entry, the same day, and is first on 4% of them. A mirror and its upstream never compete for the lead.
CNA and GNA scorecard
Every CVE JSON 5 record is scored against thirteen published rules in four pillars (describe, scope, assess, act; 25 points each), on its CNA container only. Severity and weaknesses an ADP added later are counted apart, as an enrichment dependency, rather than folded into the score; severity is any severity statement (CVSS, SSVC or a vendor rating), not CVSS alone; the rules are graded rather than binary; and the reservation-to-publication delay is displayed but never scored. A CNA is the record’s assigner and a GNA is its own source, so one engine ranks both.
The figures are prepared by poetry run scorecard and by every reindex: per-assigner tallies, a rolling 180-day ranking in which an assigner needs ten publications to be ranked, a per-year history and a versioned methodology whose history is kept in the code and in the documentation. The ranking is at /assigners/scorecard, with a hall of fame and the rules; each assigner page shows the credit rate per rule, the context figures and the trend, and offers an embeddable grade badge at /assigners/<name>/badge.svg. The assigner line of every CVE and GCVE record now carries the grade (“CNA scorecard B · 89/100”), and the /assigners/ page opens with a chart of publications per year, the eight most active assigners drawn individually, a sparkline per assigner, and a closing list of the partner-catalogue CNAs that never published.
Each CNA is enriched with the CVE Program partner catalogue (organisation, country, root, partner profile), attributed only when the catalogue identifies exactly one partner. A wall of shame naming the assigners that trip a set of thresholded practices is behind WEB_MODULES["wall_of_shame"], off by default: naming publishers is a stance an instance takes deliberately. See docs/scorecard.md.
Vulnerability timeline
A new Timeline tab on the vulnerability page, right after Sightings, lays out everything dated the instance holds on a record: publication and updates, the KEV catalogue listings, the related advisories and every sighting, day by day. Busy days unfold on demand, two hundred days at a time. It loads from one cached page endpoint instead of fanning out over several rate-limited API reads, and sightings and KEV entries are matched under every spelling of the identifier, GCVE-0 and altId-only entries included. The same events are available as an event-level export at GET /api/vulnerability/<id>/timeline, with a JSON button on the tab.

The timeline of CVE-2026-88771: a Dutch NCSC CSAF advisory generated 402 days before the identifier was reserved, then the publication, the KEV listings and hundreds of sightings folded per day with a summary of what each day holds.

A smaller timeline, CVE-2026-58457: each KEV listing is followed within a minute by the exploited sighting the catalogue entry carries, Previdian on 2026-09-05 and Shadowserver on 2026-09-09.
Sighting correlation graphs redrawn with Pivotick
The correlation pane of the vulnerability page and the /sighting/<uuid>/correlations page are rebuilt on Pivotick. Both now draw a document the server builds in one window query each, served outside the API budget and also at /api/vulnerability/<id>/correlations and /api/sighting/<uuid>/correlations, instead of paging the sighting API’s substring search from the browser, which collided with the API rate limit and had no node ceiling.
Every node is a card. Sources carry their sighting count, nodes are coloured by the latest sighting type, a legend filters by kind and by type, tooltips show the sighting’s content and follow the site theme, fullscreen included, and a status line says what the caps left out.

The correlation graph of CVE-2026-88771 in its tab: 120 related vulnerabilities across 50 sources, the sources coloured by their latest sighting type. The status line says what the caps left out.

The same graph in fullscreen, with the tooltip of a Telegram source: one sighting of this vulnerability, a published proof of concept on 2026-10-08.

A sparser one, CVE-2026-58457: its sightings on Telegram, Bluesky and two KEV catalogues, one related vulnerability, and the content of a Bluesky post in the tooltip.
GCVE-BCP-05-X-03, the handling and disclosure timeline, is read and rendered
GCVE-BCP-05-X-03 lets a GCVE record carry the dated events of its handling process and, optionally, the regulatory notifications that branched off it: the EU CRA early warning, vulnerability notification and final report. Until now such a record rendered the extension as raw JSON.
A record carrying x_timeline now gets its own section of the GCVE extensions panel: the event count, the time to fix, exploitation and the publisher’s CRA assessment in the folded header; the spans (report to fix, report to disclosure, exploitation to protective measure), the event list and one block per regulatory framework with its notification table in the body. The reader keeps document order for ties, reads a bare date as midnight UTC and flags a span over it as approximate, reports a malformed member rather than failing, and keeps values outside the extension’s vocabularies as written, as the extension requires of consumers. dueAt and submittedAt are read but never compared into a verdict.
An exploitation-observed event is corroborated with what the instance holds, in both directions and deriving nothing: the record page lists the catalogues listing the vulnerability with their first-listed date and its distance from the observation, and the independent exploited sightings, KEV mirrors excluded; the KEV entry page says what the record states and how many days before or after the listing. When nothing else is on record, the note says so, which is the reading a CSIRT wants: a GNA recording exploitation nobody has listed yet. Every extension section now opens with the extension key, its title as gcve.eu publishes it and a link to its text. The first real record published with the extension, GCVE-1-2026-20307, is a test fixture.

The handling and disclosure timeline of GCVE-1-2026-20116, published by GNA-1 from two MISP commits: two fix-developed events, each linking the corrective change, under the patch provenance section of the same record.
CNA publication audit history
Every action on a publication (reserve, publish, update, reject, abort, delete) is recorded with its actor and the values before and after, and the rows are immutable: deleting a user keeps them. The publication page shows its fifty most recent entries and links to /publications/audit for the rest.
The publications list itself is reworked: one labelled primary action per row (Reserve, Publish, Update), a details link and a “more” menu for reject, abort and delete; status pills with counts replace the status select; and each action is shown only to users holding the permission its route enforces.
Improved exploit hazard
The local exploit hazard aggregates multiplied the summed instantaneous daily hazards by the horizon. That is exact for the exponential model, but under the age-dependent Weibull model the early-life hazard is not sustained across the horizon, so young vulnerabilities were overstated: about 1.6x for a day-old CVE over the 7-day report horizon, about 1.25x for a realistic weekly batch of zero-to-six-day-old CVEs.
Each instance now carries expected_events, the Weibull hazard integrated over [age, age + horizon], the same integral the per-item horizon_probability already used, and the aggregates (the subscription reports and the threshold alerts) sum that field. The instantaneous daily rate is still reported for threshold monitoring, and the payload keys are unchanged. Thanks to Laura Voicu for reporting and fixing it.

A notification report after the fix: the batch estimate and the standing exposure of a CPython subscription both now sum the per-instance Weibull integral over the 7-day horizon rather than the daily rate times seven.
Single sign-on with OpenID Connect
A “Sign in with …” button next to the password form runs the authorization code flow with PKCE, state and nonce, verifies the ID token against the provider’s keys, and matches an existing active, confirmed account by its verified e-mail address; nothing is provisioned. Local two-factor authentication still applies unless a configured claim proves the provider did it, and required claims (groups, tenant) gate sign-in. It is configured on /admin/sso/ or pinned read-only by OIDC_* keys in config/website.py, and off by default. The flow was hardened end to end before release: settings and provider metadata checked on every flow, no redirects followed, rate limits keyed on the real client, a flow redeemed once, a __Host- flow cookie over https, and a failed sign-in that tells the user why without logging out an existing session. Documented in docs/sso.md. Contributed by Jeroen Trappers.
Statistics: every figure drawn, scoped and dated
- The statistics page draws the four prepared figures that had no chart (severity distribution, CVSS version distribution, known-exploited inclusion, time to exploit) in a Composition of a source section with its own source picker, and is organised in named sections with a jump line.
/api/stats/vulnerability/statusand/api/stats/time_to_exploittake aperiod, and/api/stats/kevagranularityof year or month bucketed on the date a catalogue listed the record, so a year selector applies to every figure of the page. Time to exploit per publication year draws a mean and a median bar per year.STATS_KEV_CATALOGSscopes a source’s known-exploited figure to the catalogues it publishes; EUVD defaults to CISA KEV and EU KEV. The prepared figures record the scope they were measured over, so a change of configuration cannot be claimed over figures measured without it./api/stats/vulnerability/statusreports the current lifecycle state beside the date-based counts, which differ by roughly 12,000 withdrawn CVE records, and the severity distributions no longer count withdrawn records.- The exploited CVE ratio gets a stacked view and a monthly drilldown, both sides now bucketed by the publication date, and every chart shows the API query behind it. The rankings accept a limit up to 100.
euvd --stats-scanprepares the known-exploited share, the per-period series and the time to exploit for every source, GNAs included, andindex_vulnerabilitiesno longer empties a source’s derived keys before walking it, so the figures stay readable during a rebuild.
Most of this work was contributed by Nikiforos Archakis for the EUVD, and is documented in docs/statistics.md.
Bulk lookup and GCVE-0 identifiers
POST /api/vulnerability/bulk serves up to a hundred records in one call, read in one MGET with their metadata in three pipelined round-trips; a corrupt stored value is a null slot rather than a 500 that loses the other results, and with_linked is accepted for at most ten identifiers. GCVE-0 is the CVE namespace, and a GCVE-0-YYYY-NNNN identifier is now resolved at the storage layer, so it works wherever a CVE identifier does: every API route, search, and the comments, sightings and bundles filed under either spelling.
Detection rules viewer
The Detection rules tab syntax-highlights YARA, Suricata and TOML rules with the grammars of rulezet-core’s own code viewer, and adds a search box, a format filter, a page size control and pagination. Every rule page is fetched, and only a 404 reads as “no rule”: an outage surfaces as an error. Contributed by ecrou-exact.

The Detection rules tab of CVE-2026-19490: a Suricata rule from Rulezet, syntax-highlighted, with the search, format filter and page size controls above it.
And more
Feeds — The
/recentAtom and RSS entries carry the record’s own title, the affected vendor and product (CVE 5, GCVE) or ecosystem package (OSV sources) and the description, and are spelled as the record spells its identifier.Unknown identifiers — The page of an id no source holds states the three honest explanations (reserved, not imported yet, withheld), summarises the sightings recorded on it, links the assigner’s own page, and accepts comments again.

CVE-2026-92392, which no source holds yet: six sightings on Bluesky and Mastodon over three days, the upstream links to check, and the buttons to record a sighting or a comment in advance.
VLAI — Classification answers are cached for 30 days, so a text is classified once whoever views it, and
VLAI_CLASSIFICATION_REQUIRE_AUTHcan gate the two classification endpoints behind a session, an API key or the signed token the page sends with its own calls.Sightings —
export_sightingsandimport_sightingsmove sightings between instances as NDJSON, streaming on both sides;dedup_sightingsremoves duplicate observations; the API deduplicates URL sightings across days while keeping another author’s report of the same source; and the client’screation_timestampis stored again.EUVD — The EU CSIRT coordinated assigner list is mirrored from the table the CSIRTs Network publishes rather than administered through the API.
Administration — The maintenance page describes every action it offers and gains the registry update and the organization sync; the running version is shown in the footer of every page.
Changes
- CNA partner catalogue — Read from the GCVE client’s copy of
cna_partners.jsonon every registry refresh, the bundled snapshot is gone. - CSAF — The feeders identify the instance to the providers; Cisco refused the anonymous client with a 403 before a single advisory was requested.
- Dashboard — A shared URL carries the selected tab as
#tab-<name>. - Website — The topbar is sticky again, and the administration sidebar scrolls in a short window.
- Dependencies — Updated Python and frontend dependencies;
joserfcis added for the ID token verification.
Changelog
📂 For the full list of changes, including the fixes, check the GitHub release:
https://github.com/vulnerability-lookup/vulnerability-lookup/releases/tag/v6.3.0
🙏 A big thank you to all contributors and testers!
Feedback and Support
If you encounter any issues or have suggestions, feel free to open a ticket on our GitHub repository:
https://github.com/vulnerability-lookup/vulnerability-lookup/issues/
Your feedback is always appreciated!
Follow Us on Fediverse/Mastodon
You can follow us on Mastodon and get real-time information about security advisories:
https://social.circl.lu/@vulnerability_lookup/