muaddib-scanner 1.0.8 → 1.0.10

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.
Files changed (37) hide show
  1. package/.muaddib-cache/iocs.json +355 -0
  2. package/README.fr.md +310 -0
  3. package/README.md +118 -93
  4. package/bin/muaddib.js +33 -26
  5. package/data/iocs.json +28 -0
  6. package/package.json +3 -3
  7. package/rapport.html +159 -0
  8. package/src/index.js +73 -15
  9. package/src/ioc/scraper.js +91 -50
  10. package/src/rules/index.js +40 -1
  11. package/src/scanner/typosquat.js +52 -118
  12. package/.github/workflows/scan.yml +0 -33
  13. package/docs/threat-model.md +0 -116
  14. package/test/samples/malicious.js +0 -20
  15. package/tests/run-tests.js +0 -389
  16. package/tests/samples/ast/malicious.js +0 -20
  17. package/tests/samples/clean/safe.js +0 -14
  18. package/tests/samples/dataflow/exfiltration.js +0 -20
  19. package/tests/samples/edge/empty/empty.js +0 -0
  20. package/tests/samples/edge/invalid-syntax/broken.js +0 -5
  21. package/tests/samples/edge/large-file/large.js +0 -6
  22. package/tests/samples/edge/non-js/readme.txt +0 -3
  23. package/tests/samples/markers/shai-hulud.js +0 -10
  24. package/tests/samples/obfuscation/obfuscated.js +0 -1
  25. package/tests/samples/package/package.json +0 -9
  26. package/tests/samples/shell/malicious.sh +0 -13
  27. package/tests/samples/typosquat/package.json +0 -11
  28. package/vscode-extension/.vscode/launch.json +0 -13
  29. package/vscode-extension/.vscodeignore +0 -0
  30. package/vscode-extension/LICENSE +0 -21
  31. package/vscode-extension/README.md +0 -0
  32. package/vscode-extension/extension.js +0 -271
  33. package/vscode-extension/icon.png +0 -0
  34. package/vscode-extension/muaddib-vscode-1.0.0.vsix +0 -0
  35. package/vscode-extension/package.json +0 -64
  36. package/vscode-extension/vscode-extension/README.md +0 -44
  37. package/vscode-extension/vscode-extension/package.json +0 -64
package/rapport.html ADDED
@@ -0,0 +1,159 @@
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MUAD'DIB - Rapport de scan</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ background: #1a1a2e;
11
+ color: #eee;
12
+ margin: 0;
13
+ padding: 20px;
14
+ }
15
+ .container {
16
+ max-width: 1200px;
17
+ margin: 0 auto;
18
+ }
19
+ h1 {
20
+ color: #e94560;
21
+ border-bottom: 2px solid #e94560;
22
+ padding-bottom: 10px;
23
+ }
24
+ .summary {
25
+ display: flex;
26
+ gap: 20px;
27
+ margin: 20px 0;
28
+ }
29
+ .summary-card {
30
+ background: #16213e;
31
+ padding: 20px;
32
+ border-radius: 8px;
33
+ flex: 1;
34
+ }
35
+ .summary-card h3 {
36
+ margin: 0 0 10px 0;
37
+ color: #888;
38
+ font-size: 14px;
39
+ }
40
+ .summary-card .value {
41
+ font-size: 32px;
42
+ font-weight: bold;
43
+ }
44
+ .critical .value { color: #e94560; }
45
+ .high .value { color: #ff6b35; }
46
+ .medium .value { color: #f9c74f; }
47
+ .total .value { color: #4ecdc4; }
48
+ table {
49
+ width: 100%;
50
+ border-collapse: collapse;
51
+ margin-top: 20px;
52
+ }
53
+ th, td {
54
+ padding: 12px;
55
+ text-align: left;
56
+ border-bottom: 1px solid #333;
57
+ }
58
+ th {
59
+ background: #16213e;
60
+ color: #e94560;
61
+ }
62
+ tr.critical { background: rgba(233, 69, 96, 0.2); }
63
+ tr.high { background: rgba(255, 107, 53, 0.2); }
64
+ tr.medium { background: rgba(249, 199, 79, 0.1); }
65
+ .meta {
66
+ color: #666;
67
+ font-size: 12px;
68
+ margin-top: 40px;
69
+ }
70
+ .ok {
71
+ background: #16213e;
72
+ padding: 40px;
73
+ border-radius: 8px;
74
+ text-align: center;
75
+ color: #4ecdc4;
76
+ font-size: 24px;
77
+ }
78
+ </style>
79
+ </head>
80
+ <body>
81
+ <div class="container">
82
+ <h1>MUAD'DIB - Rapport de scan</h1>
83
+
84
+ <div class="summary">
85
+ <div class="summary-card total">
86
+ <h3>TOTAL</h3>
87
+ <div class="value">4</div>
88
+ </div>
89
+ <div class="summary-card critical">
90
+ <h3>CRITICAL</h3>
91
+ <div class="value">0</div>
92
+ </div>
93
+ <div class="summary-card high">
94
+ <h3>HIGH</h3>
95
+ <div class="value">3</div>
96
+ </div>
97
+ <div class="summary-card medium">
98
+ <h3>MEDIUM</h3>
99
+ <div class="value">1</div>
100
+ </div>
101
+ </div>
102
+
103
+
104
+ <table>
105
+ <thead>
106
+ <tr>
107
+ <th>Severite</th>
108
+ <th>Type</th>
109
+ <th>Message</th>
110
+ <th>Fichier</th>
111
+ <th>Recommandation</th>
112
+ </tr>
113
+ </thead>
114
+ <tbody>
115
+
116
+ <tr class="high">
117
+ <td>HIGH</td>
118
+ <td>sensitive_string</td>
119
+ <td>Reference a ".npmrc" detectee.</td>
120
+ <td>malicious.js</td>
121
+ <td>Reference a un chemin ou identifiant sensible. Verifier le contexte d'utilisation.</td>
122
+ </tr>
123
+
124
+ <tr class="high">
125
+ <td>HIGH</td>
126
+ <td>env_access</td>
127
+ <td>Acces a variable sensible process.env.GITHUB_TOKEN.</td>
128
+ <td>malicious.js</td>
129
+ <td>Acces a une variable d'environnement sensible. Verifier si les donnees sont exfiltrees.</td>
130
+ </tr>
131
+
132
+ <tr class="high">
133
+ <td>HIGH</td>
134
+ <td>sensitive_string</td>
135
+ <td>Reference a "api.github.com" detectee.</td>
136
+ <td>malicious.js</td>
137
+ <td>Reference a un chemin ou identifiant sensible. Verifier le contexte d'utilisation.</td>
138
+ </tr>
139
+
140
+ <tr class="medium">
141
+ <td>MEDIUM</td>
142
+ <td>dangerous_call_exec</td>
143
+ <td>Appel dangereux "exec" detecte.</td>
144
+ <td>malicious.js</td>
145
+ <td>Execution de commande systeme. Verifier les arguments passes.</td>
146
+ </tr>
147
+
148
+ </tbody>
149
+ </table>
150
+
151
+
152
+ <div class="meta">
153
+ <p>Cible: test/samples</p>
154
+ <p>Date: 2026-01-01T20:11:35.775Z</p>
155
+ <p>Genere par MUAD'DIB</p>
156
+ </div>
157
+ </div>
158
+ </body>
159
+ </html>
package/src/index.js CHANGED
@@ -6,11 +6,64 @@ const { scanDependencies } = require('./scanner/dependencies.js');
6
6
  const { scanHashes } = require('./scanner/hash.js');
7
7
  const { analyzeDataFlow } = require('./scanner/dataflow.js');
8
8
  const { getPlaybook } = require('./response/playbooks.js');
9
- const { getRule } = require('./rules/index.js');
9
+ const { getRule, PARANOID_RULES } = require('./rules/index.js');
10
10
  const { saveReport } = require('./report.js');
11
11
  const { saveSARIF } = require('./sarif.js');
12
12
  const { scanTyposquatting } = require('./scanner/typosquat.js');
13
13
  const { sendWebhook } = require('./webhook.js');
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ // Scan paranoid mode
18
+ function scanParanoid(targetPath) {
19
+ const threats = [];
20
+
21
+ function scanFile(filePath) {
22
+ try {
23
+ const content = fs.readFileSync(filePath, 'utf8');
24
+
25
+ for (const [ruleKey, rule] of Object.entries(PARANOID_RULES)) {
26
+ for (const pattern of rule.patterns) {
27
+ if (content.includes(pattern)) {
28
+ threats.push({
29
+ type: rule.id,
30
+ severity: rule.severity.toUpperCase(),
31
+ message: `${rule.message}: "${pattern}"`,
32
+ file: path.relative(targetPath, filePath),
33
+ mitre: rule.mitre
34
+ });
35
+ }
36
+ }
37
+ }
38
+ } catch (e) {
39
+ // Ignore read errors
40
+ }
41
+ }
42
+
43
+ function walkDir(dir) {
44
+ const excluded = ['node_modules', '.git', 'test', 'tests', 'src', 'vscode-extension'];
45
+ try {
46
+ const files = fs.readdirSync(dir);
47
+ for (const file of files) {
48
+ const fullPath = path.join(dir, file);
49
+ const stat = fs.statSync(fullPath);
50
+
51
+ if (stat.isDirectory()) {
52
+ if (!excluded.includes(file)) {
53
+ walkDir(fullPath);
54
+ }
55
+ } else if (file.endsWith('.js') || file.endsWith('.json') || file.endsWith('.sh')) {
56
+ scanFile(fullPath);
57
+ }
58
+ }
59
+ } catch (e) {
60
+ // Ignore walk errors
61
+ }
62
+ }
63
+
64
+ walkDir(targetPath);
65
+ return threats;
66
+ }
14
67
 
15
68
  async function run(targetPath, options = {}) {
16
69
  const threats = [];
@@ -36,34 +89,40 @@ async function run(targetPath, options = {}) {
36
89
  const dataflowThreats = await analyzeDataFlow(targetPath);
37
90
  threats.push(...dataflowThreats);
38
91
 
39
- // Scan typosquatting
40
92
  const typosquatThreats = await scanTyposquatting(targetPath);
41
93
  threats.push(...typosquatThreats);
42
94
 
95
+ // Paranoid mode
96
+ if (options.paranoid) {
97
+ console.log('[PARANOID] Mode ultra-strict active\n');
98
+ const paranoidThreats = scanParanoid(targetPath);
99
+ threats.push(...paranoidThreats);
100
+ }
101
+
43
102
  // Enrichir chaque menace avec les regles
44
103
  const enrichedThreats = threats.map(t => {
45
104
  const rule = getRule(t.type);
46
105
  return {
47
106
  ...t,
48
- rule_id: rule.id,
49
- rule_name: rule.name,
50
- confidence: rule.confidence,
51
- references: rule.references,
52
- mitre: rule.mitre,
107
+ rule_id: rule.id || t.type,
108
+ rule_name: rule.name || t.type,
109
+ confidence: rule.confidence || 'medium',
110
+ references: rule.references || [],
111
+ mitre: t.mitre || rule.mitre,
53
112
  playbook: getPlaybook(t.type)
54
113
  };
55
114
  });
56
115
 
57
- // Calculer le score de risque (0-100)
116
+ // Calculer le score de risque (0-100)
58
117
  const criticalCount = threats.filter(t => t.severity === 'CRITICAL').length;
59
118
  const highCount = threats.filter(t => t.severity === 'HIGH').length;
60
119
  const mediumCount = threats.filter(t => t.severity === 'MEDIUM').length;
61
120
 
62
121
  let riskScore = 0;
63
- riskScore += criticalCount * 25; // CRITICAL = 25 points
64
- riskScore += highCount * 10; // HIGH = 10 points
65
- riskScore += mediumCount * 3; // MEDIUM = 3 points
66
- riskScore = Math.min(100, riskScore); // Cap a 100
122
+ riskScore += criticalCount * 25;
123
+ riskScore += highCount * 10;
124
+ riskScore += mediumCount * 3;
125
+ riskScore = Math.min(100, riskScore);
67
126
 
68
127
  const riskLevel = riskScore >= 75 ? 'CRITICAL'
69
128
  : riskScore >= 50 ? 'HIGH'
@@ -125,11 +184,10 @@ async function run(targetPath, options = {}) {
125
184
  });
126
185
  }
127
186
  }
128
- // Sortie normale
187
+ // Sortie normale
129
188
  else {
130
189
  console.log(`\n[MUADDIB] Scan de ${targetPath}\n`);
131
190
 
132
- // Afficher le score de risque
133
191
  const scoreBar = '█'.repeat(Math.floor(result.summary.riskScore / 5)) + '░'.repeat(20 - Math.floor(result.summary.riskScore / 5));
134
192
  console.log(`[SCORE] ${result.summary.riskScore}/100 [${scoreBar}] ${result.summary.riskLevel}\n`);
135
193
 
@@ -153,7 +211,7 @@ async function run(targetPath, options = {}) {
153
211
  }
154
212
  }
155
213
 
156
- // Envoyer webhook si configure
214
+ // Envoyer webhook si configure
157
215
  if (options.webhook) {
158
216
  try {
159
217
  await sendWebhook(options.webhook, result);
@@ -52,7 +52,6 @@ async function scrapeGitHubAdvisories() {
52
52
  const packages = [];
53
53
 
54
54
  try {
55
- // Plusieurs pages
56
55
  for (let page = 1; page <= 5; page++) {
57
56
  const url = `https://api.github.com/advisories?ecosystem=npm&per_page=100&page=${page}`;
58
57
  const { status, data } = await fetchJSON(url);
@@ -97,7 +96,6 @@ async function scrapeOSV() {
97
96
  const packages = [];
98
97
 
99
98
  try {
100
- // Query malware specifique
101
99
  const queries = [
102
100
  { package: { ecosystem: 'npm' }, query: 'malware' },
103
101
  { package: { ecosystem: 'npm' }, query: 'malicious' },
@@ -140,54 +138,24 @@ async function scrapeOSV() {
140
138
  }
141
139
 
142
140
  // ============================================
143
- // SOURCE 3: Snyk Vulnerability DB (publique)
144
- // ============================================
145
- async function scrapeSnyk() {
146
- console.log('[SCRAPER] Snyk Vulnerability DB...');
147
- const packages = [];
148
-
149
- // Packages malveillants connus de Snyk (liste statique car API payante)
150
- const knownMalicious = [
151
- 'event-stream', 'flatmap-stream', 'eslint-scope', 'eslint-config-eslint',
152
- 'getcookies', 'mailparser', 'nodemailer', 'nodemailer-js', 'node-ipc',
153
- 'peacenotwar', 'colors', 'faker', 'ua-parser-js', 'rc', 'coa',
154
- 'pac-resolver', 'set-value', 'ansi-html', 'ini', 'y18n', 'node-notifier',
155
- 'trim', 'trim-newlines', 'glob-parent', 'is-svg', 'css-what', 'normalize-url',
156
- 'hosted-git-info', 'ssri', 'tar', 'path-parse', 'json-schema',
157
- 'underscore', 'handlebars', 'lodash', 'marked', 'minimist', 'kind-of'
158
- ];
159
-
160
- // On ne les ajoute que s'ils ont des versions malveillantes specifiques
161
- // Pour l'instant on skip car c'est trop de faux positifs
162
- console.log(`[SCRAPER] -> Skip (API payante)`);
163
-
164
- return packages;
165
- }
166
-
167
- // ============================================
168
- // SOURCE 4: Socket.dev (via leur blog/reports)
141
+ // SOURCE 3: Socket.dev reports
169
142
  // ============================================
170
143
  async function scrapeSocketReports() {
171
144
  console.log('[SCRAPER] Socket.dev reports...');
172
145
  const packages = [];
173
146
 
174
- // Packages malveillants reportes par Socket.dev
175
147
  const socketMalicious = [
176
- // Shai-Hulud variants
177
148
  { name: '@pnpm.exe/pnpm', severity: 'critical', source: 'socket-shai-hulud' },
178
149
  { name: '@nicklason/npm', severity: 'critical', source: 'socket-shai-hulud' },
179
150
  { name: 'bb-builder', severity: 'critical', source: 'socket-shai-hulud' },
180
151
  { name: 'codespaces-blank', severity: 'critical', source: 'socket-shai-hulud' },
181
- // Crypto stealers
182
152
  { name: 'crypto-browserify-aes', severity: 'critical', source: 'socket-crypto-stealer' },
183
153
  { name: 'eth-wallet-gen', severity: 'critical', source: 'socket-crypto-stealer' },
184
154
  { name: 'solana-wallet-tools', severity: 'critical', source: 'socket-crypto-stealer' },
185
- // Discord token stealers
186
155
  { name: 'discord-selfbot-tools', severity: 'critical', source: 'socket-discord-stealer' },
187
156
  { name: 'discord-selfbot-v13', severity: 'critical', source: 'socket-discord-stealer' },
188
157
  { name: 'discord-token-grabber', severity: 'critical', source: 'socket-discord-stealer' },
189
158
  { name: 'discordbot-tokens', severity: 'critical', source: 'socket-discord-stealer' },
190
- // Typosquats recents
191
159
  { name: 'electorn', severity: 'high', source: 'socket-typosquat' },
192
160
  { name: 'electrn', severity: 'high', source: 'socket-typosquat' },
193
161
  { name: 'reqeusts', severity: 'high', source: 'socket-typosquat' },
@@ -205,7 +173,6 @@ async function scrapeSocketReports() {
205
173
  { name: 'reactt', severity: 'high', source: 'socket-typosquat' },
206
174
  { name: 'chalks', severity: 'high', source: 'socket-typosquat' },
207
175
  { name: 'chalkk', severity: 'high', source: 'socket-typosquat' },
208
- // Protestware
209
176
  { name: 'styled-components-native', severity: 'high', source: 'socket-protestware' },
210
177
  { name: 'es5-ext', severity: 'medium', source: 'socket-protestware' }
211
178
  ];
@@ -229,28 +196,22 @@ async function scrapeSocketReports() {
229
196
  }
230
197
 
231
198
  // ============================================
232
- // SOURCE 5: Phylum Research
199
+ // SOURCE 4: Phylum Research
233
200
  // ============================================
234
201
  async function scrapePhylum() {
235
202
  console.log('[SCRAPER] Phylum Research...');
236
203
  const packages = [];
237
204
 
238
- // Packages malveillants reportes par Phylum
239
205
  const phylumMalicious = [
240
- // Shai-Hulud original
241
206
  { name: '@nicklason/npm-register', severity: 'critical' },
242
207
  { name: 'lemaaa', severity: 'critical' },
243
208
  { name: 'badshell', severity: 'critical' },
244
- // Reverse shells
245
209
  { name: 'node-shell', severity: 'critical' },
246
210
  { name: 'reverse-shell-as-a-service', severity: 'critical' },
247
- // Data exfiltration
248
211
  { name: 'browserify-sign-steal', severity: 'critical' },
249
212
  { name: 'npm-script-demo', severity: 'high' },
250
- // Malware loaders
251
213
  { name: 'load-from-cwd-or-npm', severity: 'high' },
252
214
  { name: 'loadyaml-', severity: 'high' },
253
- // Install scripts malicious
254
215
  { name: 'preinstall-script', severity: 'high' },
255
216
  { name: 'postinstall-script', severity: 'high' }
256
217
  ];
@@ -274,13 +235,12 @@ async function scrapePhylum() {
274
235
  }
275
236
 
276
237
  // ============================================
277
- // SOURCE 6: npm unpublished/removed packages
238
+ // SOURCE 5: npm removed packages
278
239
  // ============================================
279
240
  async function scrapeNpmRemoved() {
280
241
  console.log('[SCRAPER] npm removed packages...');
281
242
  const packages = [];
282
243
 
283
- // Packages retires de npm pour raisons de securite
284
244
  const removedPackages = [
285
245
  { name: 'event-stream', version: '3.3.6', reason: 'Malicious code injection' },
286
246
  { name: 'flatmap-stream', version: '0.1.1', reason: 'Bitcoin wallet stealer' },
@@ -314,13 +274,12 @@ async function scrapeNpmRemoved() {
314
274
  }
315
275
 
316
276
  // ============================================
317
- // SOURCE 7: Known typosquats generator
277
+ // SOURCE 6: Typosquats generator
318
278
  // ============================================
319
279
  async function generateTyposquats() {
320
280
  console.log('[SCRAPER] Typosquats generation...');
321
281
  const packages = [];
322
282
 
323
- // Top packages npm et leurs typosquats connus
324
283
  const typosquatMap = {
325
284
  'lodash': ['lodahs', 'lodasg', 'lodash-', '-lodash', 'lodas', 'lodashh'],
326
285
  'express': ['expres', 'expresss', 'exprees', 'exprss', 'exppress'],
@@ -364,6 +323,90 @@ async function generateTyposquats() {
364
323
  return packages;
365
324
  }
366
325
 
326
+ // ============================================
327
+ // SOURCE 7: AlienVault OTX
328
+ // ============================================
329
+ async function scrapeAlienVault() {
330
+ console.log('[SCRAPER] AlienVault OTX...');
331
+ const packages = [];
332
+
333
+ try {
334
+ const searches = ['npm%20malware', 'nodejs%20malware', 'supply%20chain%20npm'];
335
+
336
+ for (const search of searches) {
337
+ const url = `https://otx.alienvault.com/api/v1/search/pulses?q=${search}&limit=20`;
338
+ const { status, data } = await fetchJSON(url);
339
+
340
+ if (status === 200 && data?.results) {
341
+ for (const pulse of data.results) {
342
+ if (pulse.indicators) {
343
+ for (const indicator of pulse.indicators) {
344
+ if (indicator.type === 'hostname' || indicator.type === 'domain' || indicator.type === 'FileHash-SHA256') {
345
+ const name = indicator.indicator;
346
+ // Filtre pour noms de packages npm potentiels
347
+ if (name && !name.includes('.') && !name.includes('/') && name.length > 2 && name.length < 50) {
348
+ packages.push({
349
+ id: `OTX-${pulse.id}-${name.slice(0, 20)}`,
350
+ name: name,
351
+ version: '*',
352
+ severity: 'high',
353
+ confidence: 'medium',
354
+ source: 'alienvault-otx',
355
+ description: (pulse.name || 'AlienVault OTX threat intelligence').slice(0, 200),
356
+ references: [`https://otx.alienvault.com/pulse/${pulse.id}`],
357
+ mitre: 'T1195.002'
358
+ });
359
+ }
360
+ }
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
366
+
367
+ console.log(`[SCRAPER] -> ${packages.length} packages trouves`);
368
+ } catch (e) {
369
+ console.log(`[SCRAPER] -> Erreur: ${e.message}`);
370
+ }
371
+
372
+ return packages;
373
+ }
374
+
375
+ // ============================================
376
+ // SOURCE 8: Aikido Intel (leur feed public)
377
+ // ============================================
378
+ async function scrapeAikidoIntel() {
379
+ console.log('[SCRAPER] Aikido Intel...');
380
+ const packages = [];
381
+
382
+ try {
383
+ const url = 'https://intel.aikido.dev/api/v1/malware?ecosystem=npm&limit=100';
384
+ const { status, data } = await fetchJSON(url);
385
+
386
+ if (status === 200 && Array.isArray(data)) {
387
+ for (const pkg of data) {
388
+ packages.push({
389
+ id: `AIKIDO-${pkg.name || pkg.id}`,
390
+ name: pkg.name,
391
+ version: pkg.version || '*',
392
+ severity: pkg.severity || 'high',
393
+ confidence: 'high',
394
+ source: 'aikido-intel',
395
+ description: (pkg.description || 'Malware detected by Aikido Intel').slice(0, 200),
396
+ references: ['https://intel.aikido.dev'],
397
+ mitre: 'T1195.002'
398
+ });
399
+ }
400
+ }
401
+
402
+ console.log(`[SCRAPER] -> ${packages.length} packages trouves`);
403
+ } catch (e) {
404
+ console.log(`[SCRAPER] -> Erreur: ${e.message}`);
405
+ }
406
+
407
+ return packages;
408
+ }
409
+
367
410
  // ============================================
368
411
  // MAIN SCRAPER
369
412
  // ============================================
@@ -372,7 +415,6 @@ async function runScraper() {
372
415
  console.log('║ MUAD\'DIB IOC Scraper ║');
373
416
  console.log('╚════════════════════════════════════════════╝\n');
374
417
 
375
- // Charger les IOCs existants
376
418
  let existingIOCs = { packages: [], hashes: [], markers: [], files: [] };
377
419
  if (fs.existsSync(IOC_FILE)) {
378
420
  existingIOCs = JSON.parse(fs.readFileSync(IOC_FILE, 'utf8'));
@@ -381,17 +423,17 @@ async function runScraper() {
381
423
  const existingNames = new Set(existingIOCs.packages.map(p => p.name));
382
424
  const initialCount = existingIOCs.packages.length;
383
425
 
384
- // Scraper toutes les sources
385
426
  const results = await Promise.all([
386
427
  scrapeGitHubAdvisories(),
387
428
  scrapeOSV(),
388
429
  scrapeSocketReports(),
389
430
  scrapePhylum(),
390
431
  scrapeNpmRemoved(),
391
- generateTyposquats()
432
+ generateTyposquats(),
433
+ scrapeAlienVault(),
434
+ scrapeAikidoIntel()
392
435
  ]);
393
436
 
394
- // Fusionner sans doublons
395
437
  let added = 0;
396
438
  for (const pkgList of results) {
397
439
  for (const pkg of pkgList) {
@@ -403,7 +445,6 @@ async function runScraper() {
403
445
  }
404
446
  }
405
447
 
406
- // Sauvegarder
407
448
  fs.writeFileSync(IOC_FILE, JSON.stringify(existingIOCs, null, 2));
408
449
 
409
450
  console.log('\n╔════════════════════════════════════════════╗');
@@ -207,4 +207,43 @@ function getRule(type) {
207
207
  };
208
208
  }
209
209
 
210
- module.exports = { RULES, getRule };
210
+ // Paranoid rules (ultra-strict)
211
+ const PARANOID_RULES = {
212
+ network_access: {
213
+ id: 'MUADDIB-PARANOID-001',
214
+ severity: 'HIGH',
215
+ patterns: ['fetch', 'axios', 'http.request', 'https.request', 'net.connect', 'XMLHttpRequest'],
216
+ message: 'Network access detected (paranoid mode)',
217
+ mitre: 'T1071'
218
+ },
219
+ sensitive_file_access: {
220
+ id: 'MUADDIB-PARANOID-002',
221
+ severity: 'HIGH',
222
+ patterns: ['.env', '.npmrc', '.ssh', '.git', 'id_rsa', 'credentials', 'secrets'],
223
+ message: 'Sensitive file access detected (paranoid mode)',
224
+ mitre: 'T1552.001'
225
+ },
226
+ dynamic_execution: {
227
+ id: 'MUADDIB-PARANOID-003',
228
+ severity: 'CRITICAL',
229
+ patterns: ['eval', 'Function', 'vm.runInContext'],
230
+ message: 'Dynamic code execution detected (paranoid mode)',
231
+ mitre: 'T1059'
232
+ },
233
+ subprocess: {
234
+ id: 'MUADDIB-PARANOID-004',
235
+ severity: 'CRITICAL',
236
+ patterns: ['child_process', 'spawn', 'exec', 'execSync', 'spawnSync', 'fork'],
237
+ message: 'Subprocess execution detected (paranoid mode)',
238
+ mitre: 'T1059.004'
239
+ },
240
+ env_access: {
241
+ id: 'MUADDIB-PARANOID-005',
242
+ severity: 'MEDIUM',
243
+ patterns: ['process.env'],
244
+ message: 'Environment variable access detected (paranoid mode)',
245
+ mitre: 'T1552.001'
246
+ }
247
+ };
248
+
249
+ module.exports = { RULES, getRule, PARANOID_RULES };