APK Secdb Processors (chainguard-json-processor, wolfi-json-processor)
Two daily processors that ingest the Alpine secdb security feeds published by Chainguard and Wolfi:
chainguard-json-processor→ https://packages.cgr.dev/chainguard/security.jsonwolfi-json-processor→ https://packages.wolfi.dev/os/security.json
Both feeds share an identical shape, so the per-binary cmd/ entrypoint is a thin shim around a shared internal/apksecdb/ package.
Feed format
Validated against schemas/apk_secdb_advisory.schema.json.
{
"apkurl": "{{urlprefix}}/{{reponame}}/{{arch}}/{{pkg.name}}-{{pkg.ver}}.apk",
"archs": ["x86_64", "aarch64"],
"reponame": "chainguard",
"urlprefix": "https://packages.cgr.dev",
"packages": [
{"pkg": {"name": "aactl", "secfixes": {
"0": ["CVE-2023-45283"], // unfixed → wildcard
"0.4.12-r10": ["CVE-2024-29902", "GHSA-..."] // fixed in this version
}}}
]
}
Storage model
For every (pkg.name, fixVersion, vulnID) tuple the processor:
- Looks up a base CVEMetadata row by
cveId = vulnID, ordered by source preference:cve.org→nist-nvd→github→osv→ any other. If no row exists yet, a minimal placeholder is created with the natural source for the ID prefix (CVE-* →cve.org, GHSA-* →github). - Upserts a CVEAffected row at
(cveId, baseSource, containerType="adp", affectedHash)withadpOrgId = "chainguard" | "wolfi",vendor = product = packageName = pkg.name,collectionURL = https://images.chainguard.dev | https://wolfi.dev, and a single wildcard CPE (cpe:2.3:a:<vendor>:<pkg>:*:*:*:*:*:*:*:*) in thecpesJSON column. TheaffectedHashincludes theadpOrgIdso two different ADPs don’t collide on the same CVE+source. - Inserts CVEAffectedVersion rows for every observed fix version:
secfixes["0"](unfixed) →Version="*",Status="affected",VersionType="apk".secfixes["X.Y.Z-rN"](fix shipped) →Version="0",LessThan="X.Y.Z-rN",Status="affected",VersionType="apk"— i.e. all versions strictly less than the fix are affected, mirroring the OSV/gemnasium convention.
- Drives
EnrichAffectedWithDependencyso PURLs and PackageVersionCVE junctions are populated. Generated PURL:pkg:apk/<reponame>/<pkg>(no@versionwhen wildcard) orpkg:apk/<reponame>/<pkg>@<version>for explicit versions.
“Version 0 is wildcard, not zero”
The Alpine secdb format uses the literal string "0" as the secfixes key for advisories with no
shipped fix. Generated CPEs and PURLs render that as * — never as a literal 0. The two
helpers in internal/apksecdb/identifiers.go (CPE, PURL) and the row builder
(versionRowFor) both encode this rule, and identifiers_test.go locks it in.
Tracker
Each processor owns its own BulkDataDumpTracker row (chainguard_secdb, wolfi_secdb) keyed on
SHA-256 of the fetched feed body. A run that observes an unchanged hash exits early with a
task.no_work notification. The tracker is only updated on a clean, full run; partial runs leave
it stale so the next scheduled invocation retries.
Schedules
Both schedules are daily EventBridge crons:
chainguard-json-processor—cron(30 8 * * ? *)(08:30 UTC)wolfi-json-processor—cron(0 9 * * ? *)(09:00 UTC)
CPU 512, memory 1024 MiB, expected duration 60 min.
Local execution
just go-chainguard-json-backfill # local DB
just go-chainguard-json-backfill prod # .env.production
just go-wolfi-json-backfill
just go-wolfi-json-backfill prod
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.
⚠ Not in the compliance matrix — status needs verification.
Expected paths when implemented:
- Archive:
apksecdb/files/{sha256}/{filename} - Quarantine:
failed-feeds/apksecdb-processor/{YYYY-MM-DD}/{reason}/{filename} - Likely reasons: (none documented)