muaddib-scanner 2.11.94 → 2.11.95

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.94",
3
+ "version": "2.11.95",
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-11T12:30:31.641Z",
3
+ "timestamp": "2026-06-11T13:00:12.001Z",
4
4
  "threats": [
5
5
  {
6
6
  "type": "string_mutation_obfuscation",
@@ -17,7 +17,6 @@
17
17
 
18
18
  const dns = require('dns');
19
19
  const { debugLog } = require('../utils.js');
20
- const { isShadowEnabled, recordShadowDivergence } = require('../shared/shadow.js');
21
20
 
22
21
  const MX_TIMEOUT_MS = 3000;
23
22
  const MX_CACHE_TTL = 30 * 24 * 60 * 60 * 1000; // 30 days
@@ -226,8 +225,11 @@ async function fetchRdap(domain, options = {}) {
226
225
  }
227
226
 
228
227
  /**
229
- * Returns true if the domain registration came AFTER the package was first
230
- * published (with a 30-day margin to absorb timing edges).
228
+ * @deprecated Retired from the production emission path by the 2026-06-11 V2
229
+ * flip `checkCompromisedDomain` now emits via `isCompromisedDomainV2`. Kept
230
+ * (exported + unit-tested) as the documented former semantics: creation AFTER
231
+ * first publish with a 30-day pre-publish margin. The margin was the source of
232
+ * ~23% of historical FPs (a dev buying a domain weeks before shipping v1).
231
233
  */
232
234
  function isCompromisedDomain(creationDateISO, packageCreatedAtISO) {
233
235
  if (!creationDateISO || !packageCreatedAtISO) return false;
@@ -292,12 +294,16 @@ function isCompromisedDomainV2(creationDateISO, firstPublishISO, domain) {
292
294
  }
293
295
 
294
296
  /**
295
- * F1 entry point.
297
+ * F1 entry point — emits `compromised_email_domain` (HIGH×high = 10, composite-only:
298
+ * sub-T1, and the tier-1b corroboration gate requires ≥2 distinct signals). LIVE
299
+ * semantics since 2026-06-11 = `isCompromisedDomainV2` (strict creation > first
300
+ * publish, public email providers excluded). The former V1 30-day-margin
301
+ * semantics is retired (see isCompromisedDomain @deprecated).
296
302
  * @param {object|null} meta - Digested metadata. Reads maintainer_emails + created_at
297
303
  * (= the package's FIRST publish date, both npm and PyPI sides).
298
304
  * @param {object} options - { fetchRdap } for tests to inject a mock.
299
- * { shadowCtx: {name, version, ecosystem} } identifies the scanned package in
300
- * shadow-divergence records (optional without it divergences log package:null).
305
+ * { shadowCtx } is accepted but ignored since the flip (kept so existing callers
306
+ * in processor.js / pypi-maintainer.js need no change).
301
307
  * @returns {Promise<Array>} threats array
302
308
  */
303
309
  async function checkCompromisedDomain(meta, options = {}) {
@@ -321,25 +327,12 @@ async function checkCompromisedDomain(meta, options = {}) {
321
327
  continue;
322
328
  }
323
329
  if (!rdap || !rdap.creationDate) continue;
324
- // SHADOW (zero effect on the threats emitted below): compare the live V1
325
- // verdict with the V2 candidate and log only disagreements. Adjudication =
326
- // scripts/backtest-email-domain.js replay + `muaddib shadow-report`.
327
- try {
328
- if (isShadowEnabled()) {
329
- const v1 = isCompromisedDomain(rdap.creationDate, meta.created_at);
330
- const v2 = isCompromisedDomainV2(rdap.creationDate, meta.created_at, domain);
331
- if (v1 !== v2) {
332
- const ctx = options.shadowCtx || {};
333
- recordShadowDivergence({
334
- detector: 'compromised_email_domain',
335
- package: ctx.name, version: ctx.version, ecosystem: ctx.ecosystem,
336
- oldVerdict: v1, newVerdict: v2,
337
- evidence: { domain, creationDate: rdap.creationDate, firstPublish: meta.created_at, oldMarginDays: 30 }
338
- });
339
- }
340
- }
341
- } catch { /* shadow must never affect the scan */ }
342
- if (isCompromisedDomain(rdap.creationDate, meta.created_at)) {
330
+ // V2 is the LIVE semantics since the 2026-06-11 flip (backtest #545 era:
331
+ // V2 strict-creation + public-provider exclusion killed 307/1346 historical
332
+ // FP alerts — the @tronweb3/* wallet-adapter monorepo flood and added 0
333
+ // new flags / 0 FN; V2 ⊂ V1, so no shadow net is needed). The old V1 margin
334
+ // shadow hook is retired with the flip.
335
+ if (isCompromisedDomainV2(rdap.creationDate, meta.created_at, domain)) {
343
336
  const cd = rdap.creationDate.slice(0, 10);
344
337
  const pd = meta.created_at.slice(0, 10);
345
338
  threats.push({