cisco-cvrf-backfill

Status: Live (local-only — no ECS task, no EventBridge schedule, no Containerfile target) Source: Cisco Security Center CVRF via the getCvrfPaginationRecords.x JSON enumeration API Type: cvrf (CSAF-CVRF v1.2 XML per advisory) Source slug: cisco — the same rows cisco-cvrf-processor maintains Schedule: none. Run by hand: just go-cisco-cvrf-backfill prod.

Overview

The scheduled cisco-cvrf-processor short-circuits on an unchanged listing-page SHA256, which is what keeps the daily run cheap — but it also means a mapper change, a schema addition or a partially-failed run never gets retroactively applied to the ~5,200 historical Cisco advisories. This binary is the escape hatch: it skips the listing tracker entirely and walks every advisory URL, relying only on the per-advisory content hash for resume, so --force re-imports the full Cisco corpus.

It shares the parser and mapper with the scheduled processor (internal/cisco), so the record shape is identical; only the driving loop and the resume policy differ.

PropertyScheduled processorThis backfill
Listing-page SHA256 trackerConsulted; exits early when unchanged, and updated on completionNot read, not written
Per-advisory sourceFileHash resumeYesYes (bypass with --force)
DeadlineSoft deadline from EXPECTED_DURATION_MINUTES, else 90 min (4 h with --force)None — runs to completion, per scripts/go-processors/AGENTS.md
Write shapeOne advisory per transaction, up to 3 retriesBatches of 50 in one transaction, per-item SAVEPOINT sp_item + up to 3 retries
Parse-failure handlingStores a state="REJECTED" hash row so the advisory is not refetchedQuarantines the XML and counts an error; no hash row
S3 archive keycisco/advisories/{sha256}/{advisoryID}.xml (non-canonical)cisco/files/{sha256}/{advisoryID}.xml (canonical)

Records produced

Identical to the scheduled processor — see cisco-cvrf-processor § Data Mapping: CVEMetadata (cveId = Cisco advisory ID, source="cisco"), CVEDescription (document notes + remediations), CVEMetadataReferences, CVEAffected (vendor Cisco), CVEAlias (CVE IDs and Cisco Bug IDs — the bug IDs mint placeholder CVEMetadata rows), plus Artifact + Link + CVEMetadata.fileLinkId when the S3 upload succeeds.

The same CVSS gap applies: internal/cisco/types.go:113-118 decodes ScoreSet/BaseScore/Vector, while Cisco emits ScoreSetV3/BaseScoreV3/VectorV3, so no CVEMetric row is produced from upstream data. Re-running this backfill will not fill in Cisco CVSS until that struct is fixed — worth knowing before committing to a multi-hour run.

Failure modes

FailureBehaviour
Listing enumeration failstask.errored + exit 1
Advisory fetch failsRecordError, counted, advisory skipped (no quarantine — there are no bytes)
ParseCVRF failsXML quarantined to failed-feeds/cisco-cvrf-backfill/{date}/parse-error/{file}.xml, counted as an error
MapToSourceData returns nilCounted as skipped
Item store fails after 3 attempts (with UTF-8 sanitisation retry)Rolled back to sp_item, XML quarantined under store-error, counted; batch continues
Batch transaction failsEntire batch counted as errored
Any error at end of runtask.errored to SNS and exit 1 (so a partially successful backfill still exits non-zero)

Flags

FlagDefaultDescription
--forcefalseIgnore the per-advisory content hash and reprocess everything
--limit0Cap advisories processed (0 = all)
# Import everything new since the last run, against production
just go-cisco-cvrf-backfill prod

# Full re-import (all ~5.2k advisories)
just go-cisco-cvrf-backfill prod true

# Smoke test: 5 advisories against the local DB
just go-cisco-cvrf-backfill local false 5

Key files

FilePurpose
cmd/cisco-cvrf-backfill/main.goEnumeration, batching, savepoint/retry logic, S3 archive + Artifact/Link
internal/cisco/parser.goFetchListingEntries, FetchAdvisory, ParseCVRF
internal/cisco/mapper.goMapToSourceData — CVRF document → osv.CVESourceData
internal/processor/pipeline.goStoreCVESourceData — shared write path for all child tables

S3 Persistence

  • Archive path: cisco/files/{sha256}/{filename}
  • Quarantine path: failed-feeds/cisco-cvrf-backfill/{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).

flowchart LR SRC[Source feed] --> PROC[cisco-cvrf-backfill] PROC -->|success| ARCHIVE[("S3: cisco/files/{sha256}/{filename}")] PROC -->|failure| Q[("S3: failed-feeds/cisco-cvrf-backfill/{date}/{reason}/{filename}")] PROC --> DB[(PostgreSQL)]

See the S3 Persistence Contract for the full reason taxonomy.