# feeds — design
> **Class:** `design` · **Function axis:** `f0` the ingest fold · `f1` per-source parsers ·
> `f2` version grammars · `f3` enrichment & suppression · `f4` package behaviour features (A23).
> Strict: only the shape we run. The
> *why* is in [`feeds-reasoning.md`](feeds-reasoning.md); the system view in [`design.md`](design.md).

## f0 — the ingest fold
`feeds` turns a set of **offline** feed documents into a deduplicated `Vec<`
[`scan::Advisory`](../src/scan.rs)`>` carrying **ranges**, ready for the warehouse or a
direct scan. The pipeline (as wired in `main.rs:cmd_import`): parse each source →
[`merge_by_alias`](../src/scan.rs) (`scan.rs:224`, dedup CVE↔GHSA↔RUSTSEC into one row via
alias union / range union / max severity / OR-of-kev) → optional NVD enrichment →
[`feeds::enrich`](../src/feeds.rs) (EPSS/KEV) → append.
An `Advisory` is range-native: `ranges: Vec<RangeEvent>` is the source of truth,
`affected_versions` an optional pre-expanded cache, and [`Advisory::matches`](../src/scan.rs)
(`scan.rs:183`) is `withdrawn ⇒ false` else `versions::affected_by(ecosystem, &ranges,
version)` OR the exact-list fallback.
**Reuse:** `feeds` is leaf-of-leaf — pure parse, no warehouse/network. It produces the
`Advisory`s that `verdict` matches and `warehouse` round-trips.
## f1 — per-source parsers
| source | entry | file:symbol | produces |
|---|---|---|---|
| OSV / GHSA (single) | `parse_osv` | `osv.rs:191` | `Vec<Advisory>` (ranges + aliases + withdrawn) |
| OSV batch | `parse_osv_batch` | `osv.rs:197` | `Vec<Advisory>` |
| OSV (auto single/batch) | `parse_doc` | `osv.rs:206` | `Vec<Advisory>` |
| OSV dir → merged | `parse_and_merge` | `osv.rs:221` | deduped `Vec<Advisory>` |
| OSV range specs | `parse_osv_ranges` | `osv.rs:244` | `Vec<AffectedRange>` |
| RUSTSEC `.md` (feature `rustsec`) | `parse_rustsec_md` | `osv.rs:642` | `Option<Advisory>` |
| RUSTSEC range inversion | `rustsec_affected_ranges` | `osv.rs:391` | `Vec<RangeEvent>` (patched/unaffected → affected) |
| NVD CVE JSON 2.0 | `parse_nvd` | `nvd.rs:183` | `Vec<NvdEntry>` (CVSS vector + CWE + severity) |
| NVD CPE backfill | `parse_nvd_advisories` | `nvd.rs:209` | standalone `Advisory`s (`ecosystem = "cpe"`) |
| CISA KEV | `parse_kev_json` | `feeds.rs:62` | `HashSet<String>` (exploited CVE ids) |
| ENISA EUVD (exploited) | `parse_euvd_exploited` | `feeds.rs` | `HashSet<String>` (EU-authority `exploitedSince` CVE ids → folded into `kev`) |
| **VulnCheck KEV** | `parse_vulncheck_kev` | `feeds.rs` | `HashSet<String>` — a *superset* of CISA KEV (adds exploited CVEs CISA has not yet catalogued, from VulnCheck's own `vulncheck_reported_exploitation`); a KEV-grade "exploited in the wild" signal from a **third** authority (US-gov / EU / commercial-intel), folded into the SAME `kev` escalation set. Document is `{data:[{cve:[…]}]}` — an entry's `cve` is a LIST; lower-case `cve-…` canonicalized to `CVE-…`. `--vulncheck-kev <file>`; provided-but-0 ⇒ hard error (empty-is-RED). LIVE download needs a FREE VulnCheck token — this OFFLINE parser is token-free |
| FIRST EPSS | `parse_epss_csv` | `feeds.rs:29` | `HashMap<String, f64>` |
| OpenSSF Malicious | `parse_malicious` | `feeds.rs:84` | Critical `MAL-*` `Advisory`s |
| GitLab gemnasium | `parse_gemnasium_yaml` | `gitlab.rs` | `Advisory` (flat-YAML scan + `affected_range` grammar → ranges; `source=gitlab`) |
| PoC-in-GitHub | `parse_poc_in_github` | `feeds.rs` | `HashSet<String>` (CVE ids with a public PoC) |
| Metasploit modules | `parse_metasploit_cves` | `feeds.rs` | `HashSet<String>` (CVE ids with a weaponized module) |
| Nuclei templates | `parse_nuclei_cves` | `feeds.rs` | `HashSet<String>` (CVE ids with a detection/exploit template; NDJSON) |
| **Exploit-DB** `files_exploits.csv` | `parse_exploitdb_csv` | `feeds.rs` | `HashSet<String>` (CVE ids with a **published, downloadable exploit** — the strongest public-exploit signal). `codes` column located from the **header row**, never a fixed offset (Exploit-DB has added columns over the years, so a hardcoded index silently reads the wrong field on a newer mirror); fields split with the SHARED RFC-4180 splitter `ioc::split_csv` (the `description` column is quoted and comma-heavy) rather than a second CSV parser (L5). No `codes` column ⇒ `Err` (wrong file / HTML error page), never a fake-empty set |
| crates.io yanks | `parse_crates_yanks` | `registry.rs` | Low `MODGUNN-YANK-cargo-*` `Advisory`s (exact-version) |
| npm deprecations | `parse_npm_deprecations` | `registry.rs` | Low `MODGUNN-DEPRECATED-npm-*` `Advisory`s (exact-version) |
| PyPI yanks (PEP 592) | `parse_pypi_yanks` | `registry.rs` | Low `MODGUNN-YANK-pypi-*` `Advisory`s (release fully-yanked → exact-version) |
| Go retractions | `parse_go_retractions` | `registry.rs` | Low `MODGUNN-YANK-go-*` `Advisory`s (go.mod `retract`; single → exact, `[lo,hi]` → inclusive range) |
| Sonatype **OSS-Index** | `parse_ossindex_report` | `registry.rs` | `Vec<Advisory>` — the **purl-keyed** cross-ecosystem component report, one row per `(component, vulnerability)` at the component's exact version. Coords come from the ONE purl grammar (`sbom::parse_purl`, the inverse of `sbom::purl`), so a scoped npm (`pkg:npm/%40scope/name`) and a maven `group/artifact` land on exactly the coords the join keys on. **A CVE-carrying finding is keyed BY its CVE** (the Sonatype UUID rides as an alias) so `merge_by_alias` folds it into the row OSV/NVD already produced instead of double-reporting one vulnerability; only a Sonatype-only finding gets `MODGUNN-OSSINDEX-<uuid>`. Severity from the numeric `cvssScore` through the SHARED `osv::severity_from_base`; `--ossindex` |
| ecosyste.ms status | `parse_ecosystems_package` | `registry.rs` | Low `MODGUNN-ECOSYSTEMS-{REMOVED,DEPRECATED}-*` `Advisory`s — cross-ecosystem PACKAGE-level pulled/deprecated status (all-versions); `--ecosystems`, active/null status → None (real zero) |
| **libraries.io** status | `parse_librariesio_project` | `registry.rs` | Low `MODGUNN-LIBRARIESIO-{REMOVED,DEPRECATED,UNMAINTAINED,HIDDEN}-*` `Advisory`s — the elder cross-ecosystem index (ecosyste.ms is its open successor); same all-versions "stop depending on this" row from `status` OR a non-empty `deprecation_reason`. Platform label case-folded through the shared `ecosystems_eco` (L5). `--librariesio`, active/null → None (real zero). LIVE API needs a FREE registered `api_key`; this OFFLINE parser is key-free |
| **socket.dev** alerts ⚠SCAFFOLD | `parse_socket_alerts` | `registry.rs` | `Vec<Advisory>` — per-package-version supply-chain BEHAVIOUR findings (malware / install-scripts / obfuscation / telemetry), keyed on the purl `type`→ecosystem; worst-alert severity, any `malware` alert ⇒ Critical. Accepts a single object, a batch `[…]`, or `{packages:[…]}`. `--socket`. **PENDING**: the live Socket API is **PAID** — parser follows the public alert schema but the response envelope is unverified against a live feed (see §D) |
| **GUAC** CertifyVuln ⚠SCAFFOLD | `parse_guac_certify_vuln` | `registry.rs` | `Vec<Advisory>` — package↔vuln certifications from GUAC's graph (GraphQL `CertifyVuln`), walked out of the package trie (`type`→`namespaces`→`names`→`versions`); keyed BY the vuln id (CVE/GHSA canonicalized upper) so `merge_by_alias` folds it into the OSV/NVD row; `novuln` (certified NOT vulnerable) skipped. `--guac`. **PENDING**: needs a **running GUAC GraphQL endpoint** with data loaded — schema unvalidated against a live instance here (see §D) |
| CSAF VEX | `parse_csaf_vex` / `collect_suppressions` | `vex.rs` | suppressed CVE id set (an id marked `known_affected` anywhere is never suppressed — product-scoped safety) |
| Go vuln DB symbols | `parse_osv_vuln_symbols` | `osv.rs` | `VulnSymbol{id,eco,package,symbol}` (OSV `ecosystem_specific.imports` → the A8/b1 reachability sinks; `vuln_symbols` table) |
| **ClearlyDefined** | `parse_clearlydefined` / `parse_clearlydefined_batch` | `spdx.rs` | `ClearlyDefined{ecosystem,name,version,declared,discovered,unknown_files,score}` — the **declared-vs-DISCOVERED** license comparison, the compliance leg no vulnerability feed carries. Discovered ids are read from **every** facet (`core`/`tests`/`data`/…, because a GPL file in a non-core facet still ships) and split into leaf ids through the SHARED SPDX expression parser, so `MIT OR Apache-2.0` genuinely covers a discovered `Apache-2.0`. `.findings()` → High undeclared-license / Medium no-declaration / Low unlicensed-files / Info low-score. Verb: `warehouse license-compliance` |
| Sigstore **Rekor** | `parse_rekor_entries` | `provenance.rs` | `RekorEntry{uuid,log_index,integrated_time,kind,artifact_sha256,signer_identity,has_inclusion_proof,has_signed_timestamp}` — the **transparency-log** leg. `hashedrekord` + `intoto`/`dsse` bodies, base64-decoded by a pure-Rust decoder (no `base64` dep in the lean gate). `attestation()` is deliberately `slsa_level = 0` (log presence is not a build claim); an entry with no inclusion proof is graded High, never trusted. Verb: `warehouse rekor` |
| SPDX license list | `parse_spdx_license_list` | `spdx.rs` | `SpdxLicenseList{known,deprecated}` — the **license/compliance** leg: reference data (no table), drives `license-lint` to flag deprecated/unknown SPDX ids via the shared expression parser |
| CAPEC → ATT&CK | `parse_capec_xml` | `capec.rs` | `CweAttackMap` (CAPEC XML `Related_Weaknesses`↔`Taxonomy_Mappings` → CWE→ATT&CK technique) — the **identity/mapping** leg: reference data (no table), drives `attack-map`; `techniques_for_cwes` tags a finding's CWEs with adversary techniques |
`AffectedRange` is the intermediate `{ id, ecosystem, package, severity,
summary, events: Vec<RangeEvent> }` — **one per OSV range object**, not per affected entry; [`expand_advisories`](../src/osv.rs) (`osv.rs:274`)
expands ranges to exact `Advisory`s against a known version list when a cache is wanted.
## f2 — version grammars (`versions.rs`)
[`RangeEvent`](../src/versions.rs) (`versions.rs:26`) is the OSV range vocabulary
(`Introduced`/`Fixed`/`LastAffected`) plus one **non-OSV** event,
`IntroducedExclusive` — a *strictly greater than* lower bound. OSV cannot express
it, but NVD's `versionStartExcluding` means exactly that, and folding it onto the
inclusive `Introduced` would flag the boundary version itself as vulnerable (a
false positive on the version the vendor called clean). These functions drive
matching:
- [`compare(ecosystem, a, b)`](../src/versions.rs) — per-ecosystem version ordering: semver
(cargo/npm/**go**/**nuget**/**composer**) / PEP440-lite / maven-lite / **gem** (RubyGems
segments, `1.0.0.rc1` < `1.0.0`) / loose, plus distro grammars **dpkg**
(`deb_cmp`: epoch/upstream/revision, `~` ordering), **rpm** (`rpm_cmp`: rpmvercmp) and
**apk** (`apk_cmp`: Alpine suffix ranks). The loose fallback (only `cpe` + unknown
ecosystems now) is NOT prerelease-aware — it ranks `1.5.0-rc.1` above `1.5.0`.
- [`affected_by(ecosystem, events, version)`](../src/versions.rs) — the scan-time predicate for
**one** range: is `version` inside its `[introduced, fixed)` / `[introduced, last_affected]`
window under the ecosystem's comparator — or `(introduced, …` when the lower bound is
an `IntroducedExclusive`.
- [`affected_by_any(ecosystem, ranges, version)`](../src/versions.rs) — the OR over an
advisory's range objects. `Advisory::ranges` is `Vec<Vec<RangeEvent>>` (one inner list per
OSV range object) precisely so ranges union instead of cancelling; GIT ranges are dropped at
import (commit hashes are not versions).
- [`decode_range_sets`](../src/versions.rs) — warehouse `ranges` column decode; a pre-v3 flat
event array reads back as a single range object.
- [`expand_range`](../src/versions.rs) (`versions.rs:87`) — the lazy exact-list expander.
`normalize_ecosystem` maps OSV distro labels (Debian/Ubuntu→deb, Alpine→apk,
RedHat/Rocky/Alma/SUSE→rpm) so OSV.dev's distro exports match with the right comparator.
## f3 — enrichment & suppression
- [`enrich(advisories, epss, kev)`](../src/feeds.rs) (`feeds.rs:70`) folds EPSS scores and the
KEV flag onto advisories matched by id **OR any alias** (same as `enrich_with_nvd`): the
EPSS/KEV feeds are keyed by CVE, but `merge_by_alias` keeps the first-seen record as primary,
so an actively-exploited CVE whose merged advisory is GHSA/RUSTSEC-primary carries the CVE only
in its aliases — matching on aliases keeps the KEV→Critical / EPSS→High escalation firing.
- [`enrich_exploited(advisories, exploited)`](../src/feeds.rs) lifts an advisory whose id OR alias
has a **public PoC** (PoC-in-GitHub) to at least `High` and prepends the stable
`[public-exploit]` marker to its summary (idempotent; never lowers `Critical`). Schema-free — the
lift + marker ride the existing `severity`/`summary` warehouse columns, so the signal round-trips
and the baddie-detection join ranks exploit-backed findings above version-only ones. A public PoC
floors at `High` (weaker evidence than KEV's confirmed-in-the-wild → Critical).
- [`enrich_with_nvd(advisories, entries)`](../src/nvd.rs) (`nvd.rs:255`) sets CVSS vector + CWE
and lifts severity, matched by id OR alias.
- [`collect_suppressions(docs)`](../src/vex.rs) (`vex.rs:51`) unions CSAF `known_not_affected`
ids — the set a scan drops (warehouse-scan `--vex`, or `verdict`'s `vex_not_affected`).
- **Offline CVSS v3 scoring** ([`osv::cvss_base_score`](../src/osv.rs)): an OSV/RUSTSEC advisory whose
only severity signal is a **CVSS v3.0/3.1 vector** (no numeric score, no `database_specific` label —
very common) is scored by the spec's deterministic base-score formula and bucketed via the shared
`severity_from_base`, instead of silently under-rating to Medium (OSV) / High (rustsec). Both the
**CVSS v3.0/3.1** vector (`cvss_v3_base`) and the older **CVSS v2** vector (`cvss_v2_base`, detected
by its `Au:` metric) are computed; validated against FIRST's published reference scores. Only a
**v4** vector (a far more involved formula) still falls back to the label/Medium default.
## f4 — package behaviour features (A23)
![A23 behaviour features — npm package.json / PyPI setup.py / pyproject.toml / setup.cfg → PackageBehavior → Finding[]](assets/behavior-features.svg)
Where f1–f3 import *advisory* feeds keyed by a known vulnerability, `behavior` scores a package
that has **no CVE yet** — the malicious-package detection axis. It is the read-side feature
extractor, the same `parse_*` shape: offline, **no code executed** — it reads a package's
published metadata *text* and folds the install-time code into the A23 feature vector academic
detectors use (Socket H1-2025; arXiv 2412.05259 / 2309.02637).
**Four ecosystem front-ends, one folder.** [`parse_npm_package`](../src/behavior.rs) reads an npm
`package.json`'s auto-run `scripts{}`; [`parse_pypi_setup`](../src/behavior.rs) reads a PyPI
`setup.py`; [`parse_pyproject_toml`](../src/behavior.rs) reads a PEP 621/517 `pyproject.toml`;
[`parse_setup_cfg`](../src/behavior.rs) reads a setuptools `setup.cfg` (Python is the #2
malicious-package ecosystem). All funnel their install-time text through the
SAME pure `extract_from_install_text` folder and land in the SAME
[`PackageBehavior`](../src/behavior.rs) — never a per-ecosystem twin (L5); a new ecosystem is one
more thin front-end. They differ only in **what counts as the *install text***:
- **npm** — only the **auto-run** hooks [`INSTALL_HOOKS`](../src/behavior.rs) (`preinstall` /
`install` / `postinstall` / `prepare`) are scored: those are the scripts `npm install` executes
without the user asking, so a `test` / `build` script is not the vector and is ignored.
- **PyPI `setup.py`** — a `setup.py` runs *in full* at `pip install` from an sdist, so the
**whole file** is the install text. The `install_hooks` signal is a **`cmdclass` install/develop
override** (the documented persistence trick, [`has_cmdclass_install_override`](../src/behavior.rs));
a declarative `setup(name=…, packages=…, install_requires=…)` with no dangerous call is clean,
while a `setup.py` that `urllib.urlopen`s + `os.system`s a payload trips the network /
sensitive-API findings from the whole-file scan even with no `cmdclass`.
- **PyPI `pyproject.toml`** — *declarative* TOML that does **not** itself run code, so a whole-file
scan would be WRONG: a `[project] dependencies = ["urllib3"]` trips the `urllib` needle and a
`[project.urls] Homepage = "https://…"` trips `https://` → a spurious `network_in_install` on
nearly every honest project. So [`parse_pyproject_toml`](../src/behavior.rs) is TOML-**section-
aware**: it scans ONLY the sections/keys that run at build/install time — the `[build-system]`
backend spec, a PEP 517 in-tree backend `backend-path` (an install-hook signal in itself), a
`cmdclass` table, a `[tool.*.build.hooks…]` build-hook table — and SKIPS the declarative
`[project]` block, the `[tool.poetry]` / linter config, and the dependency-list keys `requires` /
`dependencies` / `optional-dependencies` **even inside an active section** (a build
`requires = ["urllib3"]` is a declared dep, not a network call). A benign declarative pyproject
(deps incl. `urllib3`, a `https://` homepage) is clean; a build-hook that `curl … | bash`es a
stage-2 payload IS flagged **High**. A tiny line-scanner — **no `toml` dependency**, so the
pyproject front-end stays inside the lean `scan` gate (the `toml` crate never enters the default
binary — holger / static-holger purity, matching the `rustsec` / `manifests` gating).
- **setuptools `setup.cfg`** — *declarative* INI, the same false-positive hazard as pyproject and
the same cure: [`parse_setup_cfg`](../src/behavior.rs) is a section-aware INI line-scanner (tracks
`[section]` + indent-continuation, **no `ini` / `configparser` crate**, so it too stays inside the
lean `scan` gate). It scans ONLY the build/install-active keys — the `[options]` `cmdclass`
mapping (a custom install/build command), the `[options]` `dependency_links` list (URLs setuptools
*fetches* at build → a genuine build-time network source), and a build-active
`[options.entry_points]` group ([`BUILD_ENTRY_POINT_GROUPS`](../src/behavior.rs) — a
`distutils.commands` / setup-keyword hook that runs at build) — and SKIPS the entire `[metadata]`
block (incl. `url` / `project_urls`), every declarative dependency-list key
([`SETUP_CFG_DECLARATIVE_KEYS`](../src/behavior.rs) — `install_requires` / `extras_require` /
`python_requires` / …) even inside `[options]`, and a runtime `console_scripts` / `gui_scripts`
entry-point group. So a benign declarative `setup.cfg` (`install_requires` incl. `urllib3`, a
`[metadata] url = https://…`) is clean, while a `dependency_links` payload fetch + a `cmdclass`
override IS flagged **High** + Low install-hook.
Either way [`PackageBehavior`](../src/behavior.rs) carries: `install_hooks`,
`network_in_install` (a `curl`/`wget`/`fetch(`/`urllib`/raw-URL fetch — the dropper stage-2
download), `url_count`, `max_token_entropy` (max Shannon entropy over long install-script tokens
— the base64/packed-payload signal, reusing the SAME [`core::shannon_entropy`](../src/core.rs)
the SCIP A17 obfuscation tripwire keys on; one metric, no twin, L5) and the `sensitive_apis` set
(npm: `child_process` / `eval(` / `atob(`; Python: `os.system` / `subprocess` / `marshal.loads` /
`__import__(`; shared: `/dev/tcp` / `base64` / …).
[`PackageBehavior::findings`](../src/behavior.rs) ranks by academic signal strength:
| feature | severity · id |
|---|---|
| `network_in_install` (dropper) | **High** · `MODGUNN-BEHAVIOR-NETWORK-IN-INSTALL` |
| `sensitive_apis` non-empty | Medium · `MODGUNN-BEHAVIOR-SENSITIVE-API` |
| `max_token_entropy ≥ 4.5` | Medium · `MODGUNN-BEHAVIOR-OBFUSCATION` |
| `install_hooks` non-empty | Low · `MODGUNN-BEHAVIOR-INSTALL-HOOK` |
Same [`core::Finding`](../src/core.rs) model the verdict surface consumes — the scoring *model*
(the isolation-forest / behaviour-sequence classifier over the vector) is the dynamic-plane
consumer; the feature extraction lives here so every consumer extracts it the same way.
**Durable + queryable (the VERDICT ledger).** A parsed `PackageBehavior` is not only printed:
`behavior --warehouse <root>` PERSISTS it into the `behavior_features` warehouse table via
[`Warehouse::append_package_behavior`](../src/warehouse.rs) (ecosystem-tagged coords + the full
feature vector + the *derived* verdict = worst finding severity + finding ids), through the SAME
warehouse-persist plumbing `scorecard --warehouse` uses — no second warehouse seam (L5). The read
back is [`load_package_behaviors`](../src/warehouse.rs) (latest per `(ecosystem, subject)`), whose
inner `PackageBehavior` round-trips **field-identical** (mutation-verified); the CLI read verb is
`warehouse behavior-log`. All behind the `warehouse` feature — the lean `scan`/default gate stays
free of the skade/arrow tree, so a plain `behavior` still vets a package with no warehouse build.
## Surface
The `modgunn warehouse import` CLI (`main.rs:cmd_import`) is the operator face: `--osv`,
`--malicious`, `--nvd`, `--nvd-backfill`, `--epss`, `--kev`, `--crates-yanks`,
`--npm-deprecations`, `--pypi-yanks`, `--ossindex`, and the four public-exploit sources
`--exploit-poc` / `--metasploit` / `--nuclei` / `--exploitdb` (which **union** into one
exploit-CVE set before the single `enrich_exploited` lift) each feed one source into this
pipeline; a malformed doc is named on stderr and counted, never silently dropped.
**Anti-silent-empty (two edges):** a *per-doc* parse failure (a truncated OSV/registry doc missing
its `versions`/`releases` array) is counted in `n_fail` and named on stderr; a well-formed source
doc that legitimately yields 0 rows (nothing yanked) is a real zero. But an **enrichment** feed
(`--epss` / `--kev`) that was *provided yet parsed to 0 rows* is a truncated catalog that would
silently disable the KEV→Critical / EPSS→High escalation — a fake pass — so `cmd_import` **bails**
rather than import it (a provided-but-empty enrichment feed is an error, not a shrug). The same law
covers the exploit union: if ANY of `--exploit-poc` / `--metasploit` / `--nuclei` / `--exploitdb`
was provided yet the union is 0 CVEs, the import bails rather than silently skip the
`[public-exploit]` lift.
## f5 — superplan §D security tail (2026-07-29)
The nordisk super-plan (`workspace_nordisk/.nornir/nordisk-super-plan-2026-07-29.md`) §D closed
its collector gap list — *"Remaining: VulnCheck, socket.dev, OSSF Scorecard ingest, Libraries.io,
GUAC"* — as follows. All parsers are OFFLINE, zero-shell, file-in (obtaining the file — token/key/
running-instance — is the dynamic plane's job, out of modgunn's scope); every one holds
empty-is-RED (a well-formed-but-empty doc is a real zero; a malformed doc is an `Err`, never a fake
pass) and ships a real red-when-reverted test.
| §D source | status | where | why |
|---|---|---|---|
| **OSSF Scorecard** | ✅ ALREADY LANDED | `provenance::parse_scorecard`, `ScorecardSignals`, `warehouse::append_scorecard`, `scorecard` verb | Was already on `origin/master` before this pass (the §D list was stale for it) — no re-work; verified present + tested |
| **VulnCheck KEV** | ✅ LANDED | `feeds::parse_vulncheck_kev` + `--vulncheck-kev` | Real free product, documented `{data:[{cve:[…]}]}` JSON; folds into the `kev` escalation set (third KEV authority). Live download needs a FREE token; the parser is token-free |
| **Libraries.io** | ✅ LANDED | `registry::parse_librariesio_project` + `--librariesio` | Documented project JSON; mirrors the ecosyste.ms package-status row (its open successor). Live API needs a FREE registered `api_key`; the parser is key-free |
| **socket.dev** | 🟡 SCAFFOLD / PENDING | `registry::parse_socket_alerts` + `--socket` | The live Socket API is **PAID** — I cannot reach it to validate the response envelope. The parser targets Socket's PUBLIC alert schema (docs.socket.dev) with a real test, but its field-for-field fidelity to a live paid response is **UNVERIFIED**. Wired + labelled `(SCAFFOLD)` in `--help`; do not treat as production until validated against a real Socket response |
| **GUAC** | 🟡 SCAFFOLD / PENDING | `registry::parse_guac_certify_vuln` + `--guac` | Ingesting from GUAC needs a **RUNNING GUAC GraphQL endpoint** with data loaded (`POST /query`) — no offline corpus to point at here. The parser targets GUAC's documented `CertifyVuln` GraphQL shape (package trie → vuln ids) with a real test, but the schema is **UNVERIFIED** against a live instance. Wired + labelled `(SCAFFOLD)`; note GUAC is primarily a *downstream aggregator* that would consume modgunn output — this ingest path is the honest reverse direction |
**Why socket.dev / GUAC are PENDING, not fake-green:** each has a real parser + real unit test, so
`--socket` / `--guac` are honestly usable on a correctly-shaped file. The PENDING mark is about
*source fidelity* — I could not obtain a live sample (paid key / running instance) to confirm the
real envelope matches the documented one. That is a truthful "scaffolded, unverified against the
live source", never an empty-that-passes-as-green.