muaddib-scanner 2.11.88 → 2.11.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.11.88",
3
+ "version": "2.11.89",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "target": "node_modules",
3
- "timestamp": "2026-06-11T08:02:35.162Z",
3
+ "timestamp": "2026-06-11T08:17:41.614Z",
4
4
  "threats": [
5
5
  {
6
6
  "type": "string_mutation_obfuscation",
@@ -150,10 +150,14 @@ const BURST_PREALERT_MIN_VERSIONS = (() => {
150
150
  const n = parseInt(process.env.MUADDIB_BURST_MIN_VERSIONS, 10);
151
151
  return Number.isFinite(n) && n >= 2 ? n : 10;
152
152
  })();
153
- // Dedup burst pings: one per name per process window (bounded cleared at the cap so it
154
- // can never grow without limit, CLAUDE.md §2).
155
- const _burstAlerted = new Set();
153
+ // Burst ping throttle (FPR/notif audit 2026-06): name -> last burst-alert timestamp.
154
+ // Was a lifetime Set (dedup once per process), which both (a) silenced a genuine
155
+ // re-burst days later and (b) on a process that runs for weeks accumulated spam from
156
+ // every monorepo/CI nightly that re-bursts. Now a 24h-cooldown Map: one alert per
157
+ // package per day max. Bounded — cleared at the cap so it can never grow without limit.
158
+ const _burstAlerted = new Map();
156
159
  const BURST_ALERTED_MAX = 20_000;
160
+ const BURST_ALERT_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24h
157
161
 
158
162
  // Stage 3 — sandbox gate. Static-score threshold below which T1b/T2 packages
159
163
  // are NOT sandboxed (static result alone is authoritative). Tightens the prior
@@ -1530,9 +1534,22 @@ async function resolveTarballAndScan(item, stats, dailyAlerts, recentlyScanned,
1530
1534
  const isBurst = burstCount >= BURST_PREALERT_MIN_VERSIONS;
1531
1535
  if (isBurst) {
1532
1536
  item.isBurst = true;
1533
- if (!_burstAlerted.has(item.name)) {
1537
+ // Anti-flood (notification only — the burst versions are STILL queued and scanned
1538
+ // below regardless; muting the heads-up never weakens detection):
1539
+ // 1) Established packages (mature + many versions) bursting are monorepo / CI
1540
+ // nightly churn, not the Shai-Hulud account-takeover signal (that is a NEW /
1541
+ // low-reputation package suddenly bursting). A real takeover of an established
1542
+ // package is still caught by the per-version scan + the atoSignal above.
1543
+ // 2) 24h cooldown per package so a package re-bursting all day pings at most once.
1544
+ const _np = item._npmInfo || npmInfo || {};
1545
+ const _established = Number.isFinite(_np.age_days) && _np.age_days > 730 &&
1546
+ Number.isFinite(_np.version_count) && _np.version_count > 100;
1547
+ const _now = Date.now();
1548
+ const _last = _burstAlerted.get(item.name);
1549
+ const _onCooldown = _last && (_now - _last) < BURST_ALERT_COOLDOWN_MS;
1550
+ if (!_established && !_onCooldown) {
1534
1551
  if (_burstAlerted.size >= BURST_ALERTED_MAX) _burstAlerted.clear();
1535
- _burstAlerted.add(item.name);
1552
+ _burstAlerted.set(item.name, _now);
1536
1553
  stats.burstPreAlerts = (stats.burstPreAlerts || 0) + 1;
1537
1554
  console.log(`[MONITOR] BURST PRE-ALERT: ${item.name} — ${burstCount} versions in the recent window`);
1538
1555
  sendBurstPreAlert(item.name, burstCount, item.ecosystem).catch(err => {