ai-discovered-vulns-runner
Status: Local-only operator tool — not a scheduled task Source: the VDB itself (
Kev,Exploit/ExploitCVE,EpssScore) joined against a hard-coded vulnerability list Type: n/a — no upstream feed Source slug: none — writes noCVEMetadataSchedule: none. No ECS task definition, no EventBridge schedule, no Containerfile target, no justfile recipe. Invoked by hand.
Overview
summary-processor owns the ai_discovered_vulns_outcomes SummaryLog row —
the per-vulnerability “what happened next” panel served by
GET /v2/ai-discovered-vulns-outcomes and rendered by
website/src/pages/articles/ai-discovered-vulnerabilities.vue. That row is
produced once a day alongside every other summary stat.
This binary exists so an operator can recompute only that row after editing the article’s vulnerability list, instead of triggering a full summary run:
set -a && source .env.production && set +a
go run ./cmd/ai-discovered-vulns-runner -force
For each tracked identifier it reads current exploit intelligence out of the VDB and emits one JSON payload:
- KEV status — every
Kevrow for the id, the set of KEV sources, and the earliestdateAdded(main.go:281). - Exploit availability — total
ExploitCVEcount, whether any linkedExploitis weaponised, and first-seen timestamps for both (main.go:311). “Weaponised” is keyed onExploit.source, notExploit.category:categoryis mostly NULL (nuclei, metasploit, github-poc, packetstorm and vulnerability-lab write none) and leaks Snort classtypes and ExploitDB free text. The source list mirrors vdb-api’sexploitSourceMaturityweaponized set so both services agree on the word. - EPSS — newest
EpssScorerow per id bydateString(main.go:339).
Aggregates (totals, autonomous vs human-assisted, by AI system / org / affected vendor / severity, average EPSS) are folded in alongside the per-entry outcomes.
Records produced
| Condition | Records |
|---|---|
| Every run | one SummaryLog row — label = "ai_discovered_vulns_outcomes", value = number of tracked entries, expr = "json", metadata = the outcomes + aggregate payload, timestamp = time.Now().UnixMilli() |
Nothing else is written. All reads go to the read pool; the single write goes to
the write pool (main.go:252).
The vulnerability list is duplicated — and drifts
The tracked identifiers live in a hard-coded aiDiscoveredVulnIds slice in
two places:
| Copy | Role |
|---|---|
cmd/summary-processor/ai_discovered_vulns.go | canonical — what the daily run and therefore the live article use |
cmd/ai-discovered-vulns-runner/main.go | copy, meant to be kept in sync by hand |
They are not in sync, and there is no test that they are. Because the API reads
the most recent SummaryLog row for the label, running this binary while its
copy is behind replaces a fuller payload with a smaller one — the article
silently loses entries until the next daily summary-processor run restores
them.
Before running it, diff the two slices. Prefer running
summary-processor scoped to the single stat if that is available.
Failure modes
| Situation | Behaviour |
|---|---|
DATABASE_URL unset | log + os.Exit(1) |
| KEV query fails | os.Exit(1) — the stat is not written |
| Exploit or EPSS query fails | logged as a warning; the payload is still written with those fields absent |
| Write fails | os.Exit(1) |
The whole run is bounded by a 10-minute context and the compute phase by a
further 5 minutes (main.go:236, main.go:260). Unlike the scheduled
processors it takes no EXPECTED_DURATION_MINUTES into account, and it does not
ON CONFLICT on (label, timestamp) the way summary-processor does.
Flags
-force is accepted for symmetry with the processor recipes; the runner always
recomputes and always writes.
Key files
| File | Purpose |
|---|---|
scripts/go-processors/cmd/ai-discovered-vulns-runner/main.go | whole binary — list, queries, payload, write |
scripts/go-processors/cmd/summary-processor/ai_discovered_vulns.go | canonical implementation and canonical list |
website/src/pages/articles/ai-discovered-vulnerabilities.vue | consumer of the published payload |
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:
ai-discovered-vulns-runner/files/{sha256}/{filename} - Quarantine:
failed-feeds/ai-discovered-vulns-runner/{YYYY-MM-DD}/{reason}/{filename} - Likely reasons: (none documented)