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 +1 -1
- package/src/response/playbooks.js +5 -0
- package/src/rules/index.js +25 -2
- package/src/scanner/typosquat.js +30 -1
- package/src/scoring.js +24 -0
package/package.json
CHANGED
|
@@ -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.',
|
package/src/rules/index.js
CHANGED
|
@@ -350,9 +350,13 @@ const RULES = {
|
|
|
350
350
|
dependency_typosquat: {
|
|
351
351
|
id: 'MUADDIB-TYPO-002',
|
|
352
352
|
name: 'Dependency Boundary-Squat',
|
|
353
|
-
|
|
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: {
|
package/src/scanner/typosquat.js
CHANGED
|
@@ -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
|
-
|
|
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'],
|