CISA Vulnrichment Git Processor
Overview
Processes CISA Vulnrichment CVE Record Format v5 JSON files into CVEMetadata and related tables. The vulnrichment repository contains ~136K files with both original CNA data and CISA’s ADP enrichments (SSVC decision points, CWE, CVSS).
Data Source
- Repository:
https://github.com/cisagov/vulnrichment.git - Format: CVE Record Format v5 JSON (same as cvelistV5)
- Structure:
{YYYY}/{N}xxx/CVE-{YYYY}-{NNNNN}.json - Volume: 169,450 files as of 2026-08-06 (the repo is not baked into the container image — it is cloned at runtime on every run)
ID Scheme
Records are stored with source="cisa_adp" and remapped IDs to avoid collision with cve.org records:
- Stored cveId:
CISA-{YYYY}-{NNNNN}(e.g.,CISA-2024-0001) - Alias: Original
CVE-{YYYY}-{NNNNN}stored in CVEAlias table - Rationale: Same CVE ID exists in both cvelistV5 (source=cve.org) and vulnrichment (source=cisa_adp) with different enrichment data
Processing Flow
- Clone/pull the vulnrichment repository via
processor.PullOrClone()(depthprocessor.IncrementalDepthin ECS, 1 locally) - Check
BulkDataDumpTracker(source="vulnrichment_advisory") — skip withNoWorkif HEAD SHA unchanged - Walk repository root for
CVE-*.jsonfiles (year-based subdirectories) - Apply the git delta window (
processor.DetectChangedFiles+processor.FilterPaths) — skipped on the first run and under--force - Load existing
sourceFileHashmap for resume, keyed by the CISA id - Local runs only: resume from the
.repostatefile checkpoint, which is rewritten after every batch and deleted on a clean finish - Batch-process files (100 per transaction, with a savepoint around each file):
- Parse via
vulnrichment.ParseRecord()(a thin wrapper overcvelistv5) - Remap CVE ID to CISA ID (
vulnrichment.CveIDToVVD) - Store via
vulnrichment.StoreRecord(), which callscvelistv5.StoreRecordWithSource(..., source="cisa_adp", cveID=CISA-…)and thendb.InsertAliaseswith the original CVE id
- Parse via
- Stop before the 60-minute runtime budget expires. If the hard budget cancels an in-flight transaction, roll it back without classifying its files as bad input or counting its writes as committed.
- Update the tracker with HEAD SHA only after a complete repository walk. A truncated run leaves the tracker unchanged so the next run continues catching up through the per-file hash resume set.
Key Reuse
internal/vulnrichment:CveIDToVVD,StoreRecord,ParseRecord,FileHash,WalkCVEFiles— a wrapper package aroundinternal/cvelistv5that pinssource="cisa_adp"and the CISA id remapinternal/cvelistv5:ParseRecord,StoreRecordWithSource,WalkCVEFiles,FileHash, UTF-8 sanitisationinternal/processor:PullOrClone,DetectChangedFiles, statefile,IsRunningInECSinternal/db:LoadProcessedHashes,WithTx,GetTracker,UpsertTracker,InsertAliases
Failure modes
- Runtime cancellation: The repository clone takes about 6.5 minutes. A 15-minute budget left little processing time and could expire inside a 500-file transaction. The task now has a 60-minute budget and uses 100-file transactions. Context cancellation rolls the current transaction back cleanly and leaves the tracker unchanged.
- Catch-up runs are expected: The source still clones at runtime. When a run reaches its soft deadline before scanning the full repository, the next daily run retries the same HEAD and skips files whose hashes are already current.
- No AI enrichment: no
aienrich.Enricheris constructed, so the CWE / ATT&CK / TreeSitter passes never run forcisa_adprecords. - Per-file SQL failures: Each file has a savepoint. A PostgreSQL error rolls back that file while allowing the rest of the transaction to continue.
Data Mapping
All CNA + ADP containers are stored, including:
- Descriptions (CVEDescription)
- References (CVEReference)
- Metrics (CVEMetric) — CVSS v2/v3.0/v3.1/v4.0 + SSVC “other” metrics
- Problem Types (CVEProblemType) — CWE IDs from CISA ADP
- Affected (CVEAffected + CVEAffectedVersion) — CPE applicability
Deployment
- ECS:
go-vulnrichment-git-processortask, daily at 06:00 UTC (cron(0 6 * * ? *)) - Resources: 256 CPU / 1024 MB (large repo, processed across multiple resume runs)
expected_duration_minutes: 60 (scripts/task-manager.toml), which reaches the container asEXPECTED_DURATION_MINUTES;rundeadline.Softreserves six minutes for the final transaction and shutdown- Local:
just go-vulnrichment-git-backfill(the recipe unsetsEXPECTED_DURATION_MINUTES, so a backfill runs to completion)
S3 path deviations
The generated section below normalises the source slug to cisa-adp; the real
prefix is the CVEMetadata.source value cisa_adp (underscore), and the
{sha256} slot holds cvelistv5.FileHash, which is SHA-1
(internal/cvelistv5/mapper.go:26-28). Real keys therefore look like
cisa_adp/files/{sha1}/2024/0xxx/CVE-2024-0001.json. Quarantine reasons are
parse-error, store-error and tx-rollback (main.go:279, 295, 311).
S3 Persistence
- Archive path:
cisa-adp/files/{sha256}/{filename}✓ - Quarantine path:
failed-feeds/vulnrichment-git-processor/{YYYY-MM-DD}/{reason}/{filename}✓ - Failure reasons emitted:
parse-error,store-error,tx-rollback
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.