install-guard 1.1.1 → 1.1.2

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # 🚨 Should You Trust That npm Package Before Installing?
6
6
 
7
- **install-guard** analyzes npm packages for security risks and tells you if they're safe **before** you install them.
7
+ **install-guard** is a supply chain security tool that analyzes npm packages for risks **before** you install them. It detects compromised versions, typosquatting, suspicious dependencies, and more.
8
8
 
9
9
  ---
10
10
 
@@ -16,41 +16,50 @@ $ npx install-guard install some-random-lib
16
16
  ```
17
17
 
18
18
  ```
19
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
19
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
20
20
  📦 some-random-lib v0.1.3
21
21
  A random utility library
22
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
23
23
 
24
- Risk Score: 8/10 🚨 High Risk
25
- ████████░░
24
+ Risk Level: 💀 CRITICAL (9/10)
25
+ █████████░
26
26
 
27
- ─────────────────────────────────────────────────
27
+ ──────────────────────────────────────────────────────────
28
28
 
29
29
  📊 Package Info
30
30
 
31
- Downloads (weekly) 120
32
- Maintainers 1
33
- License Unknown
34
- Last Published 3 days ago
35
- Dependencies 14
36
- Versions 2
31
+ Downloads (weekly) 120
32
+ Maintainers unknown-person
33
+ License Unknown
34
+ Published 6 hours ago
35
+ Dependencies 3
36
+ Total Versions 2
37
+ Repository ✘ None
37
38
 
38
- ─────────────────────────────────────────────────
39
+ ──────────────────────────────────────────────────────────
39
40
 
40
- 🔍 Security Checks
41
+ 🔍 Findings
41
42
 
42
- Downloads: Very low (120/week)
43
- Last Updated: Recently updated
44
- Install Scripts: Has install/postinstall scripts
45
- Maintainers: Single maintainer
46
- License: No license specified
47
- Repository: No repository URL
48
- Package Age: Published less than 30 days ago
49
- Dependencies: 14 direct dependencies
43
+ Version 0.1.3 was published 6 hours ago
44
+ Lifecycle scripts found: postinstall
45
+ New dep "plain-crypto-js" looks like typosquat of "crypto-js"
46
+ New dep "plain-crypto-js" has very low downloads (12/week)
47
+ ✘ No GitHub tag found for version 0.1.3
48
+ No license specified
49
+ Single maintainer: unknown-person
50
+ No recent commits in the last 90 days
50
51
 
51
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
52
+ ──────────────────────────────────────────────────────────
52
53
 
53
- High risk package. Continue install? (y/n):
54
+ 💡 Recommendation
55
+
56
+ ⛔ Avoid installing some-random-lib@0.1.3
57
+ ✔ Safe alternative: some-random-lib@0.1.1
58
+ No install scripts, published > 7 days ago
59
+
60
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
+
62
+ 💀 CRITICAL risk. Are you absolutely sure? (y/n):
54
63
  ```
55
64
 
56
65
  </details>
@@ -59,11 +68,12 @@ $ npx install-guard install some-random-lib
59
68
 
60
69
  ## 😬 The Problem
61
70
 
62
- Installing npm packages blindly is risky.
71
+ Supply chain attacks on npm are increasing:
63
72
 
64
73
  - Malicious packages are published **daily**
65
- - Popular packages get compromised
66
- - `npm audit` only checks known vulnerabilities **not trust**
74
+ - Popular packages get compromised (e.g., event-stream, ua-parser-js, colors)
75
+ - Attackers inject postinstall scripts, add typosquatted dependencies, or hijack maintainer accounts
76
+ - `npm audit` only checks **known CVEs** — it can't detect a new attack in progress
67
77
 
68
78
  You shouldn't have to guess if a package is safe.
69
79
 
@@ -71,19 +81,25 @@ You shouldn't have to guess if a package is safe.
71
81
 
72
82
  ## 🛡️ The Solution
73
83
 
74
- install-guard gives you a **risk score before you install anything**, powered by 10+ security checks.
84
+ install-guard runs a **modular detection pipeline** with 8 specialized security checks, GitHub verification, dependency diffing, and a weighted risk scorer all before a single file is downloaded.
75
85
 
76
86
  ---
77
87
 
78
88
  ## ⚡ Quick Start
79
89
 
80
- Analyze a package:
90
+ Analyze any package:
81
91
 
82
92
  ```bash
83
93
  npx install-guard axios
84
94
  ```
85
95
 
86
- Safely install with a risk check:
96
+ Analyze a specific version:
97
+
98
+ ```bash
99
+ npx install-guard axios@1.14.0
100
+ ```
101
+
102
+ Safely install with pre-check:
87
103
 
88
104
  ```bash
89
105
  npx install-guard install axios
@@ -95,122 +111,196 @@ Scan your entire project:
95
111
  npx install-guard scan
96
112
  ```
97
113
 
98
- Scan with detailed output per package:
114
+ Scan with full details per package:
99
115
 
100
116
  ```bash
101
117
  npx install-guard scan --verbose
102
118
  ```
103
119
 
120
+ Get JSON output (for CI/CD):
121
+
122
+ ```bash
123
+ npx install-guard axios --json
124
+ ```
125
+
126
+ Skip GitHub checks (faster):
127
+
128
+ ```bash
129
+ npx install-guard axios --skip-github
130
+ ```
131
+
104
132
  ---
105
133
 
106
- ## 🧠 How Risk Score Works
134
+ ## 🧠 Detection Pipeline
135
+
136
+ install-guard runs **8 isolated checks** through a security pipeline:
137
+
138
+ | # | Check | What it detects | Risk Weight |
139
+ |---|-------|----------------|-------------|
140
+ | 1 | **Recent Publish** | Version published <24h ago, unusual version jumps, rapid publish cadence | +2 to +5 |
141
+ | 2 | **Dependency Diff** | New/removed deps between versions, deep analysis of each new dep | +3 to +5 |
142
+ | 3 | **Script Analysis** | `preinstall`/`postinstall` hooks, suspicious commands (curl, eval, base64) | +5 |
143
+ | 4 | **Typosquatting** | Package name similar to popular packages (Levenshtein distance) | +5 |
144
+ | 5 | **Maintainer Analysis** | No maintainers, single maintainer | +1 to +3 |
145
+ | 6 | **License Check** | Missing, unknown, or non-permissive licenses | +1 to +3 |
146
+ | 7 | **GitHub Verification** | No matching tag/release, no recent commits in repo | +2 to +4 |
147
+ | 8 | **Deprecation** | Package flagged as deprecated on npm | +3 |
148
+
149
+ Results are scored and normalized to **0–10** with labels: `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`.
150
+
151
+ ### Dependency Diff Engine
152
+
153
+ When you check a specific version, install-guard compares its dependencies against the previous version. For each **newly added** dependency, it runs sub-analysis:
107
154
 
108
- install-guard runs **10+ security checks** on every package:
155
+ - Download count check
156
+ - Publish age check
157
+ - Typosquatting detection
158
+ - Install script detection
109
159
 
110
- | Check | What it detects |
111
- |-------|----------------|
112
- | 📉 **Downloads** | Low popularity = higher risk |
113
- | 🕒 **Last Updated** | Abandoned packages |
114
- | ⚠ **Install Scripts** | `preinstall` / `postinstall` hooks (common attack vector) |
115
- | 👥 **Maintainers** | Single or no maintainers |
116
- | 📜 **License** | Missing or non-permissive licenses |
117
- | 🔗 **Repository** | No source code link |
118
- | 📅 **Package Age** | Brand new packages (< 30 days) |
119
- | 📦 **Dependencies** | High dependency count |
120
- | 🚫 **Deprecated** | Flagged as deprecated on npm |
121
- | 🧠 **Typosquatting** | Names suspiciously similar to popular packages |
160
+ This catches the exact pattern used in real supply chain attacks (e.g., injecting a malicious dependency in a patch release).
122
161
 
123
- Each factor contributes to a **risk score (0–10)**. Lower = safer.
162
+ ### Safe Version Recommendation
163
+
164
+ If a version is flagged as risky, install-guard scans all previous versions and suggests the latest safe one — no install scripts, published more than 7 days ago.
124
165
 
125
166
  ---
126
167
 
127
- ## 📊 Example Results
168
+ ## 📊 Example Output
128
169
 
129
170
  ### ✅ Safe package
130
171
 
131
172
  ```
132
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
133
- 📦 axios v1.7.2
173
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
174
+ 📦 axios v1.15.0
175
+ Promise based HTTP client for the browser and node.js
176
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
134
177
 
135
- Risk Score: 1/10 ✅ Low Risk
178
+ Risk Level: ✅ LOW (1/10)
136
179
  █░░░░░░░░░
137
180
 
138
- 🔍 Security Checks
139
- Downloads: 44,392,817/week
140
- Last Updated: Recently updated
141
- Install Scripts: No install scripts
142
- Maintainers: 3 maintainers
143
- License: MIT
144
- Repository: Has repository link
145
- Package Age: 9+ year(s) old
146
- Dependencies: 8 direct dependencies
147
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
181
+ 🔍 Findings
182
+ Published 6 days ago
183
+ No lifecycle scripts
184
+ ✔ No typosquatting detected
185
+ License: MIT
186
+ Not deprecated
187
+ No dependency changes from previous version
188
+ GitHub tag found for v1.15.0
189
+ Repository has recent activity
190
+ ⚠ Single maintainer: jasonsaayman
191
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
148
192
  ```
149
193
 
150
- ### 🚨 Risky package
194
+ ### 🚨 Compromised package (simulated)
151
195
 
152
196
  ```
153
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
154
- 📦 some-lib v0.1.0
155
-
156
- Risk Score: 8/10 🚨 High Risk
157
- ████████░░
158
-
159
- 🔍 Security Checks
160
- Downloads: Very low (83/week)
161
- Install Scripts: Has install/postinstall scripts
162
- License: No license specified
163
- Maintainers: Single maintainer
164
- Repository: No repository URL
165
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
197
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
198
+ 📦 evil-lib v2.0.0
199
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
200
+
201
+ Risk Level: 💀 CRITICAL (9/10)
202
+ █████████░
203
+
204
+ 🔍 Findings
205
+ Version 2.0.0 was published 3 hours ago
206
+ Lifecycle scripts found: postinstall
207
+ Script "postinstall" contains suspicious command: curl http://...
208
+ New dependencies added: plain-crypto-js
209
+ ✘ New dep "plain-crypto-js" looks like typosquat of "crypto-js"
210
+ ✘ New dep "plain-crypto-js" has very low downloads (0/week)
211
+ ✘ No GitHub tag found for version 2.0.0
212
+
213
+ 💡 Recommendation
214
+ ⛔ Avoid installing evil-lib@2.0.0
215
+ ✔ Safe alternative: evil-lib@1.9.2
216
+ No install scripts, published > 7 days ago
217
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
166
218
  ```
167
219
 
168
220
  ### 📋 Project scan
169
221
 
170
222
  ```
171
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
223
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
172
224
  📋 Dependency Scan Summary
173
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
225
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
174
226
 
175
- Package Risk Status
227
+ Package Score Level
176
228
  ────────────────────────────────────────────
177
- some-lib 8/10 🚨 High Risk
178
- old-utils 5/10 ⚠ Medium
179
- express 0/10 Safe
180
- axios 1/10 Safe
181
-
182
- ─────────────────────────────────────────────────
183
- Summary: 2 safe · 1 medium · 1 high risk
184
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
229
+ evil-lib 9/10 💀 Critical
230
+ old-utils 5/10 ⚠ Medium
231
+ express 0/10 Low
232
+ axios 1/10 Low
233
+
234
+ ──────────────────────────────────────────────────────────
235
+ Total: 4 packages 2 low · 1 medium · 0 high · 1 critical
236
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
237
+ ```
238
+
239
+ ---
240
+
241
+ ## 🏗️ Architecture
242
+
243
+ ```
244
+ src/
245
+ ├── checks/ # Isolated security checks
246
+ │ ├── recentPublish.js # Publish timing analysis
247
+ │ ├── dependencyDiff.js # Version-to-version dep comparison
248
+ │ ├── scripts.js # Lifecycle script detection
249
+ │ ├── typosquat.js # Name similarity analysis
250
+ │ ├── maintainers.js # Maintainer signals
251
+ │ ├── license.js # License validation
252
+ │ ├── githubVerify.js # GitHub tag/release verification
253
+ │ └── deprecation.js # Deprecation detection
254
+ ├── services/
255
+ │ ├── pipeline.js # Orchestrates all checks
256
+ │ └── scorer.js # Weighted risk scoring
257
+ ├── utils/
258
+ │ ├── registry.js # npm registry API + caching
259
+ │ ├── github.js # GitHub API integration
260
+ │ └── cache.js # File-based cache (15min TTL)
261
+ ├── format.js # CLI output formatter
262
+ ├── scan.js # Project-wide scanner
263
+ ├── install.js # Safe install with prompts
264
+ └── index.js # Entry point
185
265
  ```
186
266
 
267
+ Each check is **fully isolated** — returns a standardized `{ id, findings[] }` structure. The pipeline runs sync checks immediately and async checks (GitHub, dependency deep-analysis) in parallel.
268
+
187
269
  ---
188
270
 
189
271
  ## ✨ Features
190
272
 
191
- - 🔍 **Deep analysis** — 10+ security checks per package
192
- - 🧠 **Typosquatting detection** — catches lookalike package names
193
- - **Risk scoring (0–10)** — instant safety assessment
194
- - 🛑 **Block risky installs** — prompts before installing medium/high risk packages
195
- - 📋 **Project-wide scan** — audit all dependencies in one command
196
- - 📊 **Summary table** — sorted by risk with safe/medium/high breakdown
197
- - **Zero setup** — works instantly with `npx`
198
- - 🎨 **Beautiful CLI output** — color-coded, easy to read
273
+ - 🔍 **8 security checks** — modular detection pipeline
274
+ - 🔄 **Dependency diff engine** — compares deps between versions
275
+ - 🧠 **Typosquatting detection** — Levenshtein distance against 80+ popular packages
276
+ - 🏷️ **GitHub verification** — checks for matching tags and recent activity
277
+ - **Script analysis** — detects dangerous commands in lifecycle scripts
278
+ - 💡 **Safe version suggestions** — recommends the latest clean version
279
+ - 📊 **Weighted risk scoring** — 0–10 with LOW/MEDIUM/HIGH/CRITICAL labels
280
+ - 🛑 **Install blocking** — prompts before installing risky packages
281
+ - 📋 **Project scan** — audit all deps with summary table
282
+ - 🗄️ **File-based caching** — avoids redundant API calls (15min TTL)
283
+ - 📤 **JSON output** — pipe results into CI/CD pipelines
284
+ - ⚡ **Zero config** — works with `npx`, no setup required
199
285
 
200
286
  ---
201
287
 
202
288
  ## 🤔 Why not npm audit?
203
289
 
204
- | Feature | npm audit | install-guard |
205
- |-----------------------|-----------|---------------|
206
- | Known vulnerabilities | ✅ | |
207
- | Trust analysis | ❌ | ✅ |
208
- | Pre-install check | ❌ | ✅ |
209
- | Install blocking | ❌ | ✅ |
210
- | Typosquatting check | ❌ | ✅ |
211
- | License analysis | ❌ | ✅ |
212
- | Maintainer check | ❌ | ✅ |
213
- | Package age check | ❌ | ✅ |
290
+ | Feature | npm audit | install-guard |
291
+ |----------------------------|-----------|---------------|
292
+ | Known CVE vulnerabilities | ✅ | |
293
+ | Supply chain attack detection | ❌ | ✅ |
294
+ | Pre-install analysis | ❌ | ✅ |
295
+ | Dependency diff | ❌ | ✅ |
296
+ | Typosquatting detection | ❌ | ✅ |
297
+ | Script analysis | ❌ | ✅ |
298
+ | GitHub verification | ❌ | ✅ |
299
+ | Install blocking | ❌ | ✅ |
300
+ | Safe version suggestions | ❌ | ✅ |
301
+ | JSON output for CI | ✅ | ✅ |
302
+
303
+ **Use both together** — `npm audit` for known vulnerabilities, `install-guard` for everything else.
214
304
 
215
305
  ---
216
306
 
@@ -224,12 +314,12 @@ npm install -g install-guard
224
314
 
225
315
  ## 🔮 Roadmap
226
316
 
227
- - 🔍 GitHub activity analysis
228
- - 🧠 Advanced typosquatting (permutations, homoglyphs)
229
317
  - 📊 Dependency tree visualization
230
318
  - 🔌 CI/CD integration (exit codes for pipelines)
231
- - 🏷️ `.install-guardrc` config for custom thresholds
232
- - 📝 JSON/CSV report export
319
+ - 🏷️ `.install-guardrc` config for custom risk thresholds
320
+ - 📝 HTML/CSV report export
321
+ - 🧠 Advanced typosquatting (permutations, homoglyphs, scope confusion)
322
+ - 🔔 Webhook notifications for risky dependencies
233
323
 
234
324
  ---
235
325
 
@@ -243,16 +333,3 @@ PRs welcome! Let's make npm safer together.
243
333
 
244
334
  If you find this useful, consider giving it a star ⭐
245
335
  It helps others discover the project!
246
-
247
- ---
248
-
249
- ## 🤝 Contributing
250
-
251
- PRs welcome! Let's make npm safer together.
252
-
253
- ---
254
-
255
- ## ⭐ Support
256
-
257
- If you find this useful, consider giving it a star ⭐
258
- It helps others discover the project!
package/bin/cli.js CHANGED
@@ -8,25 +8,46 @@ const program = new Command();
8
8
 
9
9
  program
10
10
  .name("install-guard")
11
- .description("Analyze npm packages for security risks before installing")
12
- .version("2.0.0");
11
+ .description("Detect supply chain attacks in npm packages before installing")
12
+ .version("3.0.0");
13
13
 
14
14
  program
15
- .argument("[package]", "package name to analyze")
16
- .action(async (pkg) => {
15
+ .argument("[package]", "package name to analyze (e.g., axios or axios@1.14.1)")
16
+ .option("--json", "Output results as JSON")
17
+ .option("--skip-github", "Skip GitHub verification (faster)")
18
+ .action(async (pkg, opts) => {
17
19
  if (!pkg) {
18
20
  program.help();
19
21
  return;
20
22
  }
21
- await analyzePackage(pkg);
23
+
24
+ // Support package@version syntax
25
+ let name = pkg;
26
+ let version;
27
+ const atIndex = pkg.lastIndexOf("@");
28
+ if (atIndex > 0) {
29
+ name = pkg.slice(0, atIndex);
30
+ version = pkg.slice(atIndex + 1);
31
+ }
32
+
33
+ await analyzePackage(name, version, {
34
+ json: opts.json,
35
+ skipGithub: opts.skipGithub,
36
+ });
22
37
  });
23
38
 
24
39
  program
25
40
  .command("scan")
26
- .description("Scan all project dependencies for risks")
41
+ .description("Scan all project dependencies for supply chain risks")
27
42
  .option("-v, --verbose", "Show detailed analysis for each package")
43
+ .option("--json", "Output results as JSON")
44
+ .option("--skip-github", "Skip GitHub verification (faster)")
28
45
  .action(async (opts) => {
29
- await scanProject({ verbose: opts.verbose });
46
+ await scanProject({
47
+ verbose: opts.verbose,
48
+ json: opts.json,
49
+ skipGithub: opts.skipGithub,
50
+ });
30
51
  });
31
52
 
32
53
  program
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "install-guard",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "main": "index.js",
5
5
  "bin": {
6
6
  "install-guard": "./bin/cli.js"
@@ -10,6 +10,10 @@
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
11
11
  },
12
12
  "author": "Sarthak Kumar Sahoo",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/SarthakSahoo1407/install-guard.git"
16
+ },
13
17
  "license": "MIT",
14
18
  "description": "Analyze npm packages for security risks before installing",
15
19
  "dependencies": {
package/src/analyze.js CHANGED
@@ -1,19 +1,25 @@
1
1
  import ora from "ora";
2
- import { getPackageData } from "./npm.js";
3
- import { calculateRisk } from "./score.js";
4
- import { formatAnalysis } from "./format.js";
2
+ import { runPipeline } from "./services/pipeline.js";
3
+ import { formatPipelineResult } from "./format.js";
5
4
 
6
- export async function analyze(pkgName) {
7
- const spinner = ora(`Analyzing ${pkgName}...`).start();
5
+ /**
6
+ * Analyze a package and print results to stdout.
7
+ * Returns the structured result.
8
+ */
9
+ export async function analyze(pkgName, version, opts = {}) {
10
+ const spinner = ora(`Analyzing ${pkgName}${version ? `@${version}` : ""}...`).start();
8
11
 
9
12
  try {
10
- const data = await getPackageData(pkgName);
11
- const result = calculateRisk(data);
12
-
13
+ const result = await runPipeline(pkgName, version, opts);
13
14
  spinner.stop();
14
- console.log(formatAnalysis(data, result));
15
15
 
16
- return { data, result };
16
+ if (opts.json) {
17
+ console.log(JSON.stringify(result, null, 2));
18
+ } else {
19
+ console.log(formatPipelineResult(result));
20
+ }
21
+
22
+ return result;
17
23
  } catch (err) {
18
24
  spinner.fail(`Failed to analyze "${pkgName}": ${err.message}`);
19
25
  return null;
@@ -0,0 +1,114 @@
1
+ import { getDownloads, getRegistryData } from "../utils/registry.js";
2
+ import { checkTyposquatName } from "./typosquat.js";
3
+
4
+ /**
5
+ * Compares dependencies between the current and previous version.
6
+ * Flags newly added dependencies and analyzes them for suspiciousness.
7
+ */
8
+ export async function checkDependencyDiff(ctx) {
9
+ const findings = [];
10
+
11
+ const current = ctx.dependencies;
12
+ const previous = ctx.previousDependencies;
13
+
14
+ const added = Object.keys(current).filter((d) => !(d in previous));
15
+ const removed = Object.keys(previous).filter((d) => !(d in current));
16
+
17
+ if (added.length === 0 && removed.length === 0) {
18
+ findings.push({
19
+ severity: "info",
20
+ message: "No dependency changes from previous version",
21
+ score: 0,
22
+ });
23
+ return { id: "dependency-diff", findings };
24
+ }
25
+
26
+ if (removed.length > 0) {
27
+ findings.push({
28
+ severity: "info",
29
+ message: `Removed dependencies: ${removed.join(", ")}`,
30
+ score: 0,
31
+ });
32
+ }
33
+
34
+ if (added.length > 0) {
35
+ findings.push({
36
+ severity: "high",
37
+ message: `New dependencies added: ${added.join(", ")}`,
38
+ score: 3,
39
+ });
40
+
41
+ // Deep-analyze each newly added dependency
42
+ for (const dep of added) {
43
+ const subFindings = await analyzeSuspiciousDep(dep);
44
+ findings.push(...subFindings);
45
+ }
46
+ }
47
+
48
+ return { id: "dependency-diff", findings };
49
+ }
50
+
51
+ async function analyzeSuspiciousDep(name) {
52
+ const findings = [];
53
+
54
+ // Typosquatting check
55
+ const typo = checkTyposquatName(name);
56
+ if (typo) {
57
+ findings.push({
58
+ severity: "critical",
59
+ message: `New dep "${name}" looks like typosquat of "${typo}"`,
60
+ score: 5,
61
+ });
62
+ }
63
+
64
+ try {
65
+ const registry = await getRegistryData(name);
66
+ const downloads = await getDownloads(name);
67
+ const latest = registry["dist-tags"]?.latest;
68
+ const publishTime = registry.time?.[latest];
69
+
70
+ // Low downloads
71
+ if (downloads.downloads < 500) {
72
+ findings.push({
73
+ severity: "high",
74
+ message: `New dep "${name}" has very low downloads (${downloads.downloads}/week)`,
75
+ score: 4,
76
+ });
77
+ }
78
+
79
+ // Very new package
80
+ if (publishTime) {
81
+ const ageDays =
82
+ (Date.now() - new Date(publishTime).getTime()) / (1000 * 60 * 60 * 24);
83
+ if (ageDays < 30) {
84
+ findings.push({
85
+ severity: "high",
86
+ message: `New dep "${name}" is less than 30 days old`,
87
+ score: 4,
88
+ });
89
+ }
90
+ }
91
+
92
+ // Has install scripts
93
+ const versionData = registry.versions?.[latest];
94
+ if (
95
+ versionData?.scripts?.postinstall ||
96
+ versionData?.scripts?.preinstall ||
97
+ versionData?.scripts?.install
98
+ ) {
99
+ findings.push({
100
+ severity: "critical",
101
+ message: `New dep "${name}" has install scripts`,
102
+ score: 5,
103
+ });
104
+ }
105
+ } catch {
106
+ findings.push({
107
+ severity: "high",
108
+ message: `New dep "${name}" could not be resolved on npm`,
109
+ score: 4,
110
+ });
111
+ }
112
+
113
+ return findings;
114
+ }