muaddib-scanner 2.5.7 → 2.5.8

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/iocs/builtin.yaml CHANGED
@@ -7,39 +7,108 @@ packages:
7
7
  version: "4.1.1"
8
8
  source: shai-hulud-v1
9
9
  - name: "ng2-file-upload"
10
- version: "*"
10
+ version: "7.0.2"
11
+ source: shai-hulud-v1
12
+ - name: "ng2-file-upload"
13
+ version: "7.0.3"
14
+ source: shai-hulud-v1
15
+ - name: "ng2-file-upload"
16
+ version: "8.0.1"
17
+ source: shai-hulud-v1
18
+ - name: "ng2-file-upload"
19
+ version: "8.0.2"
20
+ source: shai-hulud-v1
21
+ - name: "ng2-file-upload"
22
+ version: "8.0.3"
23
+ source: shai-hulud-v1
24
+ - name: "ng2-file-upload"
25
+ version: "9.0.1"
11
26
  source: shai-hulud-v1
12
27
  - name: "ngx-bootstrap"
13
- version: "*"
28
+ version: "18.1.4"
29
+ source: shai-hulud-v1
30
+ - name: "ngx-bootstrap"
31
+ version: "19.0.3"
32
+ source: shai-hulud-v1
33
+ - name: "ngx-bootstrap"
34
+ version: "19.0.4"
35
+ source: shai-hulud-v1
36
+ - name: "ngx-bootstrap"
37
+ version: "20.0.3"
38
+ source: shai-hulud-v1
39
+ - name: "ngx-bootstrap"
40
+ version: "20.0.4"
41
+ source: shai-hulud-v1
42
+ - name: "ngx-bootstrap"
43
+ version: "20.0.5"
44
+ source: shai-hulud-v1
45
+ - name: "ngx-bootstrap"
46
+ version: "20.0.6"
14
47
  source: shai-hulud-v1
15
48
 
16
49
  # Shai-Hulud v2 (novembre 2025)
17
50
  - name: "@asyncapi/specs"
18
- version: "*"
51
+ version: "6.8.2"
52
+ source: shai-hulud-v2
53
+ - name: "@asyncapi/specs"
54
+ version: "6.8.3"
55
+ source: shai-hulud-v2
56
+ - name: "@asyncapi/specs"
57
+ version: "6.9.1"
58
+ source: shai-hulud-v2
59
+ - name: "@asyncapi/specs"
60
+ version: "6.10.1"
19
61
  source: shai-hulud-v2
20
62
  - name: "@asyncapi/openapi-schema-parser"
21
- version: "*"
63
+ version: "3.0.25"
64
+ source: shai-hulud-v2
65
+ - name: "@asyncapi/openapi-schema-parser"
66
+ version: "3.0.26"
22
67
  source: shai-hulud-v2
23
68
  - name: "get-them-args"
24
- version: "*"
69
+ version: "1.3.3"
25
70
  source: shai-hulud-v2
26
71
  - name: "kill-port"
27
- version: "*"
72
+ version: "2.0.2"
73
+ source: shai-hulud-v2
74
+ - name: "kill-port"
75
+ version: "2.0.3"
28
76
  source: shai-hulud-v2
29
77
  - name: "shell-exec"
30
- version: "*"
78
+ version: "1.1.3"
79
+ source: shai-hulud-v2
80
+ - name: "shell-exec"
81
+ version: "1.1.4"
31
82
  source: shai-hulud-v2
32
83
  - name: "posthog-node"
33
- version: "*"
84
+ version: "4.18.1"
85
+ source: shai-hulud-v2
86
+ - name: "posthog-node"
87
+ version: "5.11.3"
88
+ source: shai-hulud-v2
89
+ - name: "posthog-node"
90
+ version: "5.13.3"
34
91
  source: shai-hulud-v2
35
92
  - name: "posthog-js"
36
- version: "*"
93
+ version: "1.297.3"
37
94
  source: shai-hulud-v2
38
95
  - name: "@postman/tunnel-agent"
39
- version: "*"
96
+ version: "0.6.5"
97
+ source: shai-hulud-v2
98
+ - name: "@postman/tunnel-agent"
99
+ version: "0.6.6"
100
+ source: shai-hulud-v2
101
+ - name: "@postman/tunnel-agent"
102
+ version: "0.6.7"
40
103
  source: shai-hulud-v2
41
104
  - name: "@zapier/secret-scrubber"
42
- version: "*"
105
+ version: "1.1.3"
106
+ source: shai-hulud-v2
107
+ - name: "@zapier/secret-scrubber"
108
+ version: "1.1.4"
109
+ source: shai-hulud-v2
110
+ - name: "@zapier/secret-scrubber"
111
+ version: "1.1.5"
43
112
  source: shai-hulud-v2
44
113
 
45
114
  # Shai-Hulud v3 Golden Path (28 decembre 2025)
@@ -21,9 +21,21 @@ packages:
21
21
  - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
22
22
  mitre: T1195.002
23
23
 
24
- - id: SHAI-HULUD-V1-002
24
+ - id: SHAI-HULUD-V1-002a
25
25
  name: "ng2-file-upload"
26
- version: "*"
26
+ version: "7.0.2"
27
+ severity: critical
28
+ confidence: high
29
+ source: shai-hulud-v1
30
+ introduced: "2025-09-01"
31
+ description: "Package compromis par Shai-Hulud v1"
32
+ references:
33
+ - https://blog.phylum.io/shai-hulud-npm-worm
34
+ mitre: T1195.002
35
+
36
+ - id: SHAI-HULUD-V1-002b
37
+ name: "ng2-file-upload"
38
+ version: "7.0.3"
27
39
  severity: critical
28
40
  confidence: high
29
41
  source: shai-hulud-v1
@@ -33,9 +45,129 @@ packages:
33
45
  - https://blog.phylum.io/shai-hulud-npm-worm
34
46
  mitre: T1195.002
35
47
 
36
- - id: SHAI-HULUD-V1-003
48
+ - id: SHAI-HULUD-V1-002c
49
+ name: "ng2-file-upload"
50
+ version: "8.0.1"
51
+ severity: critical
52
+ confidence: high
53
+ source: shai-hulud-v1
54
+ introduced: "2025-09-01"
55
+ description: "Package compromis par Shai-Hulud v1"
56
+ references:
57
+ - https://blog.phylum.io/shai-hulud-npm-worm
58
+ mitre: T1195.002
59
+
60
+ - id: SHAI-HULUD-V1-002d
61
+ name: "ng2-file-upload"
62
+ version: "8.0.2"
63
+ severity: critical
64
+ confidence: high
65
+ source: shai-hulud-v1
66
+ introduced: "2025-09-01"
67
+ description: "Package compromis par Shai-Hulud v1"
68
+ references:
69
+ - https://blog.phylum.io/shai-hulud-npm-worm
70
+ mitre: T1195.002
71
+
72
+ - id: SHAI-HULUD-V1-002e
73
+ name: "ng2-file-upload"
74
+ version: "8.0.3"
75
+ severity: critical
76
+ confidence: high
77
+ source: shai-hulud-v1
78
+ introduced: "2025-09-01"
79
+ description: "Package compromis par Shai-Hulud v1"
80
+ references:
81
+ - https://blog.phylum.io/shai-hulud-npm-worm
82
+ mitre: T1195.002
83
+
84
+ - id: SHAI-HULUD-V1-002f
85
+ name: "ng2-file-upload"
86
+ version: "9.0.1"
87
+ severity: critical
88
+ confidence: high
89
+ source: shai-hulud-v1
90
+ introduced: "2025-09-01"
91
+ description: "Package compromis par Shai-Hulud v1"
92
+ references:
93
+ - https://blog.phylum.io/shai-hulud-npm-worm
94
+ mitre: T1195.002
95
+
96
+ - id: SHAI-HULUD-V1-003a
37
97
  name: "ngx-bootstrap"
38
- version: "*"
98
+ version: "18.1.4"
99
+ severity: critical
100
+ confidence: high
101
+ source: shai-hulud-v1
102
+ introduced: "2025-09-01"
103
+ description: "Package compromis par Shai-Hulud v1"
104
+ references:
105
+ - https://blog.phylum.io/shai-hulud-npm-worm
106
+ mitre: T1195.002
107
+
108
+ - id: SHAI-HULUD-V1-003b
109
+ name: "ngx-bootstrap"
110
+ version: "19.0.3"
111
+ severity: critical
112
+ confidence: high
113
+ source: shai-hulud-v1
114
+ introduced: "2025-09-01"
115
+ description: "Package compromis par Shai-Hulud v1"
116
+ references:
117
+ - https://blog.phylum.io/shai-hulud-npm-worm
118
+ mitre: T1195.002
119
+
120
+ - id: SHAI-HULUD-V1-003c
121
+ name: "ngx-bootstrap"
122
+ version: "19.0.4"
123
+ severity: critical
124
+ confidence: high
125
+ source: shai-hulud-v1
126
+ introduced: "2025-09-01"
127
+ description: "Package compromis par Shai-Hulud v1"
128
+ references:
129
+ - https://blog.phylum.io/shai-hulud-npm-worm
130
+ mitre: T1195.002
131
+
132
+ - id: SHAI-HULUD-V1-003d
133
+ name: "ngx-bootstrap"
134
+ version: "20.0.3"
135
+ severity: critical
136
+ confidence: high
137
+ source: shai-hulud-v1
138
+ introduced: "2025-09-01"
139
+ description: "Package compromis par Shai-Hulud v1"
140
+ references:
141
+ - https://blog.phylum.io/shai-hulud-npm-worm
142
+ mitre: T1195.002
143
+
144
+ - id: SHAI-HULUD-V1-003e
145
+ name: "ngx-bootstrap"
146
+ version: "20.0.4"
147
+ severity: critical
148
+ confidence: high
149
+ source: shai-hulud-v1
150
+ introduced: "2025-09-01"
151
+ description: "Package compromis par Shai-Hulud v1"
152
+ references:
153
+ - https://blog.phylum.io/shai-hulud-npm-worm
154
+ mitre: T1195.002
155
+
156
+ - id: SHAI-HULUD-V1-003f
157
+ name: "ngx-bootstrap"
158
+ version: "20.0.5"
159
+ severity: critical
160
+ confidence: high
161
+ source: shai-hulud-v1
162
+ introduced: "2025-09-01"
163
+ description: "Package compromis par Shai-Hulud v1"
164
+ references:
165
+ - https://blog.phylum.io/shai-hulud-npm-worm
166
+ mitre: T1195.002
167
+
168
+ - id: SHAI-HULUD-V1-003g
169
+ name: "ngx-bootstrap"
170
+ version: "20.0.6"
39
171
  severity: critical
40
172
  confidence: high
41
173
  source: shai-hulud-v1
@@ -48,9 +180,45 @@ packages:
48
180
  # ============================================
49
181
  # SHAI-HULUD v2 "The Second Coming" (Novembre 2025)
50
182
  # ============================================
51
- - id: SHAI-HULUD-V2-001
183
+ - id: SHAI-HULUD-V2-001a
52
184
  name: "@asyncapi/specs"
53
- version: "*"
185
+ version: "6.8.2"
186
+ severity: critical
187
+ confidence: high
188
+ source: shai-hulud-v2
189
+ introduced: "2025-11-01"
190
+ description: "Package compromis par Shai-Hulud v2 - inclut dead man's switch"
191
+ references:
192
+ - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
193
+ mitre: T1195.002
194
+
195
+ - id: SHAI-HULUD-V2-001b
196
+ name: "@asyncapi/specs"
197
+ version: "6.8.3"
198
+ severity: critical
199
+ confidence: high
200
+ source: shai-hulud-v2
201
+ introduced: "2025-11-01"
202
+ description: "Package compromis par Shai-Hulud v2 - inclut dead man's switch"
203
+ references:
204
+ - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
205
+ mitre: T1195.002
206
+
207
+ - id: SHAI-HULUD-V2-001c
208
+ name: "@asyncapi/specs"
209
+ version: "6.9.1"
210
+ severity: critical
211
+ confidence: high
212
+ source: shai-hulud-v2
213
+ introduced: "2025-11-01"
214
+ description: "Package compromis par Shai-Hulud v2 - inclut dead man's switch"
215
+ references:
216
+ - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
217
+ mitre: T1195.002
218
+
219
+ - id: SHAI-HULUD-V2-001d
220
+ name: "@asyncapi/specs"
221
+ version: "6.10.1"
54
222
  severity: critical
55
223
  confidence: high
56
224
  source: shai-hulud-v2
@@ -62,7 +230,7 @@ packages:
62
230
 
63
231
  - id: SHAI-HULUD-V2-002
64
232
  name: "get-them-args"
65
- version: "*"
233
+ version: "1.3.3"
66
234
  severity: critical
67
235
  confidence: high
68
236
  source: shai-hulud-v2
@@ -72,9 +240,21 @@ packages:
72
240
  - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
73
241
  mitre: T1195.002
74
242
 
75
- - id: SHAI-HULUD-V2-003
243
+ - id: SHAI-HULUD-V2-003a
76
244
  name: "kill-port"
77
- version: "*"
245
+ version: "2.0.2"
246
+ severity: critical
247
+ confidence: high
248
+ source: shai-hulud-v2
249
+ introduced: "2025-11-01"
250
+ description: "Package compromis par Shai-Hulud v2"
251
+ references:
252
+ - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
253
+ mitre: T1195.002
254
+
255
+ - id: SHAI-HULUD-V2-003b
256
+ name: "kill-port"
257
+ version: "2.0.3"
78
258
  severity: critical
79
259
  confidence: high
80
260
  source: shai-hulud-v2
@@ -84,9 +264,33 @@ packages:
84
264
  - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
85
265
  mitre: T1195.002
86
266
 
87
- - id: SHAI-HULUD-V2-004
267
+ - id: SHAI-HULUD-V2-004a
88
268
  name: "posthog-node"
89
- version: "*"
269
+ version: "4.18.1"
270
+ severity: critical
271
+ confidence: high
272
+ source: shai-hulud-v2
273
+ introduced: "2025-11-01"
274
+ description: "Package compromis par Shai-Hulud v2"
275
+ references:
276
+ - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
277
+ mitre: T1195.002
278
+
279
+ - id: SHAI-HULUD-V2-004b
280
+ name: "posthog-node"
281
+ version: "5.11.3"
282
+ severity: critical
283
+ confidence: high
284
+ source: shai-hulud-v2
285
+ introduced: "2025-11-01"
286
+ description: "Package compromis par Shai-Hulud v2"
287
+ references:
288
+ - https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack
289
+ mitre: T1195.002
290
+
291
+ - id: SHAI-HULUD-V2-004c
292
+ name: "posthog-node"
293
+ version: "5.13.3"
90
294
  severity: critical
91
295
  confidence: high
92
296
  source: shai-hulud-v2
@@ -98,7 +302,7 @@ packages:
98
302
 
99
303
  - id: SHAI-HULUD-V2-005
100
304
  name: "posthog-js"
101
- version: "*"
305
+ version: "1.297.3"
102
306
  severity: critical
103
307
  confidence: high
104
308
  source: shai-hulud-v2
@@ -0,0 +1,20 @@
1
+ {
2
+ "target": "npm/evil-pkg@1.0.0",
3
+ "timestamp": "2026-03-06T13:25:09.667Z",
4
+ "ecosystem": "npm",
5
+ "summary": {
6
+ "critical": 1,
7
+ "high": 0,
8
+ "medium": 0,
9
+ "low": 0,
10
+ "total": 1,
11
+ "riskLevel": "CRITICAL",
12
+ "riskScore": 25
13
+ },
14
+ "threats": [
15
+ {
16
+ "type": "known_malicious_package",
17
+ "severity": "CRITICAL"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "target": "npm/evil-pkg@1.0.0",
3
+ "timestamp": "2026-03-06T13:25:09.668Z",
4
+ "ecosystem": "npm",
5
+ "summary": {
6
+ "critical": 1,
7
+ "high": 0,
8
+ "medium": 0,
9
+ "low": 0,
10
+ "total": 1,
11
+ "riskLevel": "CRITICAL",
12
+ "riskScore": 25
13
+ },
14
+ "threats": [
15
+ {
16
+ "type": "known_malicious_package",
17
+ "severity": "CRITICAL"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "target": "npm/suspect-pkg@1.0",
3
+ "timestamp": "2026-03-06T13:25:09.668Z",
4
+ "ecosystem": "npm",
5
+ "summary": {
6
+ "critical": 0,
7
+ "high": 1,
8
+ "medium": 0,
9
+ "low": 0,
10
+ "total": 1,
11
+ "riskLevel": "HIGH",
12
+ "riskScore": 15
13
+ },
14
+ "threats": [
15
+ {
16
+ "type": "dynamic_require",
17
+ "severity": "HIGH"
18
+ }
19
+ ],
20
+ "sandbox": {
21
+ "score": 75,
22
+ "severity": "HIGH"
23
+ }
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "target": "npm/evil-pkg@2.0.0",
3
+ "timestamp": "2026-03-06T13:25:10.228Z",
4
+ "ecosystem": "npm",
5
+ "summary": {
6
+ "critical": 1,
7
+ "high": 0,
8
+ "medium": 0,
9
+ "low": 0,
10
+ "total": 1,
11
+ "riskLevel": "CRITICAL",
12
+ "riskScore": 25
13
+ },
14
+ "threats": [
15
+ {
16
+ "type": "known_malicious_package",
17
+ "severity": "CRITICAL"
18
+ }
19
+ ],
20
+ "sandbox": {
21
+ "score": 85,
22
+ "severity": "CRITICAL"
23
+ }
24
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "date": "2026-03-06",
3
+ "timestamp": "2026-03-06T13:25:10.394Z",
4
+ "embed": {
5
+ "embeds": [
6
+ {
7
+ "title": "📊 MUAD'DIB Daily Report",
8
+ "color": 3447003,
9
+ "fields": [
10
+ {
11
+ "name": "Packages Scanned",
12
+ "value": "3",
13
+ "inline": true
14
+ },
15
+ {
16
+ "name": "Clean",
17
+ "value": "1",
18
+ "inline": true
19
+ },
20
+ {
21
+ "name": "Suspects",
22
+ "value": "0",
23
+ "inline": true
24
+ },
25
+ {
26
+ "name": "Errors",
27
+ "value": "0",
28
+ "inline": true
29
+ },
30
+ {
31
+ "name": "Avg Scan Time",
32
+ "value": "2.0s/pkg",
33
+ "inline": true
34
+ },
35
+ {
36
+ "name": "Top Suspects",
37
+ "value": "1. **npm/test-dedup-detection-1772803509664@1.0.0** — 1 finding(s)",
38
+ "inline": false
39
+ }
40
+ ],
41
+ "footer": {
42
+ "text": "MUAD'DIB - Daily summary | 2026-03-06 13:25:10 UTC"
43
+ },
44
+ "timestamp": "2026-03-06T13:25:10.394Z"
45
+ }
46
+ ]
47
+ },
48
+ "metrics": {
49
+ "scanned": 3,
50
+ "clean": 1,
51
+ "suspect": 0,
52
+ "errors": 0,
53
+ "avgScanTimeMs": 2000,
54
+ "suspectByTier": {
55
+ "t1": 0,
56
+ "t2": 0,
57
+ "t3": 0
58
+ },
59
+ "topSuspects": []
60
+ }
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.5.7",
3
+ "version": "2.5.8",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -958,7 +958,7 @@ async function scrapeSnykMalware() {
958
958
  { name: 'event-stream', version: '3.3.6', description: 'Flatmap-stream backdoor (2018)' },
959
959
  { name: 'flatmap-stream', version: '*', description: 'Malicious dependency of event-stream' },
960
960
  { name: 'eslint-scope', version: '3.7.2', description: 'Credential theft (2018)' },
961
- { name: 'eslint-config-eslint', version: '*', description: 'Credential theft (2018)' },
961
+ { name: 'eslint-config-eslint', version: '5.0.2', description: 'Credential theft (2018)' },
962
962
  { name: 'getcookies', version: '*', description: 'Backdoor malware' },
963
963
  { name: 'mailparser', version: '2.3.0', description: 'Compromised version' },
964
964
  { name: 'node-ipc', version: '10.1.1', description: 'Protestware - file deletion' },
@@ -56,6 +56,14 @@ const SAFE_STRINGS = [
56
56
  'npmjs.com'
57
57
  ];
58
58
 
59
+ // Domains where fetch is legitimate (not C2) — used to suppress download_exec_binary compound
60
+ const SAFE_FETCH_DOMAINS = [
61
+ 'registry.npmjs.org', 'npmjs.com',
62
+ 'github.com', 'objects.githubusercontent.com', 'raw.githubusercontent.com',
63
+ 'nodejs.org', 'yarnpkg.com',
64
+ 'pypi.org', 'files.pythonhosted.org'
65
+ ];
66
+
59
67
  // Credential-stealing CLI commands (s1ngularity/Nx, Shai-Hulud)
60
68
  const CREDENTIAL_CLI_COMMANDS = [
61
69
  'gh auth token',
@@ -1182,15 +1190,22 @@ function handleLiteral(node, ctx) {
1182
1190
  }
1183
1191
  }
1184
1192
 
1185
- // Detect AI agent dangerous flags as string literals
1193
+ // Detect AI agent dangerous flags as string literals (MEDIUM signal only —
1194
+ // CRITICAL reserved for CallExpression context where flag is actually used in exec/spawn)
1186
1195
  for (const flag of AI_AGENT_DANGEROUS_FLAGS) {
1187
1196
  if (node.value === flag) {
1188
- ctx.threats.push({
1189
- type: 'ai_agent_abuse',
1190
- severity: 'CRITICAL',
1191
- message: `AI agent security bypass flag "${flag}" found — weaponized AI coding assistant (s1ngularity/Nx pattern).`,
1192
- file: ctx.relFile
1193
- });
1197
+ // Skip if already detected in a CallExpression context (avoid double-counting)
1198
+ const alreadyDetected = ctx.threats.some(t =>
1199
+ t.type === 'ai_agent_abuse' && t.severity === 'CRITICAL' && t.file === ctx.relFile
1200
+ );
1201
+ if (!alreadyDetected) {
1202
+ ctx.threats.push({
1203
+ type: 'ai_agent_abuse',
1204
+ severity: 'MEDIUM',
1205
+ message: `AI agent security bypass flag "${flag}" referenced in code — verify it is not used in exec/spawn invocations.`,
1206
+ file: ctx.relFile
1207
+ });
1208
+ }
1194
1209
  }
1195
1210
  }
1196
1211
 
@@ -1495,7 +1510,8 @@ function handlePostWalk(ctx) {
1495
1510
  }
1496
1511
 
1497
1512
  // Wave 4: Download-execute-cleanup — https download + chmod executable + execSync + unlink
1498
- if (ctx.hasRemoteFetch && ctx.hasChmodExecutable && ctx.hasExecSyncCall) {
1513
+ // Exclude when all URLs in the file point to safe registries (npm, GitHub, nodejs.org)
1514
+ if (ctx.hasRemoteFetch && ctx.hasChmodExecutable && ctx.hasExecSyncCall && !ctx.fetchOnlySafeDomains) {
1499
1515
  ctx.threats.push({
1500
1516
  type: 'download_exec_binary',
1501
1517
  severity: 'CRITICAL',
@@ -101,6 +101,8 @@ function analyzeFile(content, filePath, basePath) {
101
101
  ideConfigPathVars: new Map(),
102
102
  // Wave 4: compound detection — fetch + decrypt + eval chain
103
103
  hasRemoteFetch: /\bhttps?\.(get|request)\b/.test(content) || /\bfetch\s*\(/.test(content),
104
+ // Safe domain exclusion: if ALL URLs in file are from known registries, suppress download_exec_binary
105
+ fetchOnlySafeDomains: false, // computed below after URL extraction
104
106
  hasCryptoDecipher: /\bcreateDecipher(iv)?\s*\(/.test(content),
105
107
  // Wave 4: native addon camouflage signals
106
108
  hasRequireNodeFile: false,
@@ -111,10 +113,28 @@ function analyzeFile(content, filePath, basePath) {
111
113
  hasRunOnInContent: /\brunOn\b|\bfolderOpen\b/.test(content),
112
114
  hasWriteFileSyncInContent: /\bwriteFileSync\b|\bwriteFile\s*\(/.test(content),
113
115
  // Wave 4: MCP content keyword detection (must also have writeFileSync in same file)
116
+ // Content-level MCP detection: MCP keyword + writeFileSync + MCP config path in same file
117
+ // Path co-occurrence prevents FPs where a file reads MCP config but writes elsewhere.
118
+ // Read-only pattern (readFileSync without writeFileSync to MCP) is not injection.
114
119
  hasMcpContentKeywords: (/\bmcpServers\b/.test(content) || /\bmcp\.json\b/.test(content) || /\bclaude_desktop_config\b/.test(content)) &&
115
- /\bwriteFileSync\b|\bwriteFile\s*\(/.test(content)
120
+ /\bwriteFileSync\b|\bwriteFile\s*\(/.test(content) &&
121
+ (/\.claude[/\\]/.test(content) || /\.cursor[/\\]/.test(content) || /\.vscode[/\\]/.test(content) || /\.windsurf[/\\]/.test(content) || /\.codeium[/\\]/.test(content) || /\.continue[/\\]/.test(content) || /claude_desktop_config/.test(content) || /\bmcp\.json\b/.test(content))
116
122
  };
117
123
 
124
+ // Compute fetchOnlySafeDomains: check if ALL URLs in file point to known registries
125
+ if (ctx.hasRemoteFetch) {
126
+ const urlMatches = content.match(/https?:\/\/[^\s'"`)]+/g) || [];
127
+ const SAFE_FETCH_DOMAINS = [
128
+ 'registry.npmjs.org', 'npmjs.com',
129
+ 'github.com', 'objects.githubusercontent.com', 'raw.githubusercontent.com',
130
+ 'nodejs.org', 'yarnpkg.com',
131
+ 'pypi.org', 'files.pythonhosted.org'
132
+ ];
133
+ if (urlMatches.length > 0 && urlMatches.every(u => SAFE_FETCH_DOMAINS.some(d => u.includes(d)))) {
134
+ ctx.fetchOnlySafeDomains = true;
135
+ }
136
+ }
137
+
118
138
  walk.simple(ast, {
119
139
  VariableDeclarator(node) { handleVariableDeclarator(node, ctx); },
120
140
  CallExpression(node) { handleCallExpression(node, ctx); },
@@ -108,7 +108,11 @@ const WHITELIST = new Set([
108
108
  'docdash', // resembles lodash
109
109
  'yarpm', // resembles yargs
110
110
  'canvg', // resembles canvas
111
- 'obug' // internal sub-dependency
111
+ 'obug', // internal sub-dependency
112
+
113
+ // FPR P4: Benign packages falsely flagged as typosquat in evaluation
114
+ 'mocks', // karma dep, resembles mocha (wrong_char)
115
+ 'reactor' // stencil dep, resembles react (suffix)
112
116
  ]);
113
117
 
114
118