CERT-SE RSS Processor — Design
Overview
Fetches vulnerability advisories from Sweden’s national CERT (CERT-SE / cert.se) RSS 2.0 feed. CERT-SE is Sweden’s national computer security incident response team, operated by the Swedish Post and Telecom Authority (PTS). Advisories are published in Swedish and cover critical vulnerabilities affecting products widely used in Sweden and globally.
Records stored under original CVE IDs (source=cert-se). Items without CVE
IDs (guidance articles, weekly news bulletins, product-agnostic hardening
guides) are skipped. One CVEMetadata row is created per CVE ID found in an
advisory.
The mapper passes the advisory’s other CVE IDs as the record’s Aliases
(internal/certse/mapper.go:24-29), but db.InsertAliases deliberately
drops them: when the primary id is a CVE and the alias list carries more
than one CVE-prefixed entry, the list is treated as an upstream bundle rather
than as identity aliases, so no CVE↔CVE edge is written
(internal/db/cvealias.go:65-88). The only CVEAlias rows this processor
produces are the same-cveId cross-source edges linking (CVE-x, cert-se) to
every other source that already carries CVE-x.
Feed
| Property | Value |
|---|---|
| URL | https://www.cert.se/feed/rss.xml |
| Auth | None — fully public |
| Format | RSS 2.0 |
| Language | sv (Swedish) |
| Update frequency | Daily (sy:updatePeriod=daily) |
| Typical items | 10 most recent entries |
Content Types
The feed mixes two item types:
- Vulnerability advisories (~60% of items): contain one or more CVE IDs and a numeric CVSS score embedded in the Swedish-language description text. These are the only items stored.
- Non-vulnerability content (~40%): weekly security bulletins, hardening guides, threat-landscape articles. These never contain CVE IDs and are silently skipped.
Item Structure
Each <item> contains only core RSS 2.0 fields. No Dublin Core, media, or
<content:encoded> extensions are used.
| Field | Description |
|---|---|
<title> | Swedish-language headline, e.g. “Kritisk sårbarhet i Oracle Identity Manager” |
<description> | HTML-encoded <p> paragraph(s) with full advisory text in Swedish |
<link> | URL of the full article on cert.se (used as sourceAdvisoryRef) |
<guid isPermaLink="true"> | Identical to <link> |
<pubDate> | RFC 1123 timestamp with CET/CEST offset |
Reference URLs are not present in the RSS feed. The description contains
numbered citation markers ([1], [2], …) that link to vendor advisories,
but the actual URLs are only available in the linked HTML article, which this
processor does not fetch.
Parsing
CVE Extraction
CVE IDs appear in the description text, typically inside parentheses:
(CVE-2026-21992)— most commonCVE-2026-3055— bare, without parentheses
Regex: CVE-\d{4}-\d{4,}
CVSS Extraction
Numeric scores only — no CVSS vector strings in the RSS feed. Three Swedish phrasings are observed, matched in priority order:
| Pattern | Example |
|---|---|
| Score then version | klassning på 9.8 enligt CVSS 3.1 |
| Version then score | CVSS v4.0-klassning på 9.3 |
| Version then score (space sep.) | CVSS v3.1-klassning på 8.8 |
A fallback regex catches any CVSS mention near a decimal number for future
phrasing variations. When the CVSS version cannot be determined from the text,
the metric type defaults to cvssV3_1 (the most common version seen in the
feed).
CVSS version → MetricType mapping:
| Version prefix | MetricType |
|---|---|
4.* | cvssV4_0 |
3.1 | cvssV3_1 |
3.0 | cvssV3_0 |
2.* | cvssV2_0 |
| (unknown) | cvssV3_1 |
Date Parsing
<pubDate> uses RFC 1123Z format with named or numeric timezone offsets
(+0100, +0200). Multiple formats are tried in sequence to handle CET/CEST
variation and any malformed entries.
Advisory ID
The last path segment of the article URL, with .html suffix stripped:
https://www.cert.se/2026/03/kritisk-sarbarhet-i-oracle-…html
→ kritisk-sarbarhet-i-oracle-…
Content Hash
SHA1(link|title|pubDate) used for idempotency tracking (sourceFileHash).
Storage
No new tables or columns.
| Table | Rows inserted |
|---|---|
CVEMetadata | One per CVE ID; source="cert-se" |
CVEDescription | One per CVE; lang="sv" |
CVEMetadataReferences | One row: the cert.se article URL (type="advisory") |
CVEMetric | One row per CVE if CVSS score present (containerType="cna"), plus the pipeline’s derived containerType="vulnetix" CVSS v4.0 row when a description is present |
CVEAlias | Same-cveId cross-source edges only (co-listed CVE↔CVE aliases are suppressed — see Overview) |
No ProblemTypes (no CWE IDs in feed), no Affected (no product/package data).
Incremental Strategy
URL-based deduplication via sourceAdvisoryRef in CVEMetadata. At startup
the processor loads all known cert-se advisory URLs from the read replica into
a map. Items whose URL is already present are skipped unless --all or
--force is set.
Flags
| Flag | Default | Description |
|---|---|---|
--all | false | Reprocess all advisories including already-stored ones |
--limit | 0 | Maximum advisories to process per run (0 = unlimited) |
--force | false | Force reprocessing even if content is unchanged |
Error Handling and Alerting
- Feed fetch retried up to 3 times with exponential backoff (2s, 4s).
- Per-item transaction failures are recorded via
notifier.RecordError()and logged at WARN level; processing continues with the next item. - Fatal errors (feed fetch failure, XML parse failure) publish a
task.erroredSNS event and exit non-zero. - Overtime (execution exceeds
EXPECTED_DURATION_MINUTES − 10 minsoft deadline) cancels the context and publishes atask.overtimeSNS event. - The item loop also stops 5 minutes before that soft deadline
(
cmd/cert-se-rss-processor/main.go:115-118). WhenEXPECTED_DURATION_MINUTESis unset — which is exactly whatjust go-cert-se-rss-backfilldoes — the soft deadline falls back to a hardcoded 10 minutes, so a local run stops walking the feed after ~5 minutes. The feed only carries 10 items, so this has no practical effect today, but it does not match the “backfills must run to completion” rule inscripts/go-processors/AGENTS.md. - The
notify-dispatcherLambda transformstask.erroredandtask.overtimeSNS events into Slack Block Kit or Google Chat cardsV2 messages (selected at boot byNOTIFY_BACKEND) with CloudWatch and ECS deep-links.
ECS Schedule
Daily at 06:00 UTC (cron(0 6 * * ? *)), matching other CERT family
processors.
| Resource | Value |
|---|---|
| CPU | 256 units (0.25 vCPU) |
| Memory | 512 MB |
| Expected duration | 30 minutes |
| ECS family | go-cert-se-rss-processor |
| EventBridge schedule | go-cert-se-rss-processor |
| CloudWatch log group | /ecs/vdb-scheduler/go-cert-se-rss-processor |
| ECR image tag | go-cert-se-rss-processor-latest |
Key Files
| File | Purpose |
|---|---|
cmd/cert-se-rss-processor/main.go | Main processor, flags, feed fetch loop |
internal/certse/types.go | RSS feed structs and Advisory type |
internal/certse/parser.go | XML parsing, CVE/CVSS extraction, date parsing |
internal/certse/mapper.go | Advisory → CVESourceData mapping |
terraform/go-schedules.tf | ECS task + EventBridge schedule (module "cert_se_rss_processor") |
scripts/task-manager.toml | TUI metadata ([tasks.cert-se-rss-processor]) |
justfile | Local backfill recipe (go-cert-se-rss-backfill) |
S3 Persistence
- Archive path:
cert-se/files/{sha256}/{filename}✓ - Quarantine path:
failed-feeds/cert-se-rss-processor/{YYYY-MM-DD}/{reason}/{filename}✓ - Failure reasons emitted:
parse-error,store-error
Uses s3client.Uploader from internal/s3client/uploader.go. Skipped when S3_BUCKET_NAME is unset (local dev).
See the S3 Persistence Contract for the full reason taxonomy.