devcompass 2.2.0 → 2.3.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.
- package/README.md +252 -178
- package/package.json +7 -4
- package/src/alerts/predictive.js +54 -0
- package/src/analyzers/bundle-size.js +85 -0
- package/src/analyzers/licenses.js +107 -0
- package/src/analyzers/scoring.js +18 -4
- package/src/analyzers/security.js +111 -0
- package/src/commands/analyze.js +183 -17
- package/src/utils/json-formatter.js +37 -3
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
cd ~/devCampuss
|
|
2
|
+
cat > README.md << 'EOF'
|
|
1
3
|
# 🧭 DevCompass
|
|
2
4
|
|
|
3
5
|
**Dependency health checker with ecosystem intelligence for JavaScript/TypeScript projects**
|
|
@@ -6,22 +8,37 @@
|
|
|
6
8
|
[](https://www.npmjs.com/package/devcompass)
|
|
7
9
|
[](https://opensource.org/licenses/MIT)
|
|
8
10
|
|
|
9
|
-
Analyze your JavaScript projects to find unused dependencies, outdated packages, **detect
|
|
11
|
+
Analyze your JavaScript projects to find unused dependencies, outdated packages, **detect security vulnerabilities**, **check bundle sizes**, **verify licenses**, and **automatically fix issues** with a single command. Perfect for **CI/CD pipelines** with JSON output and exit codes.
|
|
10
12
|
|
|
13
|
+
> **NEW in v2.3.1:** Fixed all security vulnerabilities! Health score: 2.5/10 → 8/10 🔒
|
|
14
|
+
> **NEW in v2.3:** Security scanning, bundle analysis & license checker! 🔐
|
|
11
15
|
> **NEW in v2.2:** CI/CD integration with JSON output & smart caching! 🚀
|
|
12
16
|
> **NEW in v2.1:** Auto-fix command! 🔧 Fix critical issues automatically!
|
|
13
17
|
> **NEW in v2.0:** Real-time ecosystem alerts for known issues! 🚨
|
|
14
18
|
|
|
19
|
+
## 🎉 Latest Update: v2.3.1
|
|
20
|
+
|
|
21
|
+
**We practice what we preach!** After releasing v2.3.0 with security scanning, we ran DevCompass on itself and found 14 vulnerabilities. We fixed them all:
|
|
22
|
+
|
|
23
|
+
- ✅ **Health score improved:** 2.5/10 → 8/10
|
|
24
|
+
- ✅ **Security vulnerabilities:** 14 → 0
|
|
25
|
+
- ✅ **Bundle size reduced:** 9.1 MB → 6.2 MB (32% smaller)
|
|
26
|
+
- ✅ **Dependencies upgraded:** npm-check-updates v16 → v20
|
|
27
|
+
- ✅ **Removed 315 vulnerable packages**
|
|
28
|
+
|
|
29
|
+
This is what "eating your own dog food" looks like. DevCompass helps you catch and fix security issues before they reach production.
|
|
30
|
+
|
|
15
31
|
## ✨ Features
|
|
16
32
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
33
|
+
- 🔐 **Security Scanning** (v2.3) - npm audit integration with severity breakdown
|
|
34
|
+
- 📦 **Bundle Size Analysis** (v2.3) - Identify heavy packages (> 1MB)
|
|
35
|
+
- ⚖️ **License Checker** (v2.3) - Detect restrictive licenses (GPL, AGPL)
|
|
36
|
+
- 🚀 **CI/CD Integration** (v2.2) - JSON output, exit codes, and silent mode
|
|
37
|
+
- ⚡ **Smart Caching** (v2.2) - 70% faster on repeated runs
|
|
38
|
+
- 🎛️ **Advanced Filtering** (v2.2) - Control alerts by severity level
|
|
20
39
|
- 🔧 **Auto-Fix Command** (v2.1) - Fix issues automatically with one command
|
|
21
40
|
- 🚨 **Ecosystem Intelligence** (v2.0) - Detect known issues before they break production
|
|
22
41
|
- 🔍 **Detect unused dependencies** - Find packages you're not actually using
|
|
23
|
-
- 📦 **Check for outdated packages** - See what needs updating
|
|
24
|
-
- 🔐 **Security alerts** - Critical vulnerabilities and deprecated packages
|
|
25
42
|
- 📊 **Project health score** - Get a 0-10 rating for your dependencies
|
|
26
43
|
- 🎨 **Beautiful terminal UI** - Colored output with severity indicators
|
|
27
44
|
- 🔧 **Framework-aware** - Handles React, Next.js, Angular, NestJS, PostCSS, Tailwind
|
|
@@ -63,7 +80,145 @@ devcompass analyze --ci
|
|
|
63
80
|
devcompass analyze --silent
|
|
64
81
|
```
|
|
65
82
|
|
|
66
|
-
##
|
|
83
|
+
## 🔐 Security & Compliance Features
|
|
84
|
+
|
|
85
|
+
### Security Vulnerability Scanning
|
|
86
|
+
|
|
87
|
+
DevCompass integrates with **npm audit** to detect security vulnerabilities automatically!
|
|
88
|
+
|
|
89
|
+
**Example Output:**
|
|
90
|
+
```
|
|
91
|
+
🔐 SECURITY VULNERABILITIES (12)
|
|
92
|
+
|
|
93
|
+
🔴 CRITICAL: 2
|
|
94
|
+
🟠 HIGH: 4
|
|
95
|
+
🟡 MODERATE: 5
|
|
96
|
+
⚪ LOW: 1
|
|
97
|
+
|
|
98
|
+
Run npm audit fix to fix vulnerabilities
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**How it works:**
|
|
102
|
+
1. Runs `npm audit` in the background
|
|
103
|
+
2. Parses vulnerability data
|
|
104
|
+
3. Shows severity breakdown
|
|
105
|
+
4. Impacts health score (-2.5 per critical issue)
|
|
106
|
+
5. Suggests fix commands
|
|
107
|
+
|
|
108
|
+
**Health Score Impact:**
|
|
109
|
+
- Critical: −2.5 points each
|
|
110
|
+
- High: −1.5 points each
|
|
111
|
+
- Moderate: −0.5 points each
|
|
112
|
+
- Low: −0.2 points each
|
|
113
|
+
|
|
114
|
+
### Bundle Size Analysis
|
|
115
|
+
|
|
116
|
+
Identify large dependencies that bloat your `node_modules`!
|
|
117
|
+
|
|
118
|
+
**Example Output:**
|
|
119
|
+
```
|
|
120
|
+
📦 HEAVY PACKAGES (3)
|
|
121
|
+
|
|
122
|
+
Packages larger than 1MB:
|
|
123
|
+
|
|
124
|
+
webpack 2.3 MB
|
|
125
|
+
typescript 8.1 MB
|
|
126
|
+
@tensorflow/tfjs 12.4 MB
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Perfect for:**
|
|
130
|
+
- Frontend developers optimizing bundle size
|
|
131
|
+
- Identifying unnecessary large dependencies
|
|
132
|
+
- Web performance optimization
|
|
133
|
+
- Docker image size reduction
|
|
134
|
+
|
|
135
|
+
### License Compliance Checker
|
|
136
|
+
|
|
137
|
+
Detect restrictive licenses that may require legal review!
|
|
138
|
+
|
|
139
|
+
**Example Output:**
|
|
140
|
+
```
|
|
141
|
+
⚖️ LICENSE WARNINGS (2)
|
|
142
|
+
|
|
143
|
+
sharp - Restrictive (LGPL-3.0)
|
|
144
|
+
custom-lib - Unknown (UNLICENSED)
|
|
145
|
+
|
|
146
|
+
Note: Restrictive licenses may require legal review
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**What gets flagged:**
|
|
150
|
+
- **Restrictive licenses:** GPL, AGPL, LGPL (may require source code disclosure)
|
|
151
|
+
- **Unknown licenses:** Packages without license information
|
|
152
|
+
- **Unlicensed packages:** Legal risk for commercial use
|
|
153
|
+
|
|
154
|
+
**Supported licenses:**
|
|
155
|
+
- ✅ **Safe:** MIT, Apache-2.0, BSD, ISC, CC0
|
|
156
|
+
- ⚠️ **Restrictive:** GPL, AGPL, LGPL
|
|
157
|
+
- ❓ **Unknown:** Missing or custom licenses
|
|
158
|
+
|
|
159
|
+
### Combined Analysis Example
|
|
160
|
+
|
|
161
|
+
**Full Output:**
|
|
162
|
+
```
|
|
163
|
+
🔍 DevCompass v2.3.1 - Analyzing your project...
|
|
164
|
+
✔ Scanned 25 dependencies in project
|
|
165
|
+
|
|
166
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
167
|
+
|
|
168
|
+
✅ SECURITY VULNERABILITIES
|
|
169
|
+
|
|
170
|
+
No vulnerabilities detected!
|
|
171
|
+
|
|
172
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
173
|
+
|
|
174
|
+
🚨 ECOSYSTEM ALERTS (1)
|
|
175
|
+
|
|
176
|
+
🟠 HIGH
|
|
177
|
+
axios@1.6.0
|
|
178
|
+
Issue: Memory leak in request interceptors
|
|
179
|
+
Fix: 1.6.2
|
|
180
|
+
|
|
181
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
182
|
+
|
|
183
|
+
📦 HEAVY PACKAGES (2)
|
|
184
|
+
|
|
185
|
+
Packages larger than 1MB:
|
|
186
|
+
|
|
187
|
+
typescript 8.1 MB
|
|
188
|
+
webpack 2.3 MB
|
|
189
|
+
|
|
190
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
191
|
+
|
|
192
|
+
✅ LICENSE COMPLIANCE
|
|
193
|
+
|
|
194
|
+
All licenses are permissive!
|
|
195
|
+
|
|
196
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
197
|
+
|
|
198
|
+
📊 PROJECT HEALTH
|
|
199
|
+
|
|
200
|
+
Overall Score: 8.5/10
|
|
201
|
+
Total Dependencies: 25
|
|
202
|
+
Ecosystem Alerts: 1
|
|
203
|
+
Unused: 0
|
|
204
|
+
Outdated: 2
|
|
205
|
+
|
|
206
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
207
|
+
|
|
208
|
+
💡 QUICK WINS
|
|
209
|
+
|
|
210
|
+
🔴 Fix critical issues:
|
|
211
|
+
|
|
212
|
+
npm install axios@1.6.2
|
|
213
|
+
|
|
214
|
+
Expected impact:
|
|
215
|
+
✓ Resolve critical stability issues
|
|
216
|
+
✓ Improve health score → 10/10
|
|
217
|
+
|
|
218
|
+
💡 TIP: Run 'devcompass fix' to apply these fixes automatically!
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 🚀 CI/CD Integration
|
|
67
222
|
|
|
68
223
|
### JSON Output
|
|
69
224
|
Perfect for parsing in CI/CD pipelines:
|
|
@@ -74,23 +229,38 @@ devcompass analyze --json
|
|
|
74
229
|
**Output:**
|
|
75
230
|
```json
|
|
76
231
|
{
|
|
77
|
-
"version": "2.
|
|
78
|
-
"timestamp": "2026-04-
|
|
232
|
+
"version": "2.3.1",
|
|
233
|
+
"timestamp": "2026-04-02T10:30:00.000Z",
|
|
79
234
|
"summary": {
|
|
80
|
-
"healthScore":
|
|
81
|
-
"totalDependencies":
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
235
|
+
"healthScore": 8.5,
|
|
236
|
+
"totalDependencies": 25,
|
|
237
|
+
"securityVulnerabilities": 0,
|
|
238
|
+
"ecosystemAlerts": 1,
|
|
239
|
+
"unusedDependencies": 0,
|
|
240
|
+
"outdatedPackages": 2,
|
|
241
|
+
"heavyPackages": 2,
|
|
242
|
+
"licenseWarnings": 0
|
|
243
|
+
},
|
|
244
|
+
"security": {
|
|
245
|
+
"total": 0,
|
|
246
|
+
"critical": 0,
|
|
247
|
+
"high": 0,
|
|
248
|
+
"moderate": 0,
|
|
249
|
+
"low": 0,
|
|
250
|
+
"vulnerabilities": []
|
|
251
|
+
},
|
|
252
|
+
"bundleAnalysis": {
|
|
253
|
+
"heavyPackages": [
|
|
254
|
+
{ "name": "typescript", "size": "8.1 MB" },
|
|
255
|
+
{ "name": "webpack", "size": "2.3 MB" }
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
"licenses": {
|
|
259
|
+
"warnings": []
|
|
85
260
|
},
|
|
86
261
|
"ecosystemAlerts": [...],
|
|
87
|
-
"unusedDependencies": [
|
|
88
|
-
"outdatedPackages": [...]
|
|
89
|
-
"scoreBreakdown": {
|
|
90
|
-
"unusedPenalty": 0.8,
|
|
91
|
-
"outdatedPenalty": 1.7,
|
|
92
|
-
"alertsPenalty": 3.5
|
|
93
|
-
}
|
|
262
|
+
"unusedDependencies": [],
|
|
263
|
+
"outdatedPackages": [...]
|
|
94
264
|
}
|
|
95
265
|
```
|
|
96
266
|
|
|
@@ -115,8 +285,8 @@ jobs:
|
|
|
115
285
|
steps:
|
|
116
286
|
- uses: actions/checkout@v3
|
|
117
287
|
- uses: actions/setup-node@v3
|
|
118
|
-
- run: npm install
|
|
119
|
-
- run: devcompass analyze --ci
|
|
288
|
+
- run: npm install
|
|
289
|
+
- run: npx devcompass analyze --ci
|
|
120
290
|
```
|
|
121
291
|
|
|
122
292
|
### Silent Mode
|
|
@@ -126,15 +296,23 @@ devcompass analyze --silent
|
|
|
126
296
|
echo $? # Check exit code
|
|
127
297
|
```
|
|
128
298
|
|
|
129
|
-
## ⚡
|
|
299
|
+
## ⚡ Smart Caching
|
|
130
300
|
|
|
131
|
-
DevCompass
|
|
301
|
+
DevCompass caches results to improve performance:
|
|
132
302
|
|
|
133
303
|
- **First run:** Normal speed (fetches all data)
|
|
134
304
|
- **Cached runs:** ~70% faster
|
|
135
305
|
- **Cache duration:** 1 hour
|
|
136
306
|
- **Cache file:** `.devcompass-cache.json` (auto-gitignored)
|
|
137
307
|
|
|
308
|
+
**What gets cached:**
|
|
309
|
+
- Security vulnerabilities
|
|
310
|
+
- Ecosystem alerts
|
|
311
|
+
- Unused dependencies
|
|
312
|
+
- Outdated packages
|
|
313
|
+
- Bundle sizes
|
|
314
|
+
- License information
|
|
315
|
+
|
|
138
316
|
**Disable caching:**
|
|
139
317
|
```json
|
|
140
318
|
// devcompass.config.json
|
|
@@ -143,7 +321,7 @@ DevCompass now caches results to improve performance:
|
|
|
143
321
|
}
|
|
144
322
|
```
|
|
145
323
|
|
|
146
|
-
## 🎛️
|
|
324
|
+
## 🎛️ Advanced Configuration
|
|
147
325
|
|
|
148
326
|
Create `devcompass.config.json` in your project root:
|
|
149
327
|
```json
|
|
@@ -174,32 +352,33 @@ Create `devcompass.config.json` in your project root:
|
|
|
174
352
|
|
|
175
353
|
### Example Configurations
|
|
176
354
|
|
|
177
|
-
**
|
|
355
|
+
**Security-focused (strict):**
|
|
178
356
|
```json
|
|
179
357
|
{
|
|
180
358
|
"minSeverity": "critical",
|
|
181
|
-
"minScore":
|
|
359
|
+
"minScore": 9
|
|
182
360
|
}
|
|
183
361
|
```
|
|
184
362
|
|
|
185
|
-
**
|
|
363
|
+
**Balanced (recommended):**
|
|
186
364
|
```json
|
|
187
365
|
{
|
|
188
|
-
"ignoreSeverity": ["low"]
|
|
366
|
+
"ignoreSeverity": ["low"],
|
|
367
|
+
"minScore": 7
|
|
189
368
|
}
|
|
190
369
|
```
|
|
191
370
|
|
|
192
|
-
**
|
|
371
|
+
**Relaxed (development):**
|
|
193
372
|
```json
|
|
194
373
|
{
|
|
195
|
-
"
|
|
196
|
-
"
|
|
374
|
+
"ignoreSeverity": ["low", "medium"],
|
|
375
|
+
"minScore": 5
|
|
197
376
|
}
|
|
198
377
|
```
|
|
199
378
|
|
|
200
379
|
## 🔧 Auto-Fix Command
|
|
201
380
|
|
|
202
|
-
DevCompass can
|
|
381
|
+
DevCompass can **automatically fix issues** in your project!
|
|
203
382
|
|
|
204
383
|
### What it does:
|
|
205
384
|
- 🔴 **Fixes critical security issues** - Upgrades packages with known vulnerabilities
|
|
@@ -220,67 +399,6 @@ devcompass fix -y
|
|
|
220
399
|
devcompass fix --path /path/to/project
|
|
221
400
|
```
|
|
222
401
|
|
|
223
|
-
### Example Output
|
|
224
|
-
```
|
|
225
|
-
🔧 DevCompass Fix - Analyzing and fixing your project...
|
|
226
|
-
|
|
227
|
-
🔴 CRITICAL ISSUES TO FIX:
|
|
228
|
-
|
|
229
|
-
🔴 lodash@4.17.19
|
|
230
|
-
Issue: Prototype pollution vulnerability
|
|
231
|
-
Fix: Upgrade to 4.17.21
|
|
232
|
-
|
|
233
|
-
🟠 axios@1.6.0
|
|
234
|
-
Issue: Memory leak in request interceptors
|
|
235
|
-
Fix: Upgrade to 1.6.2
|
|
236
|
-
|
|
237
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
238
|
-
|
|
239
|
-
🧹 UNUSED DEPENDENCIES TO REMOVE:
|
|
240
|
-
|
|
241
|
-
● moment
|
|
242
|
-
● express
|
|
243
|
-
|
|
244
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
245
|
-
|
|
246
|
-
⬆️ SAFE UPDATES (patch/minor):
|
|
247
|
-
|
|
248
|
-
react-dom: 18.2.0 → 18.2.1 (patch update)
|
|
249
|
-
|
|
250
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
251
|
-
|
|
252
|
-
⚠️ MAJOR UPDATES (skipped - may have breaking changes):
|
|
253
|
-
|
|
254
|
-
express: 4.18.0 → 5.2.1
|
|
255
|
-
|
|
256
|
-
Run these manually after reviewing changelog:
|
|
257
|
-
npm install express@5.2.1
|
|
258
|
-
|
|
259
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
260
|
-
|
|
261
|
-
📊 FIX SUMMARY:
|
|
262
|
-
|
|
263
|
-
Critical fixes: 2
|
|
264
|
-
Remove unused: 2
|
|
265
|
-
Safe updates: 1
|
|
266
|
-
Skipped major: 1
|
|
267
|
-
|
|
268
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
269
|
-
|
|
270
|
-
❓ Apply these fixes? (y/N): y
|
|
271
|
-
|
|
272
|
-
🔧 Applying fixes...
|
|
273
|
-
|
|
274
|
-
✔ ✅ Removed 2 unused packages
|
|
275
|
-
✔ ✅ Fixed lodash@4.17.21
|
|
276
|
-
✔ ✅ Fixed axios@1.6.2
|
|
277
|
-
✔ ✅ Updated 1 packages
|
|
278
|
-
|
|
279
|
-
✨ All fixes applied successfully!
|
|
280
|
-
|
|
281
|
-
💡 Run devcompass analyze to see the new health score.
|
|
282
|
-
```
|
|
283
|
-
|
|
284
402
|
### Safety Features
|
|
285
403
|
- ✅ Shows what will be changed before applying
|
|
286
404
|
- ✅ Requires confirmation (unless `--yes` flag used)
|
|
@@ -300,73 +418,6 @@ devcompass fix
|
|
|
300
418
|
devcompass analyze
|
|
301
419
|
```
|
|
302
420
|
|
|
303
|
-
## 📊 Analyze Command
|
|
304
|
-
|
|
305
|
-
### Example Output (v2.2)
|
|
306
|
-
```
|
|
307
|
-
🔍 DevCompass v2.2.0 - Analyzing your project...
|
|
308
|
-
✔ Scanned 15 dependencies in project
|
|
309
|
-
|
|
310
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
311
|
-
|
|
312
|
-
🚨 ECOSYSTEM ALERTS (2)
|
|
313
|
-
|
|
314
|
-
🔴 CRITICAL
|
|
315
|
-
lodash@4.17.19
|
|
316
|
-
Issue: Prototype pollution vulnerability
|
|
317
|
-
Affected: <4.17.21
|
|
318
|
-
Fix: 4.17.21
|
|
319
|
-
Source: npm advisory 1523
|
|
320
|
-
|
|
321
|
-
🟠 HIGH
|
|
322
|
-
axios@1.6.0
|
|
323
|
-
Issue: Memory leak in request interceptors
|
|
324
|
-
Affected: >=1.5.0 <1.6.2
|
|
325
|
-
Fix: 1.6.2
|
|
326
|
-
Source: GitHub Issue #5456
|
|
327
|
-
|
|
328
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
329
|
-
|
|
330
|
-
🔴 UNUSED DEPENDENCIES (2)
|
|
331
|
-
● moment
|
|
332
|
-
● request
|
|
333
|
-
|
|
334
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
335
|
-
|
|
336
|
-
🟡 OUTDATED PACKAGES (3)
|
|
337
|
-
react 18.2.0 → ^19.0.0 (major update)
|
|
338
|
-
express 4.18.0 → ^4.19.0 (patch update)
|
|
339
|
-
|
|
340
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
341
|
-
|
|
342
|
-
📊 PROJECT HEALTH
|
|
343
|
-
Overall Score: 5.5/10
|
|
344
|
-
Total Dependencies: 15
|
|
345
|
-
Ecosystem Alerts: 2
|
|
346
|
-
Unused: 2
|
|
347
|
-
Outdated: 3
|
|
348
|
-
|
|
349
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
350
|
-
|
|
351
|
-
💡 QUICK WINS
|
|
352
|
-
🔴 Fix critical issues:
|
|
353
|
-
|
|
354
|
-
npm install lodash@4.17.21
|
|
355
|
-
npm install axios@1.6.2
|
|
356
|
-
|
|
357
|
-
🧹 Clean up unused dependencies:
|
|
358
|
-
|
|
359
|
-
npm uninstall moment request
|
|
360
|
-
|
|
361
|
-
Expected impact:
|
|
362
|
-
✓ Resolve critical security/stability issues
|
|
363
|
-
✓ Remove 2 unused packages
|
|
364
|
-
✓ Reduce node_modules size
|
|
365
|
-
✓ Improve health score → 8.5/10
|
|
366
|
-
|
|
367
|
-
💡 TIP: Run 'devcompass fix' to apply these fixes automatically!
|
|
368
|
-
```
|
|
369
|
-
|
|
370
421
|
## 🚨 Ecosystem Intelligence
|
|
371
422
|
|
|
372
423
|
DevCompass tracks **real-world issues** in popular packages and warns you before they break production!
|
|
@@ -420,11 +471,12 @@ DevCompass won't flag these as unused (they're typically used in config files):
|
|
|
420
471
|
- Shows current vs latest versions
|
|
421
472
|
- Indicates update type (major/minor/patch)
|
|
422
473
|
|
|
423
|
-
### Health Score
|
|
474
|
+
### Health Score
|
|
424
475
|
Calculated from 0-10 based on:
|
|
425
476
|
- Percentage of unused dependencies (−4 points per 100%)
|
|
426
477
|
- Percentage of outdated packages (−3 points per 100%)
|
|
427
478
|
- Ecosystem alerts by severity (−0.2 to −2.0 per issue)
|
|
479
|
+
- Security vulnerabilities by severity (−0.2 to −2.5 per issue)
|
|
428
480
|
- Higher score = healthier project
|
|
429
481
|
|
|
430
482
|
## ⚙️ Commands & Options
|
|
@@ -511,6 +563,21 @@ if [ $? -ne 0 ]; then
|
|
|
511
563
|
fi
|
|
512
564
|
```
|
|
513
565
|
|
|
566
|
+
### Security-Focused Workflow
|
|
567
|
+
```bash
|
|
568
|
+
# 1. Run security scan
|
|
569
|
+
devcompass analyze
|
|
570
|
+
|
|
571
|
+
# 2. Check for critical vulnerabilities
|
|
572
|
+
devcompass analyze --json | jq '.security.critical'
|
|
573
|
+
|
|
574
|
+
# 3. Auto-fix if possible
|
|
575
|
+
npm audit fix
|
|
576
|
+
|
|
577
|
+
# 4. Verify fixes
|
|
578
|
+
devcompass analyze
|
|
579
|
+
```
|
|
580
|
+
|
|
514
581
|
## ⚠️ Known Issues & Best Practices
|
|
515
582
|
|
|
516
583
|
### Installation
|
|
@@ -543,12 +610,14 @@ If you encounter a false positive, please [report it](https://github.com/AjayBTh
|
|
|
543
610
|
|
|
544
611
|
1. **Run regularly** - Add to your CI/CD pipeline or git hooks
|
|
545
612
|
2. **Use fix command** - Let DevCompass handle routine maintenance
|
|
546
|
-
3. **
|
|
547
|
-
4. **
|
|
548
|
-
5. **
|
|
549
|
-
6. **
|
|
550
|
-
7. **
|
|
551
|
-
8. **
|
|
613
|
+
3. **Check security first** - Prioritize fixing critical vulnerabilities
|
|
614
|
+
4. **Monitor bundle size** - Keep an eye on heavy packages
|
|
615
|
+
5. **Review licenses** - Ensure compliance with your legal requirements
|
|
616
|
+
6. **Configure severity levels** - Filter out noise with `minSeverity`
|
|
617
|
+
7. **Enable CI mode** - Catch issues before they reach production
|
|
618
|
+
8. **Use JSON output** - Integrate with your monitoring tools
|
|
619
|
+
9. **Review major updates** - Always check changelogs before major version bumps
|
|
620
|
+
10. **Verify before uninstalling** - DevCompass helps identify candidates, but always verify
|
|
552
621
|
|
|
553
622
|
## 🤝 Contributing
|
|
554
623
|
|
|
@@ -632,18 +701,22 @@ Check out DevCompass stats:
|
|
|
632
701
|
|
|
633
702
|
## 🌟 What's Next?
|
|
634
703
|
|
|
635
|
-
### Roadmap
|
|
704
|
+
### Roadmap
|
|
636
705
|
- [x] ~~Automatic fix command~~ ✅ **Added in v2.1!**
|
|
637
706
|
- [x] ~~CI/CD integration with JSON output~~ ✅ **Added in v2.2!**
|
|
638
707
|
- [x] ~~Smart caching system~~ ✅ **Added in v2.2!**
|
|
639
708
|
- [x] ~~Custom ignore rules via config file~~ ✅ **Added in v2.2!**
|
|
640
|
-
- [
|
|
641
|
-
- [
|
|
642
|
-
- [
|
|
643
|
-
- [
|
|
644
|
-
- [ ]
|
|
645
|
-
- [ ] Automated security patch suggestions
|
|
646
|
-
- [ ]
|
|
709
|
+
- [x] ~~npm audit integration~~ ✅ **Added in v2.3!**
|
|
710
|
+
- [x] ~~Bundle size analysis~~ ✅ **Added in v2.3!**
|
|
711
|
+
- [x] ~~License compliance checker~~ ✅ **Added in v2.3!**
|
|
712
|
+
- [x] ~~Fix all security vulnerabilities~~ ✅ **Fixed in v2.3.1!**
|
|
713
|
+
- [ ] GitHub Issues API for real-time issue tracking (v2.4.0)
|
|
714
|
+
- [ ] Automated security patch suggestions (v2.4.0)
|
|
715
|
+
- [ ] Dependency graph visualization (v2.5.0)
|
|
716
|
+
- [ ] Web dashboard for team health monitoring (v2.5.0)
|
|
717
|
+
- [ ] More tracked packages (React, Next.js, Vue, Angular) (v2.5.0)
|
|
718
|
+
- [ ] Team collaboration features (v2.6.0)
|
|
719
|
+
- [ ] Slack/Discord notifications (v2.6.0)
|
|
647
720
|
|
|
648
721
|
Want to contribute? Pick an item and open an issue! 🚀
|
|
649
722
|
|
|
@@ -653,4 +726,5 @@ Want to contribute? Pick an item and open an issue! 🚀
|
|
|
653
726
|
|
|
654
727
|
*DevCompass - Keep your dependencies healthy!* 🧭
|
|
655
728
|
|
|
656
|
-
**Like Lighthouse for your dependencies** ⚡
|
|
729
|
+
**Like Lighthouse for your dependencies** ⚡
|
|
730
|
+
EOF
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devcompass",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "Dependency health checker with ecosystem intelligence for JavaScript/TypeScript projects",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,10 @@
|
|
|
33
33
|
"ci-cd",
|
|
34
34
|
"automation",
|
|
35
35
|
"caching",
|
|
36
|
-
"json-output"
|
|
36
|
+
"json-output",
|
|
37
|
+
"npm-audit",
|
|
38
|
+
"bundle-size",
|
|
39
|
+
"license-checker"
|
|
37
40
|
],
|
|
38
41
|
"author": "Ajay Thorat <ajaythorat988@gmail.com>",
|
|
39
42
|
"license": "MIT",
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
"chalk": "^4.1.2",
|
|
42
45
|
"commander": "^11.1.0",
|
|
43
46
|
"depcheck": "^1.4.7",
|
|
44
|
-
"npm-check-updates": "^
|
|
47
|
+
"npm-check-updates": "^20.0.0",
|
|
45
48
|
"ora": "^5.4.1",
|
|
46
49
|
"semver": "^7.6.0"
|
|
47
50
|
},
|
|
@@ -56,4 +59,4 @@
|
|
|
56
59
|
"url": "https://github.com/AjayBThorat-20/devcompass/issues"
|
|
57
60
|
},
|
|
58
61
|
"homepage": "https://github.com/AjayBThorat-20/devcompass#readme"
|
|
59
|
-
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/alerts/predictive.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Analyze package health trends
|
|
5
|
+
* NOTE: This is a simplified version without GitHub API
|
|
6
|
+
* For production, integrate with GitHub Issues API
|
|
7
|
+
*/
|
|
8
|
+
async function analyzeTrends(packageName) {
|
|
9
|
+
// Placeholder for future GitHub API integration
|
|
10
|
+
// For now, return basic analysis
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
package: packageName,
|
|
14
|
+
recentIssues: 0,
|
|
15
|
+
trend: 'stable',
|
|
16
|
+
recommendation: null
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Calculate risk score based on trends
|
|
22
|
+
*/
|
|
23
|
+
function calculateRiskScore(trends) {
|
|
24
|
+
let risk = 0;
|
|
25
|
+
|
|
26
|
+
// High issue activity = higher risk
|
|
27
|
+
if (trends.recentIssues > 20) {
|
|
28
|
+
risk += 3;
|
|
29
|
+
} else if (trends.recentIssues > 10) {
|
|
30
|
+
risk += 2;
|
|
31
|
+
} else if (trends.recentIssues > 5) {
|
|
32
|
+
risk += 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return risk;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate predictive warnings
|
|
40
|
+
*/
|
|
41
|
+
function generatePredictiveWarnings(packages) {
|
|
42
|
+
const warnings = [];
|
|
43
|
+
|
|
44
|
+
// This is a placeholder
|
|
45
|
+
// In production, this would analyze GitHub activity
|
|
46
|
+
|
|
47
|
+
return warnings;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
analyzeTrends,
|
|
52
|
+
calculateRiskScore,
|
|
53
|
+
generatePredictiveWarnings
|
|
54
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// src/analyzers/bundle-size.js
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get package sizes from node_modules
|
|
7
|
+
*/
|
|
8
|
+
async function analyzeBundleSizes(projectPath, dependencies) {
|
|
9
|
+
const sizes = [];
|
|
10
|
+
|
|
11
|
+
for (const packageName of Object.keys(dependencies)) {
|
|
12
|
+
try {
|
|
13
|
+
const packagePath = path.join(projectPath, 'node_modules', packageName);
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(packagePath)) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const size = await getDirectorySize(packagePath);
|
|
20
|
+
const sizeInKB = Math.round(size / 1024);
|
|
21
|
+
|
|
22
|
+
sizes.push({
|
|
23
|
+
name: packageName,
|
|
24
|
+
size: sizeInKB,
|
|
25
|
+
sizeFormatted: formatSize(sizeInKB)
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// Skip packages we can't measure
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Sort by size (largest first)
|
|
35
|
+
sizes.sort((a, b) => b.size - a.size);
|
|
36
|
+
|
|
37
|
+
return sizes;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get total size of directory recursively
|
|
42
|
+
*/
|
|
43
|
+
function getDirectorySize(dirPath) {
|
|
44
|
+
let totalSize = 0;
|
|
45
|
+
|
|
46
|
+
const items = fs.readdirSync(dirPath);
|
|
47
|
+
|
|
48
|
+
for (const item of items) {
|
|
49
|
+
const itemPath = path.join(dirPath, item);
|
|
50
|
+
const stats = fs.statSync(itemPath);
|
|
51
|
+
|
|
52
|
+
if (stats.isFile()) {
|
|
53
|
+
totalSize += stats.size;
|
|
54
|
+
} else if (stats.isDirectory()) {
|
|
55
|
+
totalSize += getDirectorySize(itemPath);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return totalSize;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Format size in human-readable format
|
|
64
|
+
*/
|
|
65
|
+
function formatSize(kb) {
|
|
66
|
+
if (kb < 1024) {
|
|
67
|
+
return `${kb} KB`;
|
|
68
|
+
} else {
|
|
69
|
+
const mb = (kb / 1024).toFixed(1);
|
|
70
|
+
return `${mb} MB`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Identify heavy packages (> 1MB)
|
|
76
|
+
*/
|
|
77
|
+
function findHeavyPackages(sizes) {
|
|
78
|
+
return sizes.filter(pkg => pkg.size > 1024); // > 1MB
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
analyzeBundleSizes,
|
|
83
|
+
findHeavyPackages,
|
|
84
|
+
formatSize
|
|
85
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/analyzers/licenses.js
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
// Restrictive licenses that might cause issues
|
|
6
|
+
const RESTRICTIVE_LICENSES = [
|
|
7
|
+
'GPL',
|
|
8
|
+
'GPL-2.0',
|
|
9
|
+
'GPL-3.0',
|
|
10
|
+
'AGPL',
|
|
11
|
+
'AGPL-3.0',
|
|
12
|
+
'LGPL',
|
|
13
|
+
'LGPL-2.1',
|
|
14
|
+
'LGPL-3.0'
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
// Permissive licenses (usually safe)
|
|
18
|
+
const PERMISSIVE_LICENSES = [
|
|
19
|
+
'MIT',
|
|
20
|
+
'Apache-2.0',
|
|
21
|
+
'BSD-2-Clause',
|
|
22
|
+
'BSD-3-Clause',
|
|
23
|
+
'ISC',
|
|
24
|
+
'CC0-1.0',
|
|
25
|
+
'Unlicense'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check licenses of all dependencies
|
|
30
|
+
*/
|
|
31
|
+
async function checkLicenses(projectPath, dependencies) {
|
|
32
|
+
const licenses = [];
|
|
33
|
+
|
|
34
|
+
for (const [packageName, version] of Object.entries(dependencies)) {
|
|
35
|
+
try {
|
|
36
|
+
const packageJsonPath = path.join(
|
|
37
|
+
projectPath,
|
|
38
|
+
'node_modules',
|
|
39
|
+
packageName,
|
|
40
|
+
'package.json'
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
48
|
+
const license = packageJson.license || 'UNKNOWN';
|
|
49
|
+
|
|
50
|
+
licenses.push({
|
|
51
|
+
package: packageName,
|
|
52
|
+
license: license,
|
|
53
|
+
type: getLicenseType(license)
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
// Skip packages we can't read
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return licenses;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Determine license type
|
|
67
|
+
*/
|
|
68
|
+
function getLicenseType(license) {
|
|
69
|
+
const licenseStr = String(license).toUpperCase();
|
|
70
|
+
|
|
71
|
+
// Check for restrictive licenses
|
|
72
|
+
for (const restrictive of RESTRICTIVE_LICENSES) {
|
|
73
|
+
if (licenseStr.includes(restrictive)) {
|
|
74
|
+
return 'restrictive';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check for permissive licenses
|
|
79
|
+
for (const permissive of PERMISSIVE_LICENSES) {
|
|
80
|
+
if (licenseStr.includes(permissive)) {
|
|
81
|
+
return 'permissive';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Unknown or custom license
|
|
86
|
+
if (licenseStr === 'UNKNOWN' || licenseStr === 'UNLICENSED') {
|
|
87
|
+
return 'unknown';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return 'other';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Find problematic licenses
|
|
95
|
+
*/
|
|
96
|
+
function findProblematicLicenses(licenses) {
|
|
97
|
+
return licenses.filter(pkg =>
|
|
98
|
+
pkg.type === 'restrictive' || pkg.type === 'unknown'
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
checkLicenses,
|
|
104
|
+
findProblematicLicenses,
|
|
105
|
+
RESTRICTIVE_LICENSES,
|
|
106
|
+
PERMISSIVE_LICENSES
|
|
107
|
+
};
|
package/src/analyzers/scoring.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// src/analyzers/scoring.js
|
|
2
|
-
function calculateScore(
|
|
2
|
+
function calculateScore(
|
|
3
|
+
totalDeps,
|
|
4
|
+
unusedCount,
|
|
5
|
+
outdatedCount,
|
|
6
|
+
alertsCount = 0,
|
|
7
|
+
alertPenalty = 0,
|
|
8
|
+
securityPenalty = 0 // NEW
|
|
9
|
+
) {
|
|
3
10
|
let score = 10;
|
|
4
11
|
|
|
5
12
|
if (totalDeps === 0) {
|
|
@@ -9,9 +16,11 @@ function calculateScore(totalDeps, unusedCount, outdatedCount, alertsCount = 0,
|
|
|
9
16
|
unused: 0,
|
|
10
17
|
outdated: 0,
|
|
11
18
|
alerts: 0,
|
|
19
|
+
security: 0,
|
|
12
20
|
unusedPenalty: 0,
|
|
13
21
|
outdatedPenalty: 0,
|
|
14
|
-
alertsPenalty: 0
|
|
22
|
+
alertsPenalty: 0,
|
|
23
|
+
securityPenalty: 0
|
|
15
24
|
}
|
|
16
25
|
};
|
|
17
26
|
}
|
|
@@ -26,9 +35,12 @@ function calculateScore(totalDeps, unusedCount, outdatedCount, alertsCount = 0,
|
|
|
26
35
|
const outdatedPenalty = outdatedRatio * 3;
|
|
27
36
|
score -= outdatedPenalty;
|
|
28
37
|
|
|
29
|
-
// Ecosystem alerts penalty
|
|
38
|
+
// Ecosystem alerts penalty
|
|
30
39
|
score -= alertPenalty;
|
|
31
40
|
|
|
41
|
+
// Security vulnerabilities penalty (NEW)
|
|
42
|
+
score -= securityPenalty;
|
|
43
|
+
|
|
32
44
|
// Ensure score is between 0 and 10
|
|
33
45
|
score = Math.max(0, Math.min(10, score));
|
|
34
46
|
|
|
@@ -38,9 +50,11 @@ function calculateScore(totalDeps, unusedCount, outdatedCount, alertsCount = 0,
|
|
|
38
50
|
unused: unusedCount,
|
|
39
51
|
outdated: outdatedCount,
|
|
40
52
|
alerts: alertsCount,
|
|
53
|
+
security: securityPenalty > 0 ? 1 : 0, // Binary indicator
|
|
41
54
|
unusedPenalty: parseFloat(unusedPenalty.toFixed(1)),
|
|
42
55
|
outdatedPenalty: parseFloat(outdatedPenalty.toFixed(1)),
|
|
43
|
-
alertsPenalty: parseFloat(alertPenalty.toFixed(1))
|
|
56
|
+
alertsPenalty: parseFloat(alertPenalty.toFixed(1)),
|
|
57
|
+
securityPenalty: parseFloat(securityPenalty.toFixed(1))
|
|
44
58
|
}
|
|
45
59
|
};
|
|
46
60
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/analyzers/security.js
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Run npm audit and parse results
|
|
6
|
+
*/
|
|
7
|
+
async function checkSecurity(projectPath) {
|
|
8
|
+
try {
|
|
9
|
+
// Run npm audit in JSON mode
|
|
10
|
+
const auditOutput = execSync('npm audit --json', {
|
|
11
|
+
cwd: projectPath,
|
|
12
|
+
encoding: 'utf8',
|
|
13
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const auditData = JSON.parse(auditOutput);
|
|
17
|
+
|
|
18
|
+
// Extract vulnerabilities
|
|
19
|
+
const vulnerabilities = [];
|
|
20
|
+
|
|
21
|
+
if (auditData.vulnerabilities) {
|
|
22
|
+
Object.entries(auditData.vulnerabilities).forEach(([packageName, vuln]) => {
|
|
23
|
+
vulnerabilities.push({
|
|
24
|
+
package: packageName,
|
|
25
|
+
severity: vuln.severity, // critical, high, moderate, low
|
|
26
|
+
title: vuln.via[0]?.title || 'Security vulnerability',
|
|
27
|
+
range: vuln.range,
|
|
28
|
+
fixAvailable: vuln.fixAvailable,
|
|
29
|
+
cve: vuln.via[0]?.cve || null,
|
|
30
|
+
url: vuln.via[0]?.url || null
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
vulnerabilities,
|
|
37
|
+
metadata: {
|
|
38
|
+
total: auditData.metadata?.vulnerabilities?.total || 0,
|
|
39
|
+
critical: auditData.metadata?.vulnerabilities?.critical || 0,
|
|
40
|
+
high: auditData.metadata?.vulnerabilities?.high || 0,
|
|
41
|
+
moderate: auditData.metadata?.vulnerabilities?.moderate || 0,
|
|
42
|
+
low: auditData.metadata?.vulnerabilities?.low || 0
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// npm audit returns non-zero exit code when vulnerabilities found
|
|
48
|
+
// Try to parse the error output
|
|
49
|
+
try {
|
|
50
|
+
const auditData = JSON.parse(error.stdout);
|
|
51
|
+
|
|
52
|
+
const vulnerabilities = [];
|
|
53
|
+
|
|
54
|
+
if (auditData.vulnerabilities) {
|
|
55
|
+
Object.entries(auditData.vulnerabilities).forEach(([packageName, vuln]) => {
|
|
56
|
+
vulnerabilities.push({
|
|
57
|
+
package: packageName,
|
|
58
|
+
severity: vuln.severity,
|
|
59
|
+
title: vuln.via[0]?.title || 'Security vulnerability',
|
|
60
|
+
range: vuln.range,
|
|
61
|
+
fixAvailable: vuln.fixAvailable,
|
|
62
|
+
cve: vuln.via[0]?.cve || null,
|
|
63
|
+
url: vuln.via[0]?.url || null
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
vulnerabilities,
|
|
70
|
+
metadata: {
|
|
71
|
+
total: auditData.metadata?.vulnerabilities?.total || 0,
|
|
72
|
+
critical: auditData.metadata?.vulnerabilities?.critical || 0,
|
|
73
|
+
high: auditData.metadata?.vulnerabilities?.high || 0,
|
|
74
|
+
moderate: auditData.metadata?.vulnerabilities?.moderate || 0,
|
|
75
|
+
low: auditData.metadata?.vulnerabilities?.low || 0
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
} catch (parseError) {
|
|
79
|
+
// If we can't parse, return empty
|
|
80
|
+
return {
|
|
81
|
+
vulnerabilities: [],
|
|
82
|
+
metadata: {
|
|
83
|
+
total: 0,
|
|
84
|
+
critical: 0,
|
|
85
|
+
high: 0,
|
|
86
|
+
moderate: 0,
|
|
87
|
+
low: 0
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Calculate security penalty for health score
|
|
96
|
+
*/
|
|
97
|
+
function calculateSecurityPenalty(metadata) {
|
|
98
|
+
let penalty = 0;
|
|
99
|
+
|
|
100
|
+
penalty += metadata.critical * 2.5; // Critical: -2.5 points each
|
|
101
|
+
penalty += metadata.high * 1.5; // High: -1.5 points each
|
|
102
|
+
penalty += metadata.moderate * 0.5; // Moderate: -0.5 points each
|
|
103
|
+
penalty += metadata.low * 0.2; // Low: -0.2 points each
|
|
104
|
+
|
|
105
|
+
return Math.min(penalty, 5.0); // Cap at 5 points
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
checkSecurity,
|
|
110
|
+
calculateSecurityPenalty
|
|
111
|
+
};
|
package/src/commands/analyze.js
CHANGED
|
@@ -8,6 +8,9 @@ const { findUnusedDeps } = require('../analyzers/unused-deps');
|
|
|
8
8
|
const { findOutdatedDeps } = require('../analyzers/outdated');
|
|
9
9
|
const { calculateScore } = require('../analyzers/scoring');
|
|
10
10
|
const { checkEcosystemAlerts } = require('../alerts');
|
|
11
|
+
const { checkSecurity, calculateSecurityPenalty } = require('../analyzers/security');
|
|
12
|
+
const { analyzeBundleSizes, findHeavyPackages } = require('../analyzers/bundle-size');
|
|
13
|
+
const { checkLicenses, findProblematicLicenses } = require('../analyzers/licenses');
|
|
11
14
|
const {
|
|
12
15
|
formatAlerts,
|
|
13
16
|
getSeverityDisplay,
|
|
@@ -34,15 +37,25 @@ async function analyze(options) {
|
|
|
34
37
|
// Handle output modes
|
|
35
38
|
const outputMode = options.json ? 'json' : (options.ci ? 'ci' : (options.silent ? 'silent' : 'normal'));
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
// Only show header for normal and CI modes
|
|
41
|
+
if (outputMode !== 'silent' && outputMode !== 'json') {
|
|
38
42
|
console.log('\n');
|
|
39
43
|
log(chalk.cyan.bold(`🔍 DevCompass v${packageJson.version}`) + ' - Analyzing your project...\n');
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
// Create spinner (disabled for json/silent modes to prevent EPIPE errors)
|
|
47
|
+
const spinner = (outputMode === 'json' || outputMode === 'silent')
|
|
48
|
+
? {
|
|
49
|
+
start: function() { return this; },
|
|
50
|
+
succeed: function() {},
|
|
51
|
+
fail: function(msg) { if (msg) console.error(msg); },
|
|
52
|
+
text: '',
|
|
53
|
+
set text(val) {}
|
|
54
|
+
}
|
|
55
|
+
: ora({
|
|
56
|
+
text: 'Loading project...',
|
|
57
|
+
color: 'cyan'
|
|
58
|
+
}).start();
|
|
46
59
|
|
|
47
60
|
try {
|
|
48
61
|
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
@@ -148,28 +161,95 @@ async function analyze(options) {
|
|
|
148
161
|
}
|
|
149
162
|
}
|
|
150
163
|
|
|
164
|
+
// Check security vulnerabilities (NEW)
|
|
165
|
+
spinner.text = 'Checking security vulnerabilities...';
|
|
166
|
+
let securityData = { vulnerabilities: [], metadata: { total: 0, critical: 0, high: 0, moderate: 0, low: 0 } };
|
|
167
|
+
|
|
168
|
+
if (config.cache) {
|
|
169
|
+
const cached = getCached(projectPath, 'security');
|
|
170
|
+
if (cached) securityData = cached;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (securityData.metadata.total === 0) {
|
|
174
|
+
try {
|
|
175
|
+
securityData = await checkSecurity(projectPath);
|
|
176
|
+
if (config.cache) {
|
|
177
|
+
setCache(projectPath, 'security', securityData);
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (outputMode !== 'silent') {
|
|
181
|
+
console.log(chalk.yellow('\n⚠️ Could not check security vulnerabilities'));
|
|
182
|
+
console.log(chalk.gray(` Error: ${error.message}\n`));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Analyze bundle sizes (NEW)
|
|
188
|
+
spinner.text = 'Analyzing bundle sizes...';
|
|
189
|
+
let bundleSizes = [];
|
|
190
|
+
|
|
191
|
+
if (config.cache) {
|
|
192
|
+
const cached = getCached(projectPath, 'bundleSizes');
|
|
193
|
+
if (cached) bundleSizes = cached;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (bundleSizes.length === 0) {
|
|
197
|
+
try {
|
|
198
|
+
bundleSizes = await analyzeBundleSizes(projectPath, dependencies);
|
|
199
|
+
if (config.cache && bundleSizes.length > 0) {
|
|
200
|
+
setCache(projectPath, 'bundleSizes', bundleSizes);
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
// Bundle size analysis is optional
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check licenses (NEW)
|
|
208
|
+
spinner.text = 'Checking licenses...';
|
|
209
|
+
let licenses = [];
|
|
210
|
+
|
|
211
|
+
if (config.cache) {
|
|
212
|
+
const cached = getCached(projectPath, 'licenses');
|
|
213
|
+
if (cached) licenses = cached;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (licenses.length === 0) {
|
|
217
|
+
try {
|
|
218
|
+
licenses = await checkLicenses(projectPath, dependencies);
|
|
219
|
+
if (config.cache && licenses.length > 0) {
|
|
220
|
+
setCache(projectPath, 'licenses', licenses);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
// License checking is optional
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Calculate score (UPDATED)
|
|
151
228
|
const alertPenalty = calculateAlertPenalty(alerts);
|
|
229
|
+
const securityPenalty = calculateSecurityPenalty(securityData.metadata);
|
|
230
|
+
|
|
152
231
|
const score = calculateScore(
|
|
153
232
|
totalDeps,
|
|
154
233
|
unusedDeps.length,
|
|
155
234
|
outdatedDeps.length,
|
|
156
235
|
alerts.length,
|
|
157
|
-
alertPenalty
|
|
236
|
+
alertPenalty,
|
|
237
|
+
securityPenalty
|
|
158
238
|
);
|
|
159
239
|
|
|
160
240
|
spinner.succeed(chalk.green(`Scanned ${totalDeps} dependencies in project`));
|
|
161
241
|
|
|
162
242
|
// Handle different output modes
|
|
163
243
|
if (outputMode === 'json') {
|
|
164
|
-
const jsonOutput = formatAsJson(alerts, unusedDeps, outdatedDeps, score, totalDeps);
|
|
244
|
+
const jsonOutput = formatAsJson(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses);
|
|
165
245
|
console.log(jsonOutput);
|
|
166
246
|
} else if (outputMode === 'ci') {
|
|
167
|
-
displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps);
|
|
247
|
+
displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses);
|
|
168
248
|
handleCiMode(score, config, alerts, unusedDeps);
|
|
169
249
|
} else if (outputMode === 'silent') {
|
|
170
250
|
// Silent mode - no output
|
|
171
251
|
} else {
|
|
172
|
-
displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps);
|
|
252
|
+
displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses);
|
|
173
253
|
}
|
|
174
254
|
|
|
175
255
|
} catch (error) {
|
|
@@ -182,10 +262,40 @@ async function analyze(options) {
|
|
|
182
262
|
}
|
|
183
263
|
}
|
|
184
264
|
|
|
185
|
-
function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
265
|
+
function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses) {
|
|
186
266
|
logDivider();
|
|
187
267
|
|
|
188
|
-
//
|
|
268
|
+
// SECURITY VULNERABILITIES (NEW SECTION)
|
|
269
|
+
if (securityData.metadata.total > 0) {
|
|
270
|
+
const criticalCount = securityData.metadata.critical;
|
|
271
|
+
const highCount = securityData.metadata.high;
|
|
272
|
+
const moderateCount = securityData.metadata.moderate;
|
|
273
|
+
const lowCount = securityData.metadata.low;
|
|
274
|
+
|
|
275
|
+
logSection('🔐 SECURITY VULNERABILITIES', securityData.metadata.total);
|
|
276
|
+
|
|
277
|
+
if (criticalCount > 0) {
|
|
278
|
+
log(chalk.red.bold(`\n 🔴 CRITICAL: ${criticalCount}`));
|
|
279
|
+
}
|
|
280
|
+
if (highCount > 0) {
|
|
281
|
+
log(chalk.red(` 🟠 HIGH: ${highCount}`));
|
|
282
|
+
}
|
|
283
|
+
if (moderateCount > 0) {
|
|
284
|
+
log(chalk.yellow(` 🟡 MODERATE: ${moderateCount}`));
|
|
285
|
+
}
|
|
286
|
+
if (lowCount > 0) {
|
|
287
|
+
log(chalk.gray(` ⚪ LOW: ${lowCount}`));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
log(chalk.cyan('\n Run') + chalk.bold(' npm audit fix ') + chalk.cyan('to fix vulnerabilities\n'));
|
|
291
|
+
} else {
|
|
292
|
+
logSection('✅ SECURITY VULNERABILITIES');
|
|
293
|
+
log(chalk.green(' No vulnerabilities detected!\n'));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
logDivider();
|
|
297
|
+
|
|
298
|
+
// ECOSYSTEM ALERTS
|
|
189
299
|
if (alerts.length > 0) {
|
|
190
300
|
logSection('🚨 ECOSYSTEM ALERTS', alerts.length);
|
|
191
301
|
|
|
@@ -266,6 +376,46 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
|
266
376
|
|
|
267
377
|
logDivider();
|
|
268
378
|
|
|
379
|
+
// BUNDLE SIZE (NEW SECTION)
|
|
380
|
+
const heavyPackages = findHeavyPackages(bundleSizes);
|
|
381
|
+
if (heavyPackages.length > 0) {
|
|
382
|
+
logSection('📦 HEAVY PACKAGES', heavyPackages.length);
|
|
383
|
+
|
|
384
|
+
log(chalk.gray(' Packages larger than 1MB:\n'));
|
|
385
|
+
|
|
386
|
+
heavyPackages.slice(0, 10).forEach(pkg => {
|
|
387
|
+
const nameCol = pkg.name.padEnd(25);
|
|
388
|
+
const size = pkg.size > 5120
|
|
389
|
+
? chalk.red(pkg.sizeFormatted)
|
|
390
|
+
: chalk.yellow(pkg.sizeFormatted);
|
|
391
|
+
|
|
392
|
+
log(` ${nameCol} ${size}`);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
log('');
|
|
396
|
+
logDivider();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// LICENSE WARNINGS (NEW SECTION - ALWAYS SHOW)
|
|
400
|
+
const problematicLicenses = findProblematicLicenses(licenses);
|
|
401
|
+
if (problematicLicenses.length > 0) {
|
|
402
|
+
logSection('⚖️ LICENSE WARNINGS', problematicLicenses.length);
|
|
403
|
+
|
|
404
|
+
problematicLicenses.forEach(pkg => {
|
|
405
|
+
const type = pkg.type === 'restrictive'
|
|
406
|
+
? chalk.red('Restrictive')
|
|
407
|
+
: chalk.yellow('Unknown');
|
|
408
|
+
log(` ${chalk.bold(pkg.package)} - ${type} (${pkg.license})`);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
log(chalk.gray('\n Note: Restrictive licenses may require legal review\n'));
|
|
412
|
+
} else {
|
|
413
|
+
logSection('✅ LICENSE COMPLIANCE');
|
|
414
|
+
log(chalk.green(' All licenses are permissive!\n'));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
logDivider();
|
|
418
|
+
|
|
269
419
|
// PROJECT HEALTH
|
|
270
420
|
logSection('📊 PROJECT HEALTH');
|
|
271
421
|
|
|
@@ -273,6 +423,10 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
|
273
423
|
log(` Overall Score: ${scoreColor(score.total + '/10')}`);
|
|
274
424
|
log(` Total Dependencies: ${chalk.cyan(totalDeps)}`);
|
|
275
425
|
|
|
426
|
+
if (securityData.metadata.total > 0) {
|
|
427
|
+
log(` Security Vulnerabilities: ${chalk.red(securityData.metadata.total)}`);
|
|
428
|
+
}
|
|
429
|
+
|
|
276
430
|
if (alerts.length > 0) {
|
|
277
431
|
log(` Ecosystem Alerts: ${chalk.red(alerts.length)}`);
|
|
278
432
|
}
|
|
@@ -283,16 +437,23 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
|
283
437
|
logDivider();
|
|
284
438
|
|
|
285
439
|
// QUICK WINS
|
|
286
|
-
displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps);
|
|
440
|
+
displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData);
|
|
287
441
|
}
|
|
288
442
|
|
|
289
|
-
function displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
443
|
+
function displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData) {
|
|
290
444
|
const hasCriticalAlerts = alerts.some(a => a.severity === 'critical' || a.severity === 'high');
|
|
445
|
+
const hasCriticalSecurity = securityData.metadata.critical > 0 || securityData.metadata.high > 0;
|
|
291
446
|
|
|
292
|
-
if (hasCriticalAlerts || unusedDeps.length > 0) {
|
|
447
|
+
if (hasCriticalSecurity || hasCriticalAlerts || unusedDeps.length > 0) {
|
|
293
448
|
logSection('💡 QUICK WINS');
|
|
294
449
|
|
|
295
|
-
// Fix
|
|
450
|
+
// Fix security vulnerabilities first
|
|
451
|
+
if (hasCriticalSecurity) {
|
|
452
|
+
log(' 🔐 Fix security vulnerabilities:\n');
|
|
453
|
+
log(chalk.cyan(` npm audit fix\n`));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Fix critical alerts
|
|
296
457
|
if (hasCriticalAlerts) {
|
|
297
458
|
const criticalAlerts = alerts.filter(a => a.severity === 'critical' || a.severity === 'high');
|
|
298
459
|
|
|
@@ -325,13 +486,18 @@ function displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
|
325
486
|
0,
|
|
326
487
|
outdatedDeps.length,
|
|
327
488
|
alerts.length - alerts.filter(a => a.severity === 'critical' || a.severity === 'high').length,
|
|
328
|
-
alertPenalty
|
|
489
|
+
alertPenalty,
|
|
490
|
+
0 // Assume security issues fixed
|
|
329
491
|
);
|
|
330
492
|
|
|
331
493
|
log(' Expected impact:');
|
|
332
494
|
|
|
495
|
+
if (hasCriticalSecurity) {
|
|
496
|
+
log(` ${chalk.green('✓')} Resolve security vulnerabilities`);
|
|
497
|
+
}
|
|
498
|
+
|
|
333
499
|
if (hasCriticalAlerts) {
|
|
334
|
-
log(` ${chalk.green('✓')} Resolve critical
|
|
500
|
+
log(` ${chalk.green('✓')} Resolve critical stability issues`);
|
|
335
501
|
}
|
|
336
502
|
|
|
337
503
|
if (unusedDeps.length > 0) {
|
|
@@ -3,16 +3,36 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Format analysis results as JSON
|
|
5
5
|
*/
|
|
6
|
-
function formatAsJson(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
6
|
+
function formatAsJson(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses) {
|
|
7
|
+
const problematicLicenses = licenses.filter(l => l.type === 'restrictive' || l.type === 'unknown');
|
|
8
|
+
const heavyPackages = bundleSizes.filter(p => p.size > 1024);
|
|
9
|
+
|
|
7
10
|
return JSON.stringify({
|
|
8
11
|
version: require('../../package.json').version,
|
|
9
12
|
timestamp: new Date().toISOString(),
|
|
10
13
|
summary: {
|
|
11
14
|
healthScore: score.total,
|
|
12
15
|
totalDependencies: totalDeps,
|
|
16
|
+
securityVulnerabilities: securityData.metadata.total,
|
|
13
17
|
ecosystemAlerts: alerts.length,
|
|
14
18
|
unusedDependencies: unusedDeps.length,
|
|
15
|
-
outdatedPackages: outdatedDeps.length
|
|
19
|
+
outdatedPackages: outdatedDeps.length,
|
|
20
|
+
heavyPackages: heavyPackages.length,
|
|
21
|
+
licenseWarnings: problematicLicenses.length
|
|
22
|
+
},
|
|
23
|
+
security: {
|
|
24
|
+
total: securityData.metadata.total,
|
|
25
|
+
critical: securityData.metadata.critical,
|
|
26
|
+
high: securityData.metadata.high,
|
|
27
|
+
moderate: securityData.metadata.moderate,
|
|
28
|
+
low: securityData.metadata.low,
|
|
29
|
+
vulnerabilities: securityData.vulnerabilities.map(v => ({
|
|
30
|
+
package: v.package,
|
|
31
|
+
severity: v.severity,
|
|
32
|
+
title: v.title,
|
|
33
|
+
cve: v.cve,
|
|
34
|
+
fixAvailable: v.fixAvailable
|
|
35
|
+
}))
|
|
16
36
|
},
|
|
17
37
|
ecosystemAlerts: alerts.map(alert => ({
|
|
18
38
|
package: alert.package,
|
|
@@ -33,10 +53,24 @@ function formatAsJson(alerts, unusedDeps, outdatedDeps, score, totalDeps) {
|
|
|
33
53
|
latest: dep.latest,
|
|
34
54
|
updateType: dep.versionsBehind
|
|
35
55
|
})),
|
|
56
|
+
bundleAnalysis: {
|
|
57
|
+
heavyPackages: heavyPackages.map(pkg => ({
|
|
58
|
+
name: pkg.name,
|
|
59
|
+
size: pkg.sizeFormatted
|
|
60
|
+
}))
|
|
61
|
+
},
|
|
62
|
+
licenses: {
|
|
63
|
+
warnings: problematicLicenses.map(pkg => ({
|
|
64
|
+
package: pkg.package,
|
|
65
|
+
license: pkg.license,
|
|
66
|
+
type: pkg.type
|
|
67
|
+
}))
|
|
68
|
+
},
|
|
36
69
|
scoreBreakdown: {
|
|
37
70
|
unusedPenalty: score.breakdown.unusedPenalty,
|
|
38
71
|
outdatedPenalty: score.breakdown.outdatedPenalty,
|
|
39
|
-
alertsPenalty: score.breakdown.alertsPenalty
|
|
72
|
+
alertsPenalty: score.breakdown.alertsPenalty,
|
|
73
|
+
securityPenalty: score.breakdown.securityPenalty
|
|
40
74
|
}
|
|
41
75
|
}, null, 2);
|
|
42
76
|
}
|