muaddib-scanner 2.11.19 → 2.11.20

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.19",
3
+ "version": "2.11.20",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -168,6 +168,11 @@ const PLAYBOOKS = {
168
168
  'Le code charge cette dep typosquattee via require/import. Si ce n\'est pas intentionnel, supprimer la dep et la reference, puis reinstaller avec --ignore-scripts.',
169
169
  dependency_typosquat_require:
170
170
  'CRITIQUE — pattern Axios UNC1069 detecte: dep typosquattee declaree ET chargee dans le code. Le wrapper apparent est probablement legitime mais sa dep contient le payload. Bloquer l\'install (--ignore-scripts), supprimer la dep, auditer le history de modifications.',
171
+ // RT-C1-FPR (audit 2026-05)
172
+ typosquat_lifecycle:
173
+ 'CRITIQUE — pattern boundary-squat avec hook lifecycle: la dep typosquattee sera executee a l\'install. Bloquer l\'install (--ignore-scripts), supprimer la dep, regenerer les secrets exposes, auditer le history.',
174
+ typosquat_dataflow:
175
+ 'IMPORTANT — dep boundary-squat coexistante avec un flux credentials → reseau dans le code. Verifier si la dep est require()d depuis le fichier d\'exfil. Si oui = CRITICAL (compound dependency_typosquat_require complementaire).',
171
176
 
172
177
  dangerous_call_function:
173
178
  'Appel new Function() detecte. Equivalent a eval(). Verifier la source des donnees.',
@@ -350,9 +350,13 @@ const RULES = {
350
350
  dependency_typosquat: {
351
351
  id: 'MUADDIB-TYPO-002',
352
352
  name: 'Dependency Boundary-Squat',
353
- severity: 'HIGH',
353
+ // RT-C1-FPR (audit 2026-05): demoted HIGH -> MEDIUM. Boundary-squat alone is
354
+ // a name-resemblance heuristic without execution proof. Compounds typosquat_lifecycle
355
+ // (CRITICAL), typosquat_dataflow (HIGH), dependency_typosquat_require (CRITICAL)
356
+ // escalate when co-occurring with real execution/exfil signals.
357
+ severity: 'MEDIUM',
354
358
  confidence: 'high',
355
- description: 'Une dependance declaree porte le nom d\'un package populaire prefixe/suffixe d\'un token suspect (Axios UNC1069, mars 2026). Le wrapper innocent declare un sub-dep malveillant.',
359
+ description: 'Une dependance declaree porte le nom d\'un package populaire prefixe/suffixe d\'un token suspect (Axios UNC1069, mars 2026). Le wrapper innocent declare un sub-dep malveillant. Signal solo MEDIUM, escalade CRITICAL via compounds lifecycle/dataflow/require.',
356
360
  references: [
357
361
  'https://snyk.io/blog/typosquatting-attacks/',
358
362
  'https://attack.mitre.org/techniques/T1195/002/'
@@ -377,6 +381,25 @@ const RULES = {
377
381
  references: ['https://attack.mitre.org/techniques/T1195/002/'],
378
382
  mitre: 'T1195.002'
379
383
  },
384
+ // RT-C1-FPR (audit 2026-05): compounds escaladant dependency_typosquat solo (MEDIUM)
385
+ typosquat_lifecycle: {
386
+ id: 'MUADDIB-COMPOUND-014',
387
+ name: 'Boundary-Squat Dep + Lifecycle Hook',
388
+ severity: 'CRITICAL',
389
+ confidence: 'high',
390
+ description: 'Dependance boundary-squat declaree avec script lifecycle (preinstall/postinstall) — install-time payload delivery via typosquat sub-dep.',
391
+ references: ['https://attack.mitre.org/techniques/T1195/002/'],
392
+ mitre: 'T1195.002'
393
+ },
394
+ typosquat_dataflow: {
395
+ id: 'MUADDIB-COMPOUND-015',
396
+ name: 'Boundary-Squat Dep + Suspicious Dataflow',
397
+ severity: 'HIGH',
398
+ confidence: 'high',
399
+ description: 'Dependance boundary-squat declaree avec flux de donnees suspect (lecture credentials + envoi reseau) — typosquat dep co-occurring with exfiltration.',
400
+ references: ['https://attack.mitre.org/techniques/T1195/002/'],
401
+ mitre: 'T1195.002'
402
+ },
380
403
 
381
404
  // Package.json script patterns
382
405
  curl_pipe_sh: {
@@ -30,6 +30,23 @@ const POPULAR_PACKAGES = [
30
30
  'crypto-js'
31
31
  ];
32
32
 
33
+ // RT-C1-FPR (audit 2026-05): frameworks dont les packages d'ecosysteme ont la
34
+ // convention <framework>-<role>-<extension> (eslint-plugin-react, babel-preset-env,
35
+ // gatsby-plugin-mdx, eslint-import-resolver-typescript). Un dep dont le PREMIER
36
+ // segment hyphene est dans ce Set est une extension legitime du framework, jamais
37
+ // un squat d'un autre package -- quelles que soient les autres segments.
38
+ // EXCLUS volontairement : react, vue, angular, svelte, typescript. Leurs packages
39
+ // d'ecosysteme ont le framework EN SUFFIXE (eslint-plugin-react), donc la convention
40
+ // prefixe ne s'applique pas et conserver la detection de squats `react-token-exfil`
41
+ // reste prioritaire.
42
+ const ECOSYSTEM_FRAMEWORK_PREFIXES = new Set([
43
+ 'eslint', 'babel', 'webpack', 'rollup', 'vite', 'parcel', 'esbuild', 'swc', 'tsup',
44
+ 'gatsby', 'next', 'nuxt', 'remix', 'expo', 'metro',
45
+ 'jest', 'mocha', 'karma', 'cypress', 'playwright', 'vitest',
46
+ 'postcss', 'stylelint', 'prettier', 'typedoc',
47
+ 'storybook', 'docusaurus', 'astro'
48
+ ]);
49
+
33
50
  // RT-C1: Hyphen tokens that legitimately PREFIX or SUFFIX popular package names.
34
51
  // `<token>-<popular>` or `<popular>-<token>` is considered benign when the extra
35
52
  // token is in this set (ecosystem qualifiers, framework prefixes, official scopes).
@@ -372,7 +389,11 @@ async function scanTyposquatting(targetPath) {
372
389
  + bMatch.original + '" (extra token: "' + bMatch.extra + '"). Axios UNC1069 pattern.';
373
390
  threats.push({
374
391
  type: 'dependency_typosquat',
375
- severity: 'HIGH',
392
+ // RT-C1-FPR (audit 2026-05): demoted HIGH -> MEDIUM. Boundary-squat alone is
393
+ // a heuristic signal (name resemblance, no execution proof). The compounds
394
+ // typosquat_lifecycle (CRITICAL) and typosquat_dataflow (HIGH) escalate when
395
+ // co-occurring with real execution/exfil signals.
396
+ severity: 'MEDIUM',
376
397
  message: declMsg,
377
398
  file: 'package.json',
378
399
  details: {
@@ -557,6 +578,14 @@ function findTyposquatMatch(name) {
557
578
  }
558
579
 
559
580
  function isLegitimateVariant(name) {
581
+ // RT-C1-FPR (audit 2026-05): ecosystem framework prefix -> legitimate extension.
582
+ // Ex: eslint-import-resolver-typescript, babel-loader-svelte, gatsby-source-fs.
583
+ const firstHyphen = name.indexOf('-');
584
+ if (firstHyphen > 0) {
585
+ const prefix = name.slice(0, firstHyphen);
586
+ if (ECOSYSTEM_FRAMEWORK_PREFIXES.has(prefix)) return true;
587
+ }
588
+
560
589
  // Suffixes legitimes qui ne sont PAS du typosquatting
561
590
  const legitimateSuffixes = [
562
591
  '-cli', '-core', '-utils', '-plugin', '-loader', '-webpack',
package/src/scoring.js CHANGED
@@ -509,6 +509,30 @@ const SCORING_COMPOUNDS = [
509
509
  message: 'Boundary-squat dependency declared AND require()d in code — Axios UNC1069 pattern (scoring compound).',
510
510
  fileFrom: 'dependency_typosquat_used'
511
511
  },
512
+ {
513
+ // RT-C1-FPR (audit 2026-05): boundary-squat dep + lifecycle hook → install-time
514
+ // payload delivery via typosquat sub-dep. Mirror of dependency_typosquat_require
515
+ // but with lifecycle instead of _used: stronger signal — proves install-time
516
+ // execution intent without requiring explicit require() in scanned code.
517
+ type: 'typosquat_lifecycle',
518
+ requires: ['dependency_typosquat', 'lifecycle_script'],
519
+ severity: 'CRITICAL',
520
+ message: 'Boundary-squat dependency + lifecycle hook — install-time payload delivery via typosquat sub-dep (scoring compound).',
521
+ fileFrom: 'dependency_typosquat'
522
+ // No sameFile: both are package.json-level
523
+ },
524
+ {
525
+ // RT-C1-FPR (audit 2026-05): boundary-squat dep + suspicious dataflow → typosquat
526
+ // dep co-occurring with credential exfil. Mirror of lifecycle_dataflow (HIGH) —
527
+ // co-occurrence without direct causal link, so HIGH not CRITICAL.
528
+ type: 'typosquat_dataflow',
529
+ requires: ['dependency_typosquat', 'suspicious_dataflow'],
530
+ severity: 'HIGH',
531
+ message: 'Boundary-squat dependency + suspicious dataflow — typosquat dep co-occurring with credential exfil (scoring compound).',
532
+ fileFrom: 'suspicious_dataflow',
533
+ // No sameFile: dep is package.json, dataflow is src/*.js
534
+ excludeIfBundled: true
535
+ },
512
536
  {
513
537
  type: 'lifecycle_inline_exec',
514
538
  requires: ['lifecycle_script', 'node_inline_exec'],