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:
- vulns.xml — Structured vulnerability summaries with CVSS/CWE/CVE data. Items stored under their original CVE IDs (no VVD minting).
- fi/401 + en/401 — Alerts. FI primary with best-effort EN merge.
Items get minted
CERTFI-YYYY-Nidentifiers. - fi/400 + en/400 — Vulnerability notices. Same pattern as alerts.
Feeds
| Feed | URL | Auth | Format | Lang | Items |
|---|---|---|---|---|---|
| Vulns | https://www.kyberturvallisuuskeskus.fi/sites/default/files/rss/vulns.xml | None | RSS 2.0 + Atom namespace | en-us | ~2-5 (rolling daily) |
| Alerts FI | https://www.kyberturvallisuuskeskus.fi/feed/rss/fi/401 | None | RSS 2.0 | fi | ~12 |
| Alerts EN | https://www.kyberturvallisuuskeskus.fi/feed/rss/en/401 | None | RSS 2.0 | en | ~12 |
| Notices FI | https://www.kyberturvallisuuskeskus.fi/feed/rss/fi/400 | None | RSS 2.0 | fi | ~25 |
| Notices EN | https://www.kyberturvallisuuskeskus.fi/feed/rss/en/400 | None | RSS 2.0 | en | ~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
| Field | Source |
|---|---|
| CVE IDs | Regex CVE-\d{4}-\d{4,} from header CVEs: key and body |
| Classification | Header Classification: value |
| Solution | Header Solution: value |
| Exploit Maturity | Header Exploit Maturity: value |
| CVSS Vectors | Regex CVSS:[234]\.\d/... from detail blocks |
| Base Score | Regex Base Score[\s:]+([\d.]+) from detail blocks |
| CWE IDs | Regex CWE-(\d+) from detail blocks |
| Affected Products | Text after Affected Products: heading |
401/400 — Simple narrative items
| Field | Source |
|---|---|
| Advisory URL | <link> (fallback: <guid>) |
| Title | <title> |
| Description | <description> |
| CVE IDs | Regex extraction from title + description |
| Published | <pubDate> parsed as RFC 2822 → Unix seconds |
| Content Hash | SHA1(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:
- On startup, query
MAX(sequenceNumber)fromGcveIssuancewheregcveId LIKE 'GCVE-110-CERTFI-{year}-%' - For each new advisory, increment the sequence counter for the advisory’s publication year
- Store
CERTFI-YYYY-NasCVEMetadata.cveIdandGCVE-110-CERTFI-YYYY-NasGcveIssuance.gcveId - Link the advisory slug and any extracted CVE IDs as
GcveAliasrecords
EN/FI Merge Strategy
FI is the primary language for 401/400 feeds. EN items are merged by URL slug matching:
- Extract language-neutral slug from URL (strip domain +
/fi/or/en/prefix) - Build map of EN items by slug
- For each FI item, look up EN by slug; copy EN title/description as merged fields
- Unmatched EN items become separate CERTFI records with lang=en only
Storage
No new tables or columns. All tables already exist.
| Table | vulns.xml | 401/400 feeds |
|---|---|---|
CVEMetadata | cveId=CVE-YYYY-NNNN, source=ncsc-fi | cveId=CERTFI-YYYY-N, source=ncsc-fi |
CVEDescription | lang=en | lang=fi + lang=en (when merged) |
CVEMetadataReferences | type=advisory, referenceSource=ncsc-fi | type=advisory, referenceSource=ncsc-fi |
CVEMetric | CVSS v3.1/v4.0 vectors | — |
CVEProblemType | CWE IDs | — |
CVEAffected | Vendor/product from text | — |
CVEAffectedVersion | Version ranges from text | — |
GcveIssuance | — | GCVE-110-CERTFI-YYYY-N |
GcveAlias | — | advisory 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
| Flag | Default | Description |
|---|---|---|
--all | false | Reprocess all advisories, not just new ones |
--limit | 0 | Maximum advisories to process per feed group (0 = unlimited) |
--force | false | Force reprocessing even if content unchanged |
ECS Schedule
Runs once daily (cron(0 6 * * ? *)).
Key Files
| File | Purpose |
|---|---|
cmd/ncsc-fi-rss-processor/main.go | Main processor with multi-feed logic |
internal/ncscfi/types.go | RSS feed and advisory Go structs |
internal/ncscfi/parser.go | Feed parsing, CDATA extraction, EN/FI merge |
internal/ncscfi/mapper.go | Advisory → CVESourceData mapping |
schemas/ncsc_fi_rss_advisory.schema.json | JSON 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