install-guard 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sarthak Kumar Sahoo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,27 +1,65 @@
1
+ ![npm](https://img.shields.io/npm/v/install-guard)
2
+ ![downloads](https://img.shields.io/npm/dw/install-guard)
3
+ ![license](https://img.shields.io/npm/l/install-guard)
4
+
1
5
  # 🚨 Should You Trust That npm Package Before Installing?
2
6
 
3
- **install-guard** analyzes npm packages and tells you if they are safe to install — 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.
4
8
 
5
9
  ---
6
10
 
7
11
  <details>
8
- <summary>Example</summary>
12
+ <summary>šŸ“¦ See it in action</summary>
9
13
 
10
14
  ```bash
11
- npx install-guard install some-random-lib
15
+ $ npx install-guard install some-random-lib
12
16
  ```
13
17
 
14
18
  ```
15
- šŸ“¦ Analyzing some-random-lib...
19
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
20
+ šŸ“¦ some-random-lib v0.1.3
21
+ A random utility library
22
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
23
+
24
+ Risk Level: šŸ’€ CRITICAL (9/10)
25
+ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘
26
+
27
+ ──────────────────────────────────────────────────────────
28
+
29
+ šŸ“Š Package Info
30
+
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
38
+
39
+ ──────────────────────────────────────────────────────────
16
40
 
17
- Downloads (weekly): 120
18
- Risk Score: 8/10 🚨 High Risk
41
+ šŸ” Findings
19
42
 
20
- ⚠ Very low downloads
21
- ⚠ Uses install scripts
22
- ⚠ Recently published
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
23
51
 
24
- ā“ Do you still want to install this package? (y/n)
52
+ ──────────────────────────────────────────────────────────
53
+
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):
25
63
  ```
26
64
 
27
65
  </details>
@@ -30,11 +68,12 @@ Risk Score: 8/10 🚨 High Risk
30
68
 
31
69
  ## 😬 The Problem
32
70
 
33
- Installing npm packages blindly is risky.
71
+ Supply chain attacks on npm are increasing:
34
72
 
35
- - Malicious packages are published daily
36
- - Popular packages get compromised
37
- - `npm audit` only checks known vulnerabilities — not trust
73
+ - Malicious packages are published **daily**
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
38
77
 
39
78
  You shouldn't have to guess if a package is safe.
40
79
 
@@ -42,90 +81,226 @@ You shouldn't have to guess if a package is safe.
42
81
 
43
82
  ## šŸ›”ļø The Solution
44
83
 
45
- install-guard gives you a **risk score before you install anything**.
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.
46
85
 
47
86
  ---
48
87
 
49
88
  ## ⚔ Quick Start
50
89
 
51
- Check a package:
90
+ Analyze any package:
52
91
 
53
92
  ```bash
54
93
  npx install-guard axios
55
94
  ```
56
95
 
57
- Safely install a package:
96
+ Analyze a specific version:
97
+
98
+ ```bash
99
+ npx install-guard axios@1.14.0
100
+ ```
101
+
102
+ Safely install with pre-check:
58
103
 
59
104
  ```bash
60
105
  npx install-guard install axios
61
106
  ```
62
107
 
63
- Scan your project:
108
+ Scan your entire project:
64
109
 
65
110
  ```bash
66
111
  npx install-guard scan
67
112
  ```
68
113
 
114
+ Scan with full details per package:
115
+
116
+ ```bash
117
+ npx install-guard scan --verbose
118
+ ```
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
+
69
132
  ---
70
133
 
71
- ## 🧠 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:
72
154
 
73
- install-guard analyzes:
155
+ - Download count check
156
+ - Publish age check
157
+ - Typosquatting detection
158
+ - Install script detection
74
159
 
75
- - šŸ“‰ Weekly downloads (popularity)
76
- - šŸ•’ Last update time
77
- - ⚠ Install/postinstall scripts
78
- - šŸ“¦ Version activity
160
+ This catches the exact pattern used in real supply chain attacks (e.g., injecting a malicious dependency in a patch release).
79
161
 
80
- Each factor contributes to a **risk score (0–10)**.
162
+ ### Safe Version Recommendation
81
163
 
82
- Lower score = safer package.
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.
83
165
 
84
166
  ---
85
167
 
86
- ## šŸ“Š Example Results
168
+ ## šŸ“Š Example Output
87
169
 
88
170
  ### āœ… Safe package
89
171
 
90
172
  ```
91
- axios
92
- Downloads: 20,000,000+
93
- Risk: 1/10
173
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
174
+ šŸ“¦ axios v1.15.0
175
+ Promise based HTTP client for the browser and node.js
176
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
177
+
178
+ Risk Level: āœ… LOW (1/10)
179
+ ā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘
180
+
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
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
94
192
  ```
95
193
 
96
- ---
194
+ ### 🚨 Compromised package (simulated)
97
195
 
98
- ### 🚨 Risky package
196
+ ```
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
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
218
+ ```
99
219
 
220
+ ### šŸ“‹ Project scan
221
+
222
+ ```
223
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
224
+ šŸ“‹ Dependency Scan Summary
225
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
226
+
227
+ Package Score Level
228
+ ────────────────────────────────────────────
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
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
100
237
  ```
101
- some-lib
102
- Downloads: 200
103
- Risk: 8/10
104
238
 
105
- ⚠ Low downloads
106
- ⚠ Uses install scripts
239
+ ---
240
+
241
+ ## šŸ—ļø Architecture
242
+
107
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
265
+ ```
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.
108
268
 
109
269
  ---
110
270
 
111
271
  ## ✨ Features
112
272
 
113
- - šŸ” Analyze any npm package instantly
114
- - ⚠ Risk scoring (0–10)
115
- - šŸ›‘ Block risky installs
116
- - šŸ“¦ Project-wide dependency scan
117
- - ⚔ Zero setup (works with npx)
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
118
285
 
119
286
  ---
120
287
 
121
288
  ## šŸ¤” Why not npm audit?
122
289
 
123
- | Feature | npm audit | install-guard |
124
- |-----------------------|-----------|------------|
125
- | Known vulnerabilities | āœ… | āœ… |
126
- | Trust analysis | āŒ | āœ… |
127
- | Pre-install check | āŒ | āœ… |
128
- | Install blocking | āŒ | āœ… |
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.
129
304
 
130
305
  ---
131
306
 
@@ -139,10 +314,12 @@ npm install -g install-guard
139
314
 
140
315
  ## šŸ”® Roadmap
141
316
 
142
- - šŸ” GitHub activity analysis
143
- - 🧠 Typosquatting detection
144
317
  - šŸ“Š Dependency tree visualization
145
- - šŸ”Œ CI/CD integration
318
+ - šŸ”Œ CI/CD integration (exit codes for pipelines)
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
146
323
 
147
324
  ---
148
325
 
@@ -154,5 +331,5 @@ PRs welcome! Let's make npm safer together.
154
331
 
155
332
  ## ⭐ Support
156
333
 
157
- If you find this useful, consider giving it a star ⭐
334
+ If you find this useful, consider giving it a star ⭐
158
335
  It helps others discover the project!
package/bin/cli.js CHANGED
@@ -8,28 +8,54 @@ const program = new Command();
8
8
 
9
9
  program
10
10
  .name("install-guard")
11
- .description("Check npm package risk before installing");
11
+ .description("Detect supply chain attacks in npm packages before installing")
12
+ .version("3.0.0");
12
13
 
13
14
  program
14
- .argument("[package]", "package name to analyze")
15
- .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) => {
16
19
  if (!pkg) {
17
- console.log("Please provide a package name");
20
+ program.help();
18
21
  return;
19
22
  }
20
- 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
+ });
21
37
  });
22
38
 
23
39
  program
24
40
  .command("scan")
25
- .description("Scan current project dependencies")
26
- .action(scanProject);
27
-
41
+ .description("Scan all project dependencies for supply chain risks")
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)")
45
+ .action(async (opts) => {
46
+ await scanProject({
47
+ verbose: opts.verbose,
48
+ json: opts.json,
49
+ skipGithub: opts.skipGithub,
50
+ });
51
+ });
52
+
28
53
  program
29
- .command("install <pkg>")
30
- .description("Analyze and install package safely")
31
- .action(async (pkg) => {
32
- const { analyzeAndPrompt } = await import("../src/install.js");
33
- await analyzeAndPrompt(pkg);
34
- });
54
+ .command("install <pkg>")
55
+ .description("Analyze and safely install a package")
56
+ .action(async (pkg) => {
57
+ const { analyzeAndPrompt } = await import("../src/install.js");
58
+ await analyzeAndPrompt(pkg);
59
+ });
60
+
35
61
  program.parse();
package/package.json CHANGED
@@ -1,22 +1,31 @@
1
1
  {
2
2
  "name": "install-guard",
3
- "version": "1.0.1",
3
+ "version": "1.1.2",
4
4
  "main": "index.js",
5
5
  "bin": {
6
6
  "install-guard": "./bin/cli.js"
7
7
  },
8
-
9
8
  "type": "module",
10
9
  "scripts": {
11
10
  "test": "echo \"Error: no test specified\" && exit 1"
12
11
  },
13
12
  "author": "Sarthak Kumar Sahoo",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/SarthakSahoo1407/install-guard.git"
16
+ },
14
17
  "license": "MIT",
15
- "description": "Check if an npm package is safe before installing",
18
+ "description": "Analyze npm packages for security risks before installing",
16
19
  "dependencies": {
17
20
  "chalk": "^5.6.2",
18
21
  "commander": "^14.0.3",
19
22
  "ora": "^9.3.0"
20
23
  },
21
- "keywords": ["npm", "security", "cli", "dependency", "audit"]
24
+ "keywords": [
25
+ "npm",
26
+ "security",
27
+ "cli",
28
+ "dependency",
29
+ "audit"
30
+ ]
22
31
  }
package/src/analyze.js CHANGED
@@ -1,34 +1,27 @@
1
- import chalk from "chalk";
2
1
  import ora from "ora";
3
- import { getPackageData } from "./npm.js";
4
- import { calculateRisk } from "./score.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 { score, warnings } = calculateRisk(data);
12
-
13
+ const result = await runPipeline(pkgName, version, opts);
13
14
  spinner.stop();
14
15
 
15
- console.log(chalk.bold(`\nšŸ“¦ ${data.name}`));
16
- console.log(`Version: ${data.version}`);
17
- console.log(`Downloads (weekly): ${data.downloads.toLocaleString()}`);
18
- console.log(`Risk Score: ${score}/10`);
19
-
20
- if (score <= 3) {
21
- console.log(chalk.green("āœ… Low risk"));
22
- } else if (score <= 6) {
23
- console.log(chalk.yellow("⚠ Medium risk"));
16
+ if (opts.json) {
17
+ console.log(JSON.stringify(result, null, 2));
24
18
  } else {
25
- console.log(chalk.red("🚨 High risk"));
19
+ console.log(formatPipelineResult(result));
26
20
  }
27
21
 
28
- warnings.forEach((w) => {
29
- console.log(chalk.yellow(`⚠ ${w}`));
30
- });
22
+ return result;
31
23
  } catch (err) {
32
- spinner.fail("Failed to fetch package");
24
+ spinner.fail(`Failed to analyze "${pkgName}": ${err.message}`);
25
+ return null;
33
26
  }
34
27
  }
@@ -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
+ }