onyx --method
Method
Methodology behind the trending score and the signals surfaced on the dashboard.
Trending score
Each CVE carries a trend score (higher = more urgent to look at). Rows from different sources are merged per CVE before scoring:
core = w_kev * (in CISA KEV ? 1 : 0)
+ w_epss * epss
+ w_poc * (public PoC or nuclei template ? 1 : 0)
+ w_template * (nuclei template ? 1 : 0)
+ w_severity * (cvss / 10)
buzz = w_advisory * min(1, advisory_feeds / 2)
+ w_chatter * min(1, log2(1 + mentions) / log2(21))
+ w_momentum * acceleration (mentions/day, last 3 d vs the 4 before)
surge = w_epss_delta * max(0, epss_change_7d) * 10 (24 h until a week of history exists)
score = core * 2^(-core_age / half_life) + buzz * 2^(-buzz_age / half_life) + surge
core_age counts from the CVE’s most recent dated signal — publication or KEV addition — so an old CVE that was just added to KEV still ranks. buzz_age counts from the newest advisory or mention, so an old CVE discussed today climbs back on its buzz points without one citation reviving its full KEV / EPSS weight. The EPSS surge is measured over the last 7 days, so it is current by definition and is not decayed.
History
Every render stores a daily snapshot of each CVE (score, rank, EPSS, signals) and each tech; EPSS history is backfilled from FIRST (~30 days) on a CVE’s first run. That feeds the timeline on each CVE page, Climbing fastest on the dashboard, each tech’s exploitation speed (median days from publication to KEV), and the signal check below.
Current weights:
- KEV:
30 - EPSS:
20 - EPSS delta (24 h):
5 - Public PoC (or nuclei template):
25 - Nuclei template bonus:
8 - Momentum (chatter accelerating):
5 - Severity:
1 - Advisories (Tier 1):
10 - Community chatter (Tier 2):
15 - Half-life:
30days (a score halves every 30 days) - Social window: mentions from the last
7days count
Tune in onyx.yaml (trending.weights.*, trending.decay_days, trending.social_window_days).
Signals
- KEV — listed in the CISA Known Exploited Vulnerabilities catalog: exploited in the wild.
- PoC — public exploit code: vulnx’s
is_pocflag, or a citation to Exploit-DB, Packet Storm, Metasploit, Nuclei templates or an exploit repository. - NUCLEI — a nuclei template exists for the CVE. It counts as public exploit code (a template is a verified, triggering check) and adds a bonus on top: internet-wide scanning for the bug is one command away, for defenders and attackers alike. A newly published template shows up under What changed and in the RSS feed.
- EPSS — FIRST’s probability of exploitation in the next 30 days; the delta compares with the previous day’s score.
- ADV (Tier 1) — the number of distinct CERT / vendor advisory feeds citing the CVE this week.
- Buzz (Tier 2) — community mentions this week: Hacker News stories and comments, Reddit posts, new GitHub repositories named after the CVE and, with a token, GitHub code search hits.
Sources
- vulnx (ProjectDiscovery) — primary CVE feed, queried per watchlist entry by CPE vendor/product, newest first.
- CISA KEV — matched to the watchlist on the catalog’s vendor/product fields.
- FIRST EPSS — daily exploitation-probability scores for every tracked CVE.
- nuclei-templates (ProjectDiscovery) — the CVE template index, downloaded each run; vulnx’s
is_templateflag fills in until a template lands in the index. - Advisory feeds (Tier 1) — CISA, CERT-EU, CERT-FR (alertes), CERT-FR (avis), Fortinet PSIRT, Cisco PSIRT, GitLab security releases, Drupal security, Node.js vulnerabilities, Django weblog, Ivanti security advisories, Kubernetes CVE feed, Wordfence (WordPress).
- Hacker News (Tier 2) — Algolia search, looked up per CVE: those already mentioned elsewhere, then the top of the ranking.
- Reddit (Tier 2) — newest posts of r/netsec, r/cybersecurity, r/sysadmin, r/blueteamsec (public RSS).
- GitHub (Tier 2) — new repositories named or described after a CVE; code search for the top CVEs when
GITHUB_TOKENis set.
CVEs that advisories or the community discuss but that no watchlist tech matches appear in Discussed, not on the watchlist on the dashboard.
Without a PDCP_API_KEY, vulnx rate-limits after a handful of queries; Onyx then resumes from where it stopped on the next run, so full coverage takes a few hourly cycles.
Cadence
Default cadence: hourly. scripts/ingest.sh and scripts/render.sh are idempotent. The site shows a warning when its data is more than two hours old.
Signal check
Which signals actually point at exploitation, measured on the 2912 tracked CVEs: precision is the share carrying the signal that CISA lists in KEV; lift compares it with the 4.9% base rate. Lead is the median number of days the signal appeared before the KEV listing, where both are dated (mentions are kept 30 days, so lead samples stay small until history builds up).