tenable-rss-processor

Status: Live Source: Tenable CVE RSS feeds Type: rss (two feeds: sort=updated and sort=newest) Source slug: tenable Schedule: see cron_match for tenable-rss-processor in scripts/task-manager.toml.

Overview

Tenable publishes its own reading of each CVE — severity, affected products and research notes — under its own TNCVE- identifier. That assessment is worth holding separately from the CNA’s, because the two disagree often enough that the disagreement is itself signal.

Two feeds are walked on every run:

  • ?sort=updated — entries whose analysis Tenable has revised
  • ?sort=newest — entries Tenable has just published

Walking both is deliberate. newest alone would miss revisions to existing analysis, and updated alone would lag on first publication.

Identifier policy: the record is keyed TNCVE-YYYY-NNNNN, formed by replacing the CVE- prefix on the upstream id. The original CVE-YYYY-NNNN is written as a CVEAlias edge through db.InsertAliases, so a lookup on either identifier resolves to the other. This is what keeps the source useful rather than a parallel namespace — a consumer querying a CVE sees Tenable’s view without knowing the TNCVE scheme exists.

Resume is content-based: a SHA is tracked per CVE in sourceFileHash, so a revised entry is re-read and an unchanged one is skipped. --force reprocesses everything.

Records produced

ConditionRecords
Every advisoryCVEMetadata (source="tenable", cveId=TNCVE-…), CVEDescription, CVEMetadataReferences
Advisory carries a scoreCVEMetricnot CVEMetadata.vectorString
AlwaysCVEAlias edge linking TNCVE-… to the original CVE-…

Where the CVSS lives

This processor writes CVSS to CVEMetric, and leaves CVEMetadata.vectorString NULL. Anyone measuring coverage from vectorString will read this source as having no CVSS at all, which is wrong: in production it holds 35,628 CVEMetric rows covering 34,438 of 34,714 CVEs (99.2%). CVEMetric is the authority for whether a record is scored; the scalar column on CVEMetadata is a denormalised convenience that several processors, including this one, do not populate.

Measured efficacy

A full production backfill on 2026-08-06 (just go-tenable-rss-backfill prod) processed 657 advisories with zero errors, taking the row count from 34,291 to 34,714. Post-run state:

MetricValue
Rows34,714
With a title34,714 (100%)
With sourceAdvisoryRef34,714 (100%)
CVEDescription rows34,714
CVEMetric rows / CVEs covered35,628 / 34,438 (99.2%)
CVEAlias edges from this source73,474
datePublished = 00
Content-free orphan rows0

Zero dateless rows and zero orphan rows are unusual across this fleet and worth preserving as a regression baseline.

Known issues

  • defaultTimeout = 30 * time.Minute is applied unconditionally rather than via internal/rundeadline.Soft gated on EXPECTED_DURATION_MINUTES. That violates the “backfill must not have a deadline” rule, so a local backfill can be truncated silently. The 657-advisory run above finished well inside the window, so it did not bite here.