Skip to content

Release 6.2.0

Latest

Choose a tag to compare

@cedricbonhomme cedricbonhomme released this 18 Sep 08:54
· 94 commits to main since this release
v6.2.0
75b7e27

This release requires four PostgreSQL migrations (poetry run flask --app website.app db upgrade).
One of them is not optional for instances serving the pub/sub stream: it grants the new
stream:subscribe permission to every role holding admin access, which seed-rbac alone
would not do. The Kvrocks CISA and CNW known-exploited feeders are retired: drop their
[feeder:cisa_known_exploited] and [feeder:cnw_known_exploited] sections from
config/modules.cfg and run tools/purge_kvrocks_kev_feeders.py to delete their keys.
Instances publishing as a GNA should run poetry run backfill_gcve_vulnid once to complete
their stored records with the amended GCVE-BCP-05 identifier placement (see docs/update.md),
and one index_vulnerabilities --source <local instance> pass to backfill the published index.
Instances importing FSTEC should run tools/fix_fstec_alias_links.py --yes to repair the
alias links written before the comma fix. pyvulnerabilitylookup >= 4.5.0 is now required. The
VARIoT feeder is disabled in the sample configuration, the service being unreachable.

Other-sources-tab-national-databases Other-sources-tab-OSV-sources FSTEC-card GCVE-extensions-panel Related-by-attack-behaviour

What's New

  • new: [vlai] Retrieval with the ATT&CK bi-encoder. Two similarity searches over the
    CIRCL/vulnerability-attack-technique-biencoder
    space served by ML-Gateway, which owns the vectors and the search (no ML dependency is added here):
    GET /api/vlai/attack-techniques/<technique_id>/vulnerabilities ranks the indexed vulnerabilities for
    one MITRE ATT&CK technique, and GET /api/vlai/related/<vulnerability_id> lists the vulnerabilities
    nearest to one record, searching on its stored vector or, when it is not indexed yet, on its
    description (the response says which). Both are labelled as similarity searches, not classifications:
    the vulnerability page gains a "Related by attack behaviour" block under the ATT&CK suggestions, and a
    new /attack/technique/<technique_id> page, linked from every suggestion card, lists the
    vulnerabilities for a technique with an "outside the trained vocabulary" badge when the model was not
    trained on it; a technique index at /attack/technique/ (navbar entry ATT&CK, backed by
    GET /api/vlai/attack-techniques/catalog, which needs an ML-Gateway that serves
    GET /retrieve/attack-biencoder/techniques) is the entry point. A new index_attack_embeddings consumer of the vulnerability channel keeps the
    gateway index in step with the feeders (batched, retried with backoff, bounded, never blocking a
    feeder), started by poetry run start when ATTACK_EMBEDDING_INDEXER is true in
    config/website.py; its batch size adapts to the gateway's pace (halved on a read timeout, grown back
    after consecutive full batches) so a CPU-bound gateway is never re-sent the same oversized batch forever.
    The existing corpus is loaded on the gateway host from the dumps. The classification head remains the
    default for CVE -> technique suggestions, and the ATT&CK tab links to the CVE-ATTACK working draft paper.
    5d7df2f7, 48163180, 6e0864e2
  • new: [metrics] Prometheus endpoint. An opt-in /metrics endpoint (WEB_MODULES["metrics"], off by
    default, with an optional METRICS_TOKEN bearer check and a 30/min rate limit) exposes what nothing
    else could alert on: Kvrocks memory, disk, RocksDB estimates and pending compactions; whether the
    derived state indexes still account for the corpus; EUVD identifier integrity from a stored
    euvd --integrity-scan snapshot that carries its own age; per-source ingestion freshness (last
    import, record count, whether a feeder is enabled, and the staleness threshold itself, so the alert
    rule lives in one place: METRICS_FRESHNESS_THRESHOLDS / METRICS_FRESHNESS_DEFAULT_SECONDS); and a
    request duration histogram and counter labelled by endpoint name and status class, kept in the cache
    Redis so a scrape reaching one of many gunicorn workers reads the whole traffic. Collectors are isolated
    so one failing cannot silence the others, the feeder-enabled gauge bridges modules.cfg section names
    to the source names feeders actually store under (including the per-GNA fan-out of gcve_vl), and the
    scrape is served no-store so an edge cache cannot freeze it. Documented in docs/metrics.md.
    Contributed by @archakisn in #615 and #630.
    2dbf7022, af0c8ed9, a823b659, 754e9b87, 6066ab92
  • new: [kev] Withdrawn KEV assertions (GCVE BCP-07 2.3). A producer can take back its own assertion:
    status_reason: withdrawn with exploited: false, kept as a tombstone rather than deleted so mirrors
    discover the withdrawal. Every exploited: false record is left out of the current KEV set: catalog
    pages, counts, the activity grid, coverage statistics, search badges and the API listing filter on it,
    the list endpoint's exploited filter becomes true (default) | false | all, and the remote sync
    asks for all so an upstream withdrawal reaches mirrors. The catalog page gets a "Show withdrawn"
    toggle; the entry page and the vulnerability header render a withdrawn entry as a muted tombstone
    pill with the withdrawal date instead of a red "listed as exploited" badge. The exploited sighting an
    entry carries is created only for asserted entries and removed on withdrawal. Administrators can
    withdraw an entry of any catalog from its page (the edit form stays confined to the local catalog,
    which now has an edit button on the entry page). The shipped BCP-07 schema is refreshed and enforces
    the invariant on POST and PUT. Requires a DB migration, which adds the enum member.
    6aafe800, e81188c8, cdeebb74, b734ecd4
  • new: [web] KEV catalogs page: evidence, profile and trends. Three new cards on /kev-catalogs read
    what the listings rest on. Evidence behind the listings: per catalog, the mix of BCP-07 evidence
    types (first-hand observation or reports) with the mean confidence and the share of entries without
    evidence; per vulnerability, the strongest signal any catalog attaches and whether observation and
    reports corroborate each other, which no single catalog can tell. Profile of the listed
    vulnerabilities
    : the age of a CVE when each catalog picks it up, how many entries preceded
    publication, the median days from publication to first listing by publication year, and the EPSS bands
    and critical CVSS share of each catalog's entries today. Weekly trends: the last 26 weeks as five
    small multiples (new listings per catalog, pre-publication listings, median lead time, evidence types,
    which catalog listed first), with configurable event markers drawn as a dashed line on every panel:
    the sample KEV_EVENT_MARKERS carries the CRA Article 14 reporting obligation applying from
    2026-09-11, the question this card is built to answer. The corroboration buckets and the
    pre-publication and low-EPSS sets filter the coverage table through new evidence and focus
    parameters. Everything is computed from one Kvrocks pass and cached for an hour.
    c6090770, 5afce95e, a9b4959e
  • new: [gcve] Amended GCVE-BCP-05 identifier placement, AI provenance extensions and relationships.
    GCVE-BCP-05, amended in September 2026, puts a record's identifier in the vulnId of its single
    recordType: advisory entry of containers.cna.x_gcve instead of the non-standard
    cveMetadata.vulnId. This is the compatible half of the migration: every reader (CNA API, GNA
    service, reindexers, full-text indexer, gcve_vl feeder, Vulnogram) accepts both placements, writers
    emit the new one and keep the legacy field, the GNA service refuses a record naming two different
    identifiers, and the dashboard's GNA cards count the records a remote GNA still publishes in the
    legacy shape so the deprecation can be timed on data. poetry run backfill_gcve_vulnid completes the
    stored records without bumping dates, rescoring indexes or notifying anyone. The record view renders
    the BCP-05 extensions GNAs now push in a folded "GCVE extensions" panel: BCP-05-X-01 AI annotations
    (level, review status, models table) and BCP-05-X-02 patch2vuln provenance (generator, patches,
    rationales, model comparison), any other extension key falling back to JSON. The Relationships row
    groups relationships under the x_gcve entry that states them, with the type as a badge carrying the
    BCP-05 verb definition, and GCVE records assigned by this instance's own GNA get a "This instance"
    pill. The gcve_vl feeder now tops the BCP-03 static feed up from the publication API on every run,
    newest first until a whole page is already indexed, so a publication reaches peers without waiting for
    the next dump (#631).
    c9b62a16, 0a76be62, ea33edb1, eb371218, c1f6c6dc, 5b472678
  • new: [gcve] Correlations through x_gcve relationships. The relationships a GCVE record states in
    its x_gcve entries now feed the {id}:link sets behind the "Related vulnerabilities" tab, both ways
    and for every relationship type, the source being the named srcId or the record itself. The
    gcve_vl feeder, the GNA service and the reindexer write the pairs and drop the ones a new edition no
    longer states; stored records pick the links up on --reimport or a reindex (#638).
    69509096
  • new: [gcve_vl] GCVE BCP-03 static feeds. The feeder reads a GNA's complete published history from
    its BCP-03 static feed, falling back to the pull API when the feed cannot be read, and the import loop
    was adapted to a lazily read full history: per-organisation error guards and last_updates stamps,
    text/html bodies rejected and the first record validated eagerly so a catch-all page is not
    imported as an empty feed, the producer's dump envelope keys stripped, records already indexed with
    the same modification timestamp skipped, conditional fetches on ETag / Last-Modified, and a stop
    request honoured mid-feed. A GNA that publishes only a dump directory (gcve_dump in the registry,
    as VULNARCHIVE does) is now pulled from it instead of being silently skipped. Contributed by
    @adulau in #619, with #632.
    ca8191d5, 364e3872, 0b628ebe, a9e213e2, 4da28e6b, 6290e1e9, 61db2209
  • new: [dashboard] GNAs tab. A fifth dashboard tab covers the GCVE side the way the CNA Roots tab
    covers the CVE Program: a card per feed (this instance's own GNA first, then the pulled ones) with the
    five latest publications, their vendor/product pairs and feed links, and a filterable table of every
    GNA in the signature-verified registry copy with its services and publication counts (#620).
    d0ff9a57
  • new: [website] Site notice. An administrator can announce maintenance or an incident from
    /admin/banner; the notice (bold, italic and link Markdown allowed) is rendered on every page and
    served by GET /api/banner, with PUT /api/banner for automation and /api/euvd/banner kept as an
    alias for the EUVD frontend. It is stored in a new app_settings table of administrator-set JSON
    documents, projected through one cache entry (APP_SETTINGS_CACHE_TIMEOUT) that the writes refresh,
    so a notice is visible on the next request; a failed read costs the notice and nothing else, and is not
    cached as an answer. The same store serves the EUVD curated assigner list (GET/PUT /api/euvd/assigners/names), an editorial subset rather than the derived set. Requires a DB migration.
    Contributed by @archakisn in #605.
    3897a629, 0a790e98, b7f0fa18, faf68e5a, e15be74d, f620d671, f4663f83
  • new: [euvd] Analytics figures. The EUVD statistics surfaces read prepared figures instead of
    recomputing them per visitor: an EUVD pass of index_vulnerabilities writes the per-source counters
    and leaderboards under the euvd source name (answered by the existing /api/stats/*?source=euvd)
    plus a CVSS distribution by band and version derived from each linked CVE record, and
    euvd --stats-scan stores an average and median time to exploit with its denominator, refusing to
    overwrite a good snapshot when nothing can be dated and naming which of the two causes it hit. The
    "first listed as exploited" date rule is unified on the KEV entry model as listed_at, so the KEV
    timeline and the EUVD exploited dates agree. Contributed by @archakisn in #626.
    faa2f503, 9eed2e08, fae01a48, 36ccf022
  • new: [dump] BCP-07 KEV export. The known-exploited export in dumps/ is the GCVE BCP-07
    serialization GET /api/kev returns, one record per assertion with its origin, evidence and status,
    every row included (a negative assertion is how BCP-07 expresses a retraction), ordered so two runs
    over unchanged data are byte-identical, with evidence fetched once rather than per entry. Every dump
    is now written to a temporary file and renamed over the destination, so a reader never sees a
    half-written file. Contributed by @archakisn in #604.
    7a312ec6, 683e0da0, 3497506d
  • new: [feeders] Three OSV sources. The Homebrew advisory database
    (BREW-<formula>-<upstream id> records for homebrew-core formulae, CC0), the
    openEuler security advisories (OESA records served
    from a plain HTTP directory with an all.json manifest, fetched conditionally and only for the records
    whose timestamp changed) and the BellSoft OSV database
    (BELL-CVE-* records for Alpaquita Linux, MIT). Their upstream ids are cross-linked by the generic OSV
    parser, and the release suffix is stripped from the ecosystem for the vendor keys (#645, #646, #652).
    6923d683, bcae490b, 44da60c5
  • new: [vuln] Withdrawn advisories. A source that publishes an advisory can retract it (GitHub
    withdraws a few percent of its advisories; the OSV feeds carry a top-level withdrawn timestamp),
    and the field was stored and shown nowhere. One badge macro now marks a withdrawn advisory wherever
    the record is drawn: its own page, the related-advisory lists, the /recent rows and the feeds, where
    an entry titles itself "Withdrawn: " since a withdrawal bumps modified and would otherwise
    arrive at the top as news. The marker matches the KEV tombstones. Contributed by
    @archakisn in #636.
    0bbf0c31, 7e84186b
  • new: [organization] What an organization published and assigned. An organization page carries the
    three relations it can have to a vulnerability as independently paged tabs: affecting the CPE names of
    its products, published as a GNA (from index:gna-n) and assigned as a CNA (from a new,
    administrator-set cna_short_name column, never derived from the name). Each list gets its Atom/RSS
    feed at /organization/<uuid>/recent.<format>?tab=..., and a product page advertises its own
    vulnerabilities as a feed at /product/<uuid>/recent.<format>. The organization, organizations and
    product views share one grammar and only render the facts a record carries, so a GNA-only
    organization is a compact card rather than an empty product grid. Requires a DB migration.
    15c6d32c, 5ceb28b4, fc3c3d7d
  • new: [search] An empty search form lists the selected sources' entries newest first instead of
    returning the bare form, reading the per-source date index the results are sorted by, so entries
    without a CVSS score are not dropped.
    dcdae5dd
  • new: [web] EUVD records minted by the allocator are rendered as what they are, an alias whose
    description, severity and references belong to the linked CVE, instead of an "Other source" raw JSON
    dump (#644).
    bb144b84
  • new: [doc] Generated OpenAPI snapshot. flask --app website.app dump_openapi renders the API
    reference straight from the API object: sorted so the diff only moves when the API does, freed of the
    producing instance's UUID, and refusing to write a partial reference when optional modules are off. A
    new openapi.yml workflow checks the snapshot on every pull request, and a pre-commit hook regenerates
    it. Array body arguments keep their items, enum, default and help text in the specification, which
    also unblocks the Sphinx build (#643).
    b4d574a8, 8162aba1, 1db2928d
  • new: [website] The landing page search placeholder is replayed letter by letter, then the field is
    focused; skipped under reduced motion or when the field is already in use.
    3fb449f0

Changes

  • chg: [website] Dashboard tabs reorganised. The Activity tab is gone: its Comments, Bundles and
    Sightings lists duplicated the Community pages and its KEV entries the KEV Catalogs page. What was
    only visible there gets a proper place instead. A Ghost CVEs tab lists the identifiers sighted in
    the wild but absent from the CVE registries (the former switch of the Vulnerabilities list and the
    "Unpublished advisories" option of the Trending tab), one row per identifier with its recent
    sightings, the sites that sighted it and its last sighting. An Other sources tab shows the latest
    records of every feed outside the CVE and GCVE programs (GitHub, PySec, OSV, national databases, CSAF
    providers...) as per-source cards grouped like the recent vulnerabilities page, the CVE Program mirrors
    being covered by the CNA Roots tab and the GNA feeds by the GNAs tab, with a toolbar to narrow the
    cards by group or by name. Dashboard tabs can be linked to with a URL hash (/#tab-ghost,
    /#tab-other-sources, ...).
    850f7a4e, b10a4450
  • chg: [feeders] Kvrocks CISA and CNW KEV feeders retired. cisa_known_exploited and
    cnw_known_exploited mirrored the two lists outside the BCP-07 model, append-only and keyed by meta
    uuid, so retractions stayed forever and re-minted uuids produced duplicates. The same lists are
    imported as BCP-07 catalogs by gcve-eu-kev, which reconciles retractions, so the feeders, their
    /api/cisa_kev/ and /api/cnw_kev/ endpoints (served by /api/kev/ filtered on
    vulnerability_lookup_origin) and their storage accessors are removed; the classic
    /known-exploited-vulnerabilities.{atom,rss} feed redirects permanently to
    /kev-catalogs/feed.{format}. tools/purge_kvrocks_kev_feeders.py deletes the leftover keys.
    pyvulnerabilitylookup 4.5.0 is the client release that stops calling the removed endpoint
    (#640, #641).
    f4125a28, 696889ef, 7b77262a
  • chg: [feeders] The VARIoT feeder and source are disabled in the sample configuration, the service
    being unreachable.
    2c51de8f
  • chg: [vuln] A vulnerability description is rendered with its own line structure (about one CVE in
    five wraps its lines, and advisories built from mailing-list posts carry headings and numbered steps),
    escaped character by character with bare http(s) URLs linked, and with the escapes HTML-to-text
    extraction leaves behind decoded once. Applied to the CVE 5 and FKIE NVD summaries.
    72542d7b
  • chg: [website] The FSTEC card is rebuilt on the model of the CVE one through a normalising filter:
    ISO dates, a link to the BDU record, status and KEV badges, severity chips with score, rating and
    calculator link for CVSS 2.0/3.x/4.0, linked CWEs, aliases linked to their records, collapsible
    references and impacted products, mitigations and credits, closed-vocabulary values translated.
    1d89524e
  • chg: [website] The generic OSV card, shared by the eleven OSV-format sources, has the header of the
    CVE one (icon badge, first upstream id, source and dates, withdrawn / VLAI / KEV badges) and the VLAI
    severity classification.
    36d90c97
  • chg: [notifications] Each scheduler cycle takes at most ten due notifications, longest-waiting first,
    and a run that started late by more than a quarter of its interval is re-anchored at a random earlier
    point, so the population due after a downtime drains over a few cycles instead of landing again in
    the same few minutes one interval later.
    fd6e8cc3
  • chg: [config] Every source of SOURCES_TO_SHOW whose identity is unambiguous has a one-line
    description.
    64bf8f7f
  • chg: [website] The user profile links to the credits page filtered on the user's name; the admin
    users list highlights the active filter.
    2cbac703, cbee4716
  • chg: [lint] The ruff rule set is pinned in pyproject.toml (E4, E7, E9, F): 0.16 widened
    the default set from four families to some forty, turning a clean tree into 1167 findings, and the
    pre-commit hook was pinned to a version that does not exist. CI and the hook take 0.16.7. The code
    base uses PEP 585 builtins and collections.abc instead of the deprecated typing aliases, and the CSAF
    and gcve_vl feeders stamp their backoff and last_updates values with timezone-aware datetimes.
    a5bc5960, f18c361d, 83a96ff7, fc4b7065
  • chg: [dependencies] Updated Python and GitHub Actions dependencies, including alembic.
    5dddd2c1, 2c07c321, 5d7a66f0, 208014be, 2a1b3965, 0a164da5, 815b06a3, ea025e06, 1b977b61

Fixes

  • fix: [web] CSAF advisories render to pages of bounded size. A SUSE or Red Hat kernel advisory
    repeated its full product table under every CVE (126,705 rows and 245 MB for one RHSA), inlined a
    36 MB raw JSON block, and a kernel CVE page embedding ten such advisories reached 680 MB, which is
    what Redis refused to cache: the recurring BrokenPipeError under flask_caching was Redis closing
    the connection on a value over proto-max-bulk-len, not a stale connection. A product list is now
    rendered once per advisory and capped at 1000 rows, raw JSON above 1 MiB is fetched lazily, related
    CSAF advisories are summary cards, and a BoundedRedisCache skips and logs cache writes above
    CACHE_MAX_VALUE_BYTES (8 MiB). Measured: 245 MB to 3.6 MB. The recent API clamps its page size to
    100 like the other listings.
    e8eff6d1, f915c374
  • fix: [website] Raw JSON blocks no longer emit anchors with the JSON tail glued on: the nvd and
    vulnrichment metas are decoded before pretty-printing so every URL ends at a real quote, and URL
    linkification is switched off in every raw JSON viewer, first on the vulnerability page and then on the
    six remaining call sites, with a regression test that sweeps the templates for any viewer without it.
    Googlebot was requesting advisory URLs such as .../VDE-2025-052/",/"source/":..., as reported by
    CERT@VDE (#648, #654).
    cae1aaa3, cbbf15c4, 1af685a1
  • fix: [web] The VEX badge and tab were hidden on every vulnerability carrying VEX metadata: a \/
    inside the lodash template's backtick literal made _.template() throw and abort the whole page
    script.
    938b2af4
  • fix: [web] CVSS vectors wrap between their metrics instead of overflowing the severity card on
    Chrome, with overflow-wrap: anywhere as the fallback for a segment too long for a line.
    ec1ba747, fd39016e
  • fix: [fulltext] Four defects that had frozen the production index: CSAF tracking ids carrying a colon
    (RHSA-2026:12282) failed every batch with invalid_document_id, so ids are sanitized on write and
    hits linked by the original; an index creation task never run by a stalled Meilisearch scheduler
    escaped the main loop and lost the buffer once per record, so creation is its own step remembered by
    task uid; the backpressure probe ran once per pub/sub message (one GET /tasks and one warning line
    every 5 ms during a burst), now at most every 5 s with the warning repeated at most once a minute; and
    the stringified CVE records the vulnrichment feeder publishes raised in the identifier accessor and
    restarted the indexer at every batch (before that they were indexed under the id "none"), so they are
    classified as an unknown format and a classification failure is isolated to its event. An instance
    that indexed before this release holds a none document in cve_list_v5 to delete once.
    98e9d043, bc85959d, 575a4e19, bf89921e
  • fix: [search] Meilisearch rejected the whole federated query when one index did not exist, and the
    nvd index never existed (its records are metadata on the CVE), so every search that ticked NVD
    returned nothing at all. The nvd source leaves the index mapping, and missing indexes are dropped from
    the query with a warning instead of failing everyone else's hits.
    968f716b
  • fix: [notifications] The report scheduler walked Kvrocks on the event loop inside the transaction
    opened by its reads, and PostgreSQL's idle-in-transaction timeout killed the connection under it
    ("KEV catalog lookup failed, hazard KEV policy skipped"); the exposure scheduler died the same way on
    its first UPDATE. Reads are committed away before storage work, the walks run on a worker thread, and
    the KEV lookups use a connection of their own. last_execution_time is stored as the naive UTC it
    claims instead of the session time zone's local time, so a notification no longer fires early or late
    by the server's offset (#639).
    d346aaf4, ca32c080
  • fix: [kev] A KEV entry failing the model's identifier pattern (vulnId: "UNKNOWN") answered 500;
    POST and PUT now answer 400 with the validator's message.
    8029b4d0
  • fix: [web] The entry table of a catalog ordered by status date with no tiebreaker, so on a
    bulk-synced catalog consecutive pages overlapped and skipped as many entries; both sort orders end on
    the uuid. The catalog page now carries the catalog's name and description, a summary strip (entries,
    last change, entries listed in the last 30 and 7 days, dominant evidence type, share also carried by
    other catalogs) and, per entry, the other catalogs listing it.
    7be603c3
  • fix: [index] index_vulnerabilities prunes the ids of a source index whose record is gone (deletions
    that predate the cleanup on delete), which inflated the entry count and pagination of /recent
    across every reindex (#629).
    326ef897
  • fix: [gna] The local GNA service never wrote index:{local}:published, so the dashboard's "This
    instance" card froze at the last reindex while /recent kept moving; it is written on every save and
    cleaned on delete. One index_vulnerabilities pass backfills the entries saved since the last reindex
    (#627, #628).
    fc35deea
  • fix: [euvd] The creation pass read absence from the published state index as "not published" and
    advanced its cursor past such CVEs for good, when it also means "not yet indexed". Unindexed candidates
    are parked and re-examined every pass, the feeder warns while any remain, and
    euvd --backfill-unminted mints for instances that already ran against stale indexes; the mapping
    export tests the rejected index instead of the published one (#597).
    e1eb5d27
  • fix: [monitoring] The heartbeat and log streams and /api/system/checkProcess read a single SCAN
    batch and dropped the cursor, so every process past the twentieth was missing from the monitoring
    page. The cursor is followed, values are read in one MGET or pipeline, the Redis client is bound once
    per stream, and the processes page shows a banner while a stream is down instead of stale timestamps
    that look live (#622, #623).
    bbce52b8
  • fix: [feeds] A feed carrying a text node over 10 MB fell back to raw XML for browsers (lxml
    Text node too long); it is parsed with huge_tree.
    1d6bab74
  • fix: [product] A product's vulnerabilities were found by walking the whole source index and listed
    once per matching synonym (30 cards twice and a total overstated by 18% for MISP). The candidates are
    read from the {vendor}:{product}:vulnerabilities sets and scored with ZMSCORE (0.30 s to 0.005 s),
    de-duplicated and ordered newest first; an organization matches the union of its products' pairs. Also
    fixed: a malformed ?since served a 500, cards showed an empty source, and the pagination line
    counted "organizations".
    6134666f
  • fix: [fstec] About one record in ten separates its aliases with commas, and the feeder split on
    spaces only, linking the BDU record to cve-2025-32414, and leaving 21k bogus link sets behind.
    Aliases are split on commas and whitespace, and tools/fix_fstec_alias_links.py repairs the stored
    links (62,465 links across 23,231 records on one instance). FSTEC dates, stored as dd.mm.yyyy, were
    blanked on /recent by the relative-date script.
    f279d035, 4332a8fd
  • fix: [vulnogram] A reserved identifier was inserted lowercase and failed the field's pattern; the
    Delete and Reject handlers were bound on the new-record page where the buttons do not exist, aborting
    the rest of the page script.
    a0c13ad8, 5b391970
  • fix: [tests] Session-level settings a test SETs (a time zone) no longer outlive it on a pooled backend,
    which made one CI matrix leg fail on every run; the KEV evidence and list assertions are scoped to
    their own catalogs; the VLAI retrieval tests patch the originals mypy can see.
    c78a3344, c2e2fb5b