muaddib-scanner 2.11.131 → 2.11.132
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
package/src/monitor/classify.js
CHANGED
|
@@ -405,6 +405,17 @@ function evaluateCacheTrigger(name, docMeta, doc, opts = {}) {
|
|
|
405
405
|
return { shouldCache: true, reason: 'first_publish', retentionDays: TARBALL_CACHE_DEFAULT_RETENTION_DAYS };
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
// Trigger 4: account-takeover / burst -- 30-day retention (high-risk fast-takedown class).
|
|
409
|
+
// These are batch/registry-derived (published version vs dist-tags.latest; per-name recent
|
|
410
|
+
// version count), NOT knowable from name+docMeta at ingestion, so the caller
|
|
411
|
+
// (preResolveNpmBatch) computes them from _npmInfo and passes opts.{atoSignal,isBurst}.
|
|
412
|
+
// Closes the Leo Platform gap (June 2026): 20 existing packages re-published at non-latest
|
|
413
|
+
// versions matched neither typosquat nor first_publish -> never prefetched -> the malicious
|
|
414
|
+
// tarballs were 404'd before scan. ATO catches that class; burst catches Miasma-style floods.
|
|
415
|
+
if (opts.atoSignal || opts.isBurst) {
|
|
416
|
+
return { shouldCache: true, reason: opts.atoSignal ? 'ato' : 'burst', retentionDays: TARBALL_CACHE_HIGH_RISK_RETENTION_DAYS };
|
|
417
|
+
}
|
|
418
|
+
|
|
408
419
|
return { shouldCache: false, reason: '', retentionDays: 0 };
|
|
409
420
|
}
|
|
410
421
|
|
package/src/monitor/ingestion.js
CHANGED
|
@@ -598,6 +598,14 @@ function preResolveShouldShed(scanQueue) {
|
|
|
598
598
|
// in-flight work, not all the chunks that already completed. When scanQueue
|
|
599
599
|
// is omitted (unit tests, lib usage), items are only mutated in place and the
|
|
600
600
|
// caller decides when to push.
|
|
601
|
+
// Burst threshold for the capture-at-publish prefetch trigger. Mirrors
|
|
602
|
+
// BURST_PREALERT_MIN_VERSIONS (queue.js:212) and reads the SAME env var so ops tunes
|
|
603
|
+
// one knob. Per-name version count in the recent-publish window.
|
|
604
|
+
const BURST_MIN_VERSIONS_PREFETCH = (() => {
|
|
605
|
+
const n = parseInt(process.env.MUADDIB_BURST_MIN_VERSIONS, 10);
|
|
606
|
+
return Number.isFinite(n) && n >= 2 ? n : 10;
|
|
607
|
+
})();
|
|
608
|
+
|
|
601
609
|
async function preResolveNpmBatch(items, stats, scanQueue) {
|
|
602
610
|
if (!items || items.length === 0) return;
|
|
603
611
|
const start = Date.now();
|
|
@@ -644,6 +652,25 @@ async function preResolveNpmBatch(items, stats, scanQueue) {
|
|
|
644
652
|
// weekly_downloads (from getWeeklyDownloads) so the triage block in
|
|
645
653
|
// queue.js can read meta directly without re-fetching.
|
|
646
654
|
item._npmInfo = npmInfo;
|
|
655
|
+
// Capture-at-publish trigger for the fast-takedown class (Leo Platform / Miasma,
|
|
656
|
+
// June 2026). ATO (published version != dist-tags.latest) and per-name burst are
|
|
657
|
+
// only knowable from the registry data fetched here, so the name-only
|
|
658
|
+
// evaluateCacheTrigger at ingestion (change.doc is null post-2025) cannot set them.
|
|
659
|
+
// Feed them in now so the burst/ATO subset is prefetched too — only when no
|
|
660
|
+
// higher-priority (ioc/typosquat) trigger already fired. queue.js still computes
|
|
661
|
+
// the same signals later for scan-queue protection + extras enqueue (idempotent).
|
|
662
|
+
if (!item._cacheTrigger) {
|
|
663
|
+
const atoSignal = !!(npmInfo.latestTagVersion && item.version &&
|
|
664
|
+
item.version !== npmInfo.latestTagVersion);
|
|
665
|
+
const burstCount = Number.isFinite(npmInfo.recentWindowCount)
|
|
666
|
+
? npmInfo.recentWindowCount
|
|
667
|
+
: ((Array.isArray(npmInfo.recentVersions) ? npmInfo.recentVersions.length : 0) + 1);
|
|
668
|
+
const isBurst = burstCount >= BURST_MIN_VERSIONS_PREFETCH;
|
|
669
|
+
if (atoSignal || isBurst) {
|
|
670
|
+
const trig = evaluateCacheTrigger(item.name, null, null, { atoSignal, isBurst });
|
|
671
|
+
if (trig.shouldCache) item._cacheTrigger = trig;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
647
674
|
resolved++;
|
|
648
675
|
} else {
|
|
649
676
|
failed++;
|