muaddib-scanner 1.0.8 → 1.0.9

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.
@@ -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 };
@@ -59,7 +59,7 @@ async function scanProject() {
59
59
  const webhookUrl = config.get('webhookUrl');
60
60
  const failLevel = config.get('failLevel');
61
61
 
62
- let cmd = `node "${path.join(__dirname, '..', 'bin', 'muaddib.js')}" scan "${projectPath}" --json`;
62
+ let cmd = `npx muaddib-scanner scan "${projectPath}" --json`;
63
63
  if (webhookUrl) {
64
64
  cmd += ` --webhook "${webhookUrl}"`;
65
65
  }
@@ -2,7 +2,7 @@
2
2
  "name": "muaddib-vscode",
3
3
  "displayName": "MUAD'DIB Security Scanner",
4
4
  "description": "Detecte les attaques supply chain npm directement dans VS Code",
5
- "version": "1.0.0",
5
+ "version": "1.0.3",
6
6
  "publisher": "dnszlsk",
7
7
  "engines": {
8
8
  "vscode": "^1.85.0"
@@ -49,7 +49,12 @@
49
49
  },
50
50
  "muaddib.failLevel": {
51
51
  "type": "string",
52
- "enum": ["critical", "high", "medium", "low"],
52
+ "enum": [
53
+ "critical",
54
+ "high",
55
+ "medium",
56
+ "low"
57
+ ],
53
58
  "default": "high",
54
59
  "description": "Niveau de severite pour afficher les alertes"
55
60
  }
@@ -61,4 +66,4 @@
61
66
  "url": "https://github.com/DNSZLSK/muad-dib"
62
67
  },
63
68
  "license": "MIT"
64
- }
69
+ }