KEV::Catalog
Inherits Indexable < Enumerable < Iterable < Enumerable < Reference < Object
The full CISA Known Exploited Vulnerabilities catalog.
A Catalog is Enumerable and Indexable over its vulnerabilities, so
the usual collection methods work directly:
catalog = KEV::Catalog.parse(File.read("kev.json"))
catalog.size # => 1592
catalog.first.cve_id # => "CVE-2021-..."
catalog.select(&.known_ransomware?).size # => 300+
Catalog#query returns a chainable Query for more elaborate filters.
Direct accessors are provided for the most common lookups (by_vendor,
by_cwe, find, []).
Constants
Column names CISA emits in the CSV feed, in source order. The CSV
has no metadata (catalogVersion, dateReleased, count), so callers
supply those via the catalog_version / date_released arguments —
defaults give a stable but obviously-synthetic identity.
Constructors
Parse the CSV form of the catalog. CISA publishes a CSV alongside the JSON feed; it carries the same per-row fields but no catalog-level metadata, so the metadata defaults are synthetic.
catalog = KEV::Catalog.parse_csv(File.read("kev.csv"))
Class methods
Non-raising parse — returns nil on malformed input.
Instance methods
Entries added on or after the given date.
Entries added on or before the given date.
All entries tagged with the given CWE (e.g. "CWE-79" or "79").
All entries for a given product (exact, case-insensitive match).
All entries for a given vendor (exact, case-insensitive match).
CISA-reported entry count — not necessarily equal to
vulnerabilities.size, since the value is what CISA reported at
publish time. Use size (or vulnerabilities.size) for the
in-memory count.
All distinct CWE codes referenced in the catalog, sorted by weakness
number — CWE-20, CWE-79, CWE-100. A plain string sort would
put CWE-100 ahead of CWE-20, which is not an order any reader of
a CWE list expects.
Compare two snapshots by CVE id. Returns a Diff describing what
was added, removed, or modified relative to self. The receiver is
the "before" snapshot; other is the "after".
Entries due within the given time span from now and not yet overdue.
"Not yet overdue" uses Vulnerability#overdue?, so an entry whose
deadline is today still counts as upcoming.
Look up by CVE id. O(1) after the first call (the CVE → entry index is memoised on first lookup).
The memo is built lazily and not synchronised — if you share a
Catalog across preemptive threads, call find (or any other
by-CVE lookup) once on the owning thread before publishing the
reference, or guard the catalog with your own mutex.
The memo auto-invalidates when the underlying vulnerabilities
array grows or shrinks (push/pop/concat). If you replace an entry
in place with a different cve_id, the size doesn't change and
the index won't notice — call reindex! explicitly in that case.
Group every entry by each of its CWE codes. A vulnerability with multiple CWEs appears under each one. Entries without CWEs do not contribute to the result.
The returned Hash carries no default block, so looking up a CWE that
is not in the catalog raises KeyError (and []? returns nil)
rather than quietly inserting an empty bucket.
Group by the typed RansomwareUse value. Entries with no
knownRansomwareCampaignUse (legacy rows) are grouped under nil.
Group every entry by cve_year. Years map to Array(Vulnerability)
in source-feed order (no sort on the inner arrays).
Appends a String representation of this object which includes its class name, its object address and the values of all instance variables.
class Person
def initialize(@name : String, @age : Int32)
end
end
Person.new("John", 32).inspect # => #<Person:0x10fd31f20 @name="John", @age=32>
The N most recently added entries (newest first). Ties on
date_added are broken by cve_id for stability.
The N oldest entries (earliest first). Ties broken by cve_id.
Entries whose remediation due date has already passed (relative to
now, default Time.utc).
Drop the memoised CVE → entry index. The next find / [] call
rebuilds it from the current vulnerabilities array. Use this
after in-place edits that change a cve_id without changing the
array's length.
Case-insensitive substring match across the user-facing text fields:
cve_id, vulnerability_name, short_description, vendor_project,
and product. Useful for a single "give me everything mentioning
log4j" hit without writing a custom where block.
Compute a Stats summary in a single pass. See KEV::Stats.
Catalog metadata title. CISA hardcodes this to "CISA Catalog of Known Exploited Vulnerabilities" in the current feed, but it is not
part of the schema's required fields, so we keep it nilable.
Emit the catalog in the canonical CSV form (matches CISA's CSV
mirror byte-for-byte except where input was synthesized — the CSV
has no metadata, so catalog_version / date_released are
discarded). Round-trips through Catalog.parse_csv.
JSON serialization in the exact shape of the CISA feed. Round-trips
cleanly via Catalog.parse(catalog.to_json).
Returns the element at the given index, without doing any bounds check.
Indexable makes sure to invoke this method with index in 0...size,
so converting negative indices to positive ones is not needed here.
Clients never invoke this method directly. Instead, they access
elements with #[](index) and #[]?(index).
This method should only be directly invoked if you are absolutely sure the index is in bounds, to avoid a bounds check for a small boost of performance.
Run Vulnerability#validate! on every entry. Use after constructing
a Catalog from non-feed sources (e.g. fixtures, partial JSON,
programmatic edits) to confirm schema-level shape compliance.
Raises on the first offending entry.