NCSC-FI RSS Processor — Design

Overview

Fetches vulnerability advisories from the Finnish National Cyber Security Centre (NCSC-FI / Kyberturvallisuuskeskus) RSS feeds and creates CVEMetadata rows (source=ncsc-fi).

Three feed groups are processed:

  1. vulns.xml — Structured vulnerability summaries with CVSS/CWE/CVE data. Items stored under their original CVE IDs (no VVD minting).
  2. fi/401 + en/401 — Alerts. FI primary with best-effort EN merge. Items get minted CERTFI-YYYY-N identifiers.
  3. fi/400 + en/400 — Vulnerability notices. Same pattern as alerts.

Feeds

FeedURLAuthFormatLangItems
Vulnshttps://www.kyberturvallisuuskeskus.fi/sites/default/files/rss/vulns.xmlNoneRSS 2.0 + Atom namespaceen-us~2-5 (rolling daily)
Alerts FIhttps://www.kyberturvallisuuskeskus.fi/feed/rss/fi/401NoneRSS 2.0fi~12
Alerts ENhttps://www.kyberturvallisuuskeskus.fi/feed/rss/en/401NoneRSS 2.0en~12
Notices FIhttps://www.kyberturvallisuuskeskus.fi/feed/rss/fi/400NoneRSS 2.0fi~25
Notices ENhttps://www.kyberturvallisuuskeskus.fi/feed/rss/en/400NoneRSS 2.0en~25

Parsing

vulns.xml — Structured CDATA

The <description> contains semi-structured data with two sections:

Header line (comma-delimited key:value):

Classification: Critical, Solution: Official Fix, Exploit Maturity: Not Defined, CVSSv3.1: 10.0, CVEs: CVE-2026-22557, CVE-2026-22558, Summary: ...

Per-CVE detail blocks (multi-line):

- Path traversal (CWE-22)
    CVSS:4.0/AV:N/AC:L/... Base Score 9.3
    CVE-2026-2251
FieldSource
CVE IDsRegex CVE-\d{4}-\d{4,} from header CVEs: key and body
ClassificationHeader Classification: value
SolutionHeader Solution: value
Exploit MaturityHeader Exploit Maturity: value
CVSS VectorsRegex CVSS:[234]\.\d/... from detail blocks
Base ScoreRegex Base Score[\s:]+([\d.]+) from detail blocks
CWE IDsRegex CWE-(\d+) from detail blocks
Affected ProductsText after Affected Products: heading

401/400 — Simple narrative items

FieldSource
Advisory URL<link> (fallback: <guid>)
Title<title>
Description<description>
CVE IDsRegex extraction from title + description
Published<pubDate> parsed as RFC 2822 → Unix seconds
Content HashSHA1(guid|pubDate|description)

ID Generation

vulns.xml

Items stored under their original CVE IDs (e.g., CVE-2026-22557). Multi-CVE advisories create one CVEMetadata row per CVE with cross-references as aliases.

401/400 feeds

Each advisory is assigned a CERTFI-{year}-{seq} identifier:

  1. On startup, query MAX(sequenceNumber) from GcveIssuance where gcveId LIKE 'GCVE-110-CERTFI-{year}-%'
  2. For each new advisory, increment the sequence counter for the advisory’s publication year
  3. Store CERTFI-YYYY-N as CVEMetadata.cveId and GCVE-110-CERTFI-YYYY-N as GcveIssuance.gcveId
  4. Link the advisory slug and any extracted CVE IDs as GcveAlias records

EN/FI Merge Strategy

FI is the primary language for 401/400 feeds. EN items are merged by URL slug matching:

  1. Extract language-neutral slug from URL (strip domain + /fi/ or /en/ prefix)
  2. Build map of EN items by slug
  3. For each FI item, look up EN by slug; copy EN title/description as merged fields
  4. Unmatched EN items become separate CERTFI records with lang=en only

Storage

No new tables or columns. All tables already exist.

Tablevulns.xml401/400 feeds
CVEMetadatacveId=CVE-YYYY-NNNN, source=ncsc-ficveId=CERTFI-YYYY-N, source=ncsc-fi
CVEDescriptionlang=enlang=fi + lang=en (when merged)
CVEMetadataReferencestype=advisory, referenceSource=ncsc-fitype=advisory, referenceSource=ncsc-fi
CVEMetricCVSS v3.1/v4.0 vectors
CVEProblemTypeCWE IDs
CVEAffectedVendor/product from text
CVEAffectedVersionVersion ranges from text
GcveIssuanceGCVE-110-CERTFI-YYYY-N
GcveAliasadvisory slug + CVE IDs as aliases

Incremental Strategy

On startup, load all sourceAdvisoryRef values from CVEMetadata where source='ncsc-fi' into a map[string]bool. Per advisory: if the URL is in the set and --all/--force is false, skip. After successful processing, add the URL to the in-memory set.

Flags

FlagDefaultDescription
--allfalseReprocess all advisories, not just new ones
--limit0Maximum advisories to process per feed group (0 = unlimited)
--forcefalseForce reprocessing even if content unchanged

ECS Schedule

Runs once daily (cron(0 6 * * ? *)).

Key Files

FilePurpose
cmd/ncsc-fi-rss-processor/main.goMain processor with multi-feed logic
internal/ncscfi/types.goRSS feed and advisory Go structs
internal/ncscfi/parser.goFeed parsing, CDATA extraction, EN/FI merge
internal/ncscfi/mapper.goAdvisory → CVESourceData mapping
schemas/ncsc_fi_rss_advisory.schema.jsonJSON Schema Draft 7 for parsed advisory object

S3 Persistence

Not used. This processor does not currently archive payloads or quarantine failures to S3. Per the S3 Persistence Contract this is non-compliant — see the compliance matrix for the implementation roadmap.

Expected paths when implemented:

  • Archive: ncsc-fi/files/{sha256}/{filename}
  • Quarantine: failed-feeds/ncsc-fi-rss-processor/{YYYY-MM-DD}/{reason}/{filename}
  • Likely reasons: parse-error