PR #147 opened: fix(mcp): dedup security CVEs + collapse HN mirrors + drop empty-CVSS suffix
Summary
Three reviewer-flagged bugs from the 2026-06-02 deep dive. Verified on prod data, all with measurable before/after.
1. `StackRadar::querySecurity` — DISTINCT ON (url_hash)
Same CVE was hitting every GHSA ecosystem feed (`ghsa_npm` + `ghsa_pip` + `ghsa_composer` + `ghsa_all`) and showing up as N separate rows. The fix wraps the filter in a CTE with `DISTINCT ON (url_hash)` — same pattern `SecurityDigest` already uses.
Verified on prod (last 7 days):
| CVE | Before |
|---|---|
| Symfony YAML "Billion Laughs" | 46 dups |
| Symfony YAML ReDoS in cleanup() | 46 dups |
| Symfony MonologBridge deserialization | 46 dups |
| Automad password hash exposure | 46 dups |
(Reviewer reported "8 times" for Twig — was conservative; Symfony was 46x.)
2. `GetVelocity` cross-platform — collapse same-platform-family slugs
The cross_platform CTE counted `hn_top` + `hn_new` + `hn_best` + `hn_show` as 4 distinct platforms, so a story that only ever lived on HN claimed 4-platform reach.
Pre-normalises `source_slug` into a platform family before `COUNT(DISTINCT)`:
| Slug pattern | Normalised |
|---|---|
| `hn_*` | `hn` |
| `reddit_*` | `reddit` |
| `bluesky_*` | `bluesky` |
| `mastodon_*` | `mastodon` |
| `ghsa_*` | `ghsa` |
| `arxiv_*` | `arxiv` |
| `packagist_*` | `packagist` |
| `hf_*` / `hfpapers_daily` | `huggingface` |
| anything else | itself |
Verified on prod (last 48h):
| Bucket | Before | After |
|---|---|---|
| 4 platforms | 24 stories | 2 (the actual gems) |
| 3 platforms | 248 | 129 |
| 2 platforms | 507 | 213 |
| Total cross-platform | 779 | 344 |
435 of the previously-claimed "cross-platform" stories were just HN-internal-mirror inflation.
3. CVSS rendering — drop `CVSS:?` for empty scores
About a third of GHSA criticals ship with empty `cvss_score` in raw_json (e.g. `GHSA-qqqm-5547-774x: FileBrowser` quantum path-traversal). Old output rendered `[CRITICAL CVSS:?]` which reads like broken data, not absent data. Now `[CRITICAL]` alone — severity word does the job.
Out of scope (deferred)
- `opportunity_finder` "general" tag dominance + non-opportunity false positives ("What Is a Dickover?")
- `get_trends('laravel')` surfacing Packagist evergreens (guzzle / psr / container)
- `.NET` / JVM / SAP source coverage
- `MakePredictions` cross_platform_count family-collapse — same root cause but that path already excludes its own family, so impact is smaller; `cross_platform` class is dropped in v2 anyway
Test plan
- Pint clean
- Suite: 217 pass / 27 skip / 0 fail
- Verified the dedup SQL against prod (Symfony 46 → 1)
- Verified the cross-platform collapse against prod (779 → 344)
- CVSS rendering change is pure string formatting, no DB
- After deploy: re-run `stack_radar(["laravel","php","symfony"])` and confirm one row per CVE
- After deploy: re-run `get_velocity` and confirm the "[N sources]" tags actually reflect distinct communities