muaddib-scanner 2.10.74 → 2.10.78

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.10.74",
3
+ "version": "2.10.78",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -804,7 +804,6 @@ async function scanPackage(name, version, ecosystem, tarballUrl, registryMeta, s
804
804
  appendDetection(name, version, ecosystem, findingTypes, maxSeverity);
805
805
  recordTrainingSample(result, { name, version, ecosystem, label: 'suspect', tier, sandboxResult, registryMeta: meta, unpackedSize: meta.unpackedSize, npmRegistryMeta, fileCountTotal, hasTests });
806
806
 
807
- dailyAlerts.push({ name, version, ecosystem, findingsCount: result.summary.total, tier });
808
807
  // Persist alert locally for ALL suspects (independent of webhook filtering)
809
808
  const alertData = buildAlertData(name, version, ecosystem, result, sandboxResult);
810
809
  persistAlert(name, version, ecosystem, alertData);
@@ -832,6 +831,9 @@ async function scanPackage(name, version, ecosystem, tarballUrl, registryMeta, s
832
831
  } else if (ecosystem === 'npm' && hasHighConfidenceThreat(result)) {
833
832
  console.log(`[MONITOR] REPUTATION BYPASS: ${name} has high-confidence threat — using raw score`);
834
833
  }
834
+
835
+ // Record daily alert with post-reputation score for top suspects ranking
836
+ dailyAlerts.push({ name, version, ecosystem, findingsCount: result.summary.total, score: adjustedResult.summary.riskScore || 0, tier });
835
837
  // LLM Detective: AI-powered analysis for T1a/T1b suspects
836
838
  let llmResult = null;
837
839
  if ((tier === '1a' || tier === '1b') && (adjustedResult.summary.riskScore || 0) >= 25) {
@@ -794,7 +794,7 @@ function buildDailyReportEmbed(stats, dailyAlerts) {
794
794
 
795
795
  // Prefer in-memory dailyAlerts for top suspects (richer data), fallback to disk
796
796
  const top3 = dailyAlerts.length > 0
797
- ? dailyAlerts.slice().sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 3)
797
+ ? dailyAlerts.slice().sort((a, b) => (b.score || 0) - (a.score || 0) || b.findingsCount - a.findingsCount).slice(0, 3)
798
798
  : diskTop3;
799
799
 
800
800
  const top3Text = top3.length > 0
@@ -802,7 +802,8 @@ function buildDailyReportEmbed(stats, dailyAlerts) {
802
802
  const name = a.ecosystem ? `${a.ecosystem}/${a.name || a.package}` : (a.name || a.package);
803
803
  const version = a.version || 'N/A';
804
804
  const count = a.findingsCount || (a.findings ? a.findings.length : 0);
805
- return `${i + 1}. **${name}@${version}** ${count} finding(s)`;
805
+ const scoreText = a.score != null ? `score ${a.score}, ` : '';
806
+ return `${i + 1}. **${name}@${version}** — ${scoreText}${count} finding(s)`;
806
807
  }).join('\n')
807
808
  : 'None';
808
809
 
@@ -946,7 +947,7 @@ async function sendDailyReport(stats, dailyAlerts, recentlyScanned, downloadsCac
946
947
  deferredProcessed: stats.deferredProcessed || 0,
947
948
  deferredExpired: stats.deferredExpired || 0,
948
949
  changesStreamPackages: stats.changesStreamPackages || 0,
949
- topSuspects: dailyAlerts.slice().sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 10)
950
+ topSuspects: dailyAlerts.slice().sort((a, b) => (b.score || 0) - (a.score || 0) || b.findingsCount - a.findingsCount).slice(0, 10)
950
951
  });
951
952
 
952
953
  // Send webhook only if configured
@@ -39,16 +39,37 @@
39
39
  // fesm*/, esm/, esm5/, esm2015/, esm2020/, bundles/, assets/, chunks/, _app/)
40
40
  // - Basename suffixes (.min.js, .bundle.js, .umd.js, .esm.js, .es.js,
41
41
  // .common.js, .max.js, .prod.js, .production.js, + .cjs / .mjs variants)
42
+ // - Double-extension bundler outputs (index.cjs.js, index.esm.js, index.umd.js
43
+ // at package root — common pattern for @equinor/*, tsdx/rollup bundled libs)
42
44
  // - Hash-suffixed chunks (esbuild/vite/rollup/webpack convention):
43
45
  // `basename-[a-f0-9]{6,16}.js|mjs|cjs`
46
+ // - Tool-specific subdirectories that contain vendored bundles (v2.10.75):
47
+ // * `lib/[name]Bundle*/` — Playwright-style `lib/utilsBundleImpl/`
48
+ // * `.yarn/releases/` — vendored yarn/pnpm releases shipped in template packages
49
+ // * `sys/(node|browser|deno)/` — Stencil-style platform-specific bundle
50
+ // * `compiled/` — SWC/Stencil compiled output
51
+ // * `typings/` — only if matches a .d.ts file (defensive)
44
52
  const BUNDLE_PATH_RE = new RegExp(
45
- // Path prefix group
53
+ // Path prefix group (directories that almost always contain bundled output)
46
54
  '(?:^|[/\\\\])' +
47
- '(?:dist|build|out|output|browser|bundles|assets|chunks|_app|' +
55
+ '(?:dist|build|out|output|browser|bundles|assets|chunks|_app|compiled|' +
48
56
  'lib[/\\\\]bundled|fesm\\d*|esm|esm5|esm2015|esm2020)' +
49
57
  '[/\\\\]' +
50
- // OR basename suffix group
51
- '|\\.(?:min|bundle|umd|esm|es|common|max|prod|production)\\.(?:m?js|cjs)$' +
58
+ // OR Playwright-style lib/xxxBundle*/ (e.g. lib/utilsBundleImpl/, lib/mcpBundleImpl/,
59
+ // lib/transform/babelBundleImpl.js) — matches the directory form
60
+ // `lib/.../xxxBundleImpl/index.js` and the flat form `lib/.../xxxBundleImpl.js`
61
+ // at any depth under lib/.
62
+ '|(?:^|[/\\\\])lib[/\\\\][^\\n]*[Bb]undle[\\w-]*(?:[/\\\\]|\\.(?:m?js|cjs)$)' +
63
+ // OR vendored yarn/pnpm releases (@backstage/create-app templates etc.)
64
+ '|(?:^|[/\\\\])\\.yarn[/\\\\]releases[/\\\\]' +
65
+ '|(?:^|[/\\\\])\\.pnpm[/\\\\](?:releases|dist)[/\\\\]' +
66
+ // OR Stencil-style sys/(node|browser|deno) containing compiled platform bundles
67
+ '|(?:^|[/\\\\])sys[/\\\\](?:node|browser|deno)[/\\\\]' +
68
+ // OR basename suffix group (single extension)
69
+ '|\\.(?:min|bundle|umd|esm|es|cjs|common|max|prod|production|iife)\\.(?:m?js|cjs)$' +
70
+ // OR double-extension bundler outputs at root: index.cjs.js, index.esm.js, etc.
71
+ // Anchored by `^` or path separator + basename with exactly the double extension.
72
+ '|(?:^|[/\\\\])[\\w-]+\\.(?:cjs|esm|umd|es|iife|min)\\.js$' +
52
73
  // OR hash-suffixed chunk
53
74
  '|(?:^|[/\\\\])[\\w-]+[-.][a-f0-9]{6,16}\\.(?:m?js|cjs)$',
54
75
  'i'
@@ -131,6 +152,13 @@ function hasBundleVetoSignal(threats, targetFile) {
131
152
  if (!Array.isArray(threats) || !targetFile) return false;
132
153
  for (const t of threats) {
133
154
  if (t.file !== targetFile) continue;
155
+ // v2.10.75 fix: a LOW severity threat should never block the bundle downgrade
156
+ // of unrelated co-occurring threats. Typical regression case: a locale file
157
+ // (locales/fa-IR/*.js) contains `unicode_invisible_injection` at LOW (already
158
+ // downgraded by `isLocaleFile` in obfuscation.js) but also contains bundler
159
+ // helpers. Before this fix, the LOW unicode signal vetoed the bundle downgrade
160
+ // of the other threats, so the package scored higher than pre-v2.10.74.
161
+ if (t.severity === 'LOW') continue;
134
162
  if (VETO_TYPES.has(t.type)) return true;
135
163
  if (t.type === 'env_access' && t.message && SENSITIVE_ENV_RE.test(t.message)) {
136
164
  return true;