devcompass 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +248 -179
- package/package.json +6 -3
- 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
|
@@ -6,22 +6,24 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/devcompass)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
Analyze your JavaScript projects to find unused dependencies, outdated packages, **detect
|
|
9
|
+
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
10
|
|
|
11
|
+
> **NEW in v2.3:** Security scanning, bundle analysis & license checker! 🔐
|
|
11
12
|
> **NEW in v2.2:** CI/CD integration with JSON output & smart caching! 🚀
|
|
12
13
|
> **NEW in v2.1:** Auto-fix command! 🔧 Fix critical issues automatically!
|
|
13
14
|
> **NEW in v2.0:** Real-time ecosystem alerts for known issues! 🚨
|
|
14
15
|
|
|
15
16
|
## ✨ Features
|
|
16
17
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
18
|
+
- 🔐 **Security Scanning** (NEW in v2.3!) - npm audit integration with severity breakdown
|
|
19
|
+
- 📦 **Bundle Size Analysis** (NEW in v2.3!) - Identify heavy packages (> 1MB)
|
|
20
|
+
- ⚖️ **License Checker** (NEW in v2.3!) - Detect restrictive licenses (GPL, AGPL)
|
|
21
|
+
- 🚀 **CI/CD Integration** (v2.2) - JSON output, exit codes, and silent mode
|
|
22
|
+
- ⚡ **Smart Caching** (v2.2) - 70% faster on repeated runs
|
|
23
|
+
- 🎛️ **Advanced Filtering** (v2.2) - Control alerts by severity level
|
|
20
24
|
- 🔧 **Auto-Fix Command** (v2.1) - Fix issues automatically with one command
|
|
21
25
|
- 🚨 **Ecosystem Intelligence** (v2.0) - Detect known issues before they break production
|
|
22
26
|
- 🔍 **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
27
|
- 📊 **Project health score** - Get a 0-10 rating for your dependencies
|
|
26
28
|
- 🎨 **Beautiful terminal UI** - Colored output with severity indicators
|
|
27
29
|
- 🔧 **Framework-aware** - Handles React, Next.js, Angular, NestJS, PostCSS, Tailwind
|
|
@@ -63,7 +65,155 @@ devcompass analyze --ci
|
|
|
63
65
|
devcompass analyze --silent
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
##
|
|
68
|
+
## 🔐 NEW in v2.3: Security & Compliance Features
|
|
69
|
+
|
|
70
|
+
### Security Vulnerability Scanning
|
|
71
|
+
|
|
72
|
+
DevCompass now integrates with **npm audit** to detect security vulnerabilities automatically!
|
|
73
|
+
|
|
74
|
+
**Example Output:**
|
|
75
|
+
```
|
|
76
|
+
🔐 SECURITY VULNERABILITIES (12)
|
|
77
|
+
|
|
78
|
+
🔴 CRITICAL: 2
|
|
79
|
+
🟠 HIGH: 4
|
|
80
|
+
🟡 MODERATE: 5
|
|
81
|
+
⚪ LOW: 1
|
|
82
|
+
|
|
83
|
+
Run npm audit fix to fix vulnerabilities
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**How it works:**
|
|
87
|
+
1. Runs `npm audit` in the background
|
|
88
|
+
2. Parses vulnerability data
|
|
89
|
+
3. Shows severity breakdown
|
|
90
|
+
4. Impacts health score (-2.5 per critical issue)
|
|
91
|
+
5. Suggests fix commands
|
|
92
|
+
|
|
93
|
+
**Health Score Impact:**
|
|
94
|
+
- Critical: −2.5 points each
|
|
95
|
+
- High: −1.5 points each
|
|
96
|
+
- Moderate: −0.5 points each
|
|
97
|
+
- Low: −0.2 points each
|
|
98
|
+
|
|
99
|
+
### Bundle Size Analysis
|
|
100
|
+
|
|
101
|
+
Identify large dependencies that bloat your `node_modules`!
|
|
102
|
+
|
|
103
|
+
**Example Output:**
|
|
104
|
+
```
|
|
105
|
+
📦 HEAVY PACKAGES (3)
|
|
106
|
+
|
|
107
|
+
Packages larger than 1MB:
|
|
108
|
+
|
|
109
|
+
webpack 2.3 MB
|
|
110
|
+
typescript 8.1 MB
|
|
111
|
+
@tensorflow/tfjs 12.4 MB
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Perfect for:**
|
|
115
|
+
- Frontend developers optimizing bundle size
|
|
116
|
+
- Identifying unnecessary large dependencies
|
|
117
|
+
- Web performance optimization
|
|
118
|
+
- Docker image size reduction
|
|
119
|
+
|
|
120
|
+
### License Compliance Checker
|
|
121
|
+
|
|
122
|
+
Detect restrictive licenses that may require legal review!
|
|
123
|
+
|
|
124
|
+
**Example Output:**
|
|
125
|
+
```
|
|
126
|
+
⚖️ LICENSE WARNINGS (2)
|
|
127
|
+
|
|
128
|
+
sharp - Restrictive (LGPL-3.0)
|
|
129
|
+
custom-lib - Unknown (UNLICENSED)
|
|
130
|
+
|
|
131
|
+
Note: Restrictive licenses may require legal review
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**What gets flagged:**
|
|
135
|
+
- **Restrictive licenses:** GPL, AGPL, LGPL (may require source code disclosure)
|
|
136
|
+
- **Unknown licenses:** Packages without license information
|
|
137
|
+
- **Unlicensed packages:** Legal risk for commercial use
|
|
138
|
+
|
|
139
|
+
**Supported licenses:**
|
|
140
|
+
- ✅ **Safe:** MIT, Apache-2.0, BSD, ISC, CC0
|
|
141
|
+
- ⚠️ **Restrictive:** GPL, AGPL, LGPL
|
|
142
|
+
- ❓ **Unknown:** Missing or custom licenses
|
|
143
|
+
|
|
144
|
+
### Combined Analysis Example (v2.3)
|
|
145
|
+
|
|
146
|
+
**Full Output:**
|
|
147
|
+
```
|
|
148
|
+
🔍 DevCompass v2.3.0 - Analyzing your project...
|
|
149
|
+
✔ Scanned 25 dependencies in project
|
|
150
|
+
|
|
151
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
152
|
+
|
|
153
|
+
🔐 SECURITY VULNERABILITIES (5)
|
|
154
|
+
|
|
155
|
+
🔴 CRITICAL: 1
|
|
156
|
+
🟠 HIGH: 2
|
|
157
|
+
🟡 MODERATE: 2
|
|
158
|
+
|
|
159
|
+
Run npm audit fix to fix vulnerabilities
|
|
160
|
+
|
|
161
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
162
|
+
|
|
163
|
+
🚨 ECOSYSTEM ALERTS (1)
|
|
164
|
+
|
|
165
|
+
🟠 HIGH
|
|
166
|
+
axios@1.6.0
|
|
167
|
+
Issue: Memory leak in request interceptors
|
|
168
|
+
Fix: 1.6.2
|
|
169
|
+
|
|
170
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
171
|
+
|
|
172
|
+
📦 HEAVY PACKAGES (2)
|
|
173
|
+
|
|
174
|
+
Packages larger than 1MB:
|
|
175
|
+
|
|
176
|
+
typescript 8.1 MB
|
|
177
|
+
webpack 2.3 MB
|
|
178
|
+
|
|
179
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
180
|
+
|
|
181
|
+
⚖️ LICENSE WARNINGS (1)
|
|
182
|
+
|
|
183
|
+
sharp - Restrictive (LGPL-3.0)
|
|
184
|
+
|
|
185
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
186
|
+
|
|
187
|
+
📊 PROJECT HEALTH
|
|
188
|
+
|
|
189
|
+
Overall Score: 6.2/10
|
|
190
|
+
Total Dependencies: 25
|
|
191
|
+
Security Vulnerabilities: 5
|
|
192
|
+
Ecosystem Alerts: 1
|
|
193
|
+
Unused: 0
|
|
194
|
+
Outdated: 3
|
|
195
|
+
|
|
196
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
197
|
+
|
|
198
|
+
💡 QUICK WINS
|
|
199
|
+
|
|
200
|
+
🔐 Fix security vulnerabilities:
|
|
201
|
+
|
|
202
|
+
npm audit fix
|
|
203
|
+
|
|
204
|
+
🔴 Fix critical issues:
|
|
205
|
+
|
|
206
|
+
npm install axios@1.6.2
|
|
207
|
+
|
|
208
|
+
Expected impact:
|
|
209
|
+
✓ Resolve security vulnerabilities
|
|
210
|
+
✓ Resolve critical stability issues
|
|
211
|
+
✓ Improve health score → 8.7/10
|
|
212
|
+
|
|
213
|
+
💡 TIP: Run 'devcompass fix' to apply these fixes automatically!
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 🚀 CI/CD Integration (v2.2)
|
|
67
217
|
|
|
68
218
|
### JSON Output
|
|
69
219
|
Perfect for parsing in CI/CD pipelines:
|
|
@@ -71,26 +221,43 @@ Perfect for parsing in CI/CD pipelines:
|
|
|
71
221
|
devcompass analyze --json
|
|
72
222
|
```
|
|
73
223
|
|
|
74
|
-
**Output:**
|
|
224
|
+
**Output (v2.3):**
|
|
75
225
|
```json
|
|
76
226
|
{
|
|
77
|
-
"version": "2.
|
|
78
|
-
"timestamp": "2026-04-
|
|
227
|
+
"version": "2.3.0",
|
|
228
|
+
"timestamp": "2026-04-02T10:30:00.000Z",
|
|
79
229
|
"summary": {
|
|
80
|
-
"healthScore":
|
|
81
|
-
"totalDependencies":
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
230
|
+
"healthScore": 6.2,
|
|
231
|
+
"totalDependencies": 25,
|
|
232
|
+
"securityVulnerabilities": 5,
|
|
233
|
+
"ecosystemAlerts": 1,
|
|
234
|
+
"unusedDependencies": 0,
|
|
235
|
+
"outdatedPackages": 3,
|
|
236
|
+
"heavyPackages": 2,
|
|
237
|
+
"licenseWarnings": 1
|
|
238
|
+
},
|
|
239
|
+
"security": {
|
|
240
|
+
"total": 5,
|
|
241
|
+
"critical": 1,
|
|
242
|
+
"high": 2,
|
|
243
|
+
"moderate": 2,
|
|
244
|
+
"low": 0,
|
|
245
|
+
"vulnerabilities": [...]
|
|
246
|
+
},
|
|
247
|
+
"bundleAnalysis": {
|
|
248
|
+
"heavyPackages": [
|
|
249
|
+
{ "name": "typescript", "size": "8.1 MB" },
|
|
250
|
+
{ "name": "webpack", "size": "2.3 MB" }
|
|
251
|
+
]
|
|
252
|
+
},
|
|
253
|
+
"licenses": {
|
|
254
|
+
"warnings": [
|
|
255
|
+
{ "package": "sharp", "license": "LGPL-3.0", "type": "restrictive" }
|
|
256
|
+
]
|
|
85
257
|
},
|
|
86
258
|
"ecosystemAlerts": [...],
|
|
87
259
|
"unusedDependencies": [...],
|
|
88
|
-
"outdatedPackages": [...]
|
|
89
|
-
"scoreBreakdown": {
|
|
90
|
-
"unusedPenalty": 0.8,
|
|
91
|
-
"outdatedPenalty": 1.7,
|
|
92
|
-
"alertsPenalty": 3.5
|
|
93
|
-
}
|
|
260
|
+
"outdatedPackages": [...]
|
|
94
261
|
}
|
|
95
262
|
```
|
|
96
263
|
|
|
@@ -115,8 +282,8 @@ jobs:
|
|
|
115
282
|
steps:
|
|
116
283
|
- uses: actions/checkout@v3
|
|
117
284
|
- uses: actions/setup-node@v3
|
|
118
|
-
- run: npm install
|
|
119
|
-
- run: devcompass analyze --ci
|
|
285
|
+
- run: npm install
|
|
286
|
+
- run: npx devcompass analyze --ci
|
|
120
287
|
```
|
|
121
288
|
|
|
122
289
|
### Silent Mode
|
|
@@ -126,15 +293,23 @@ devcompass analyze --silent
|
|
|
126
293
|
echo $? # Check exit code
|
|
127
294
|
```
|
|
128
295
|
|
|
129
|
-
## ⚡
|
|
296
|
+
## ⚡ Smart Caching (v2.2)
|
|
130
297
|
|
|
131
|
-
DevCompass
|
|
298
|
+
DevCompass caches results to improve performance:
|
|
132
299
|
|
|
133
300
|
- **First run:** Normal speed (fetches all data)
|
|
134
301
|
- **Cached runs:** ~70% faster
|
|
135
302
|
- **Cache duration:** 1 hour
|
|
136
303
|
- **Cache file:** `.devcompass-cache.json` (auto-gitignored)
|
|
137
304
|
|
|
305
|
+
**What gets cached:**
|
|
306
|
+
- Security vulnerabilities
|
|
307
|
+
- Ecosystem alerts
|
|
308
|
+
- Unused dependencies
|
|
309
|
+
- Outdated packages
|
|
310
|
+
- Bundle sizes
|
|
311
|
+
- License information
|
|
312
|
+
|
|
138
313
|
**Disable caching:**
|
|
139
314
|
```json
|
|
140
315
|
// devcompass.config.json
|
|
@@ -143,7 +318,7 @@ DevCompass now caches results to improve performance:
|
|
|
143
318
|
}
|
|
144
319
|
```
|
|
145
320
|
|
|
146
|
-
## 🎛️
|
|
321
|
+
## 🎛️ Advanced Configuration (v2.2)
|
|
147
322
|
|
|
148
323
|
Create `devcompass.config.json` in your project root:
|
|
149
324
|
```json
|
|
@@ -174,32 +349,33 @@ Create `devcompass.config.json` in your project root:
|
|
|
174
349
|
|
|
175
350
|
### Example Configurations
|
|
176
351
|
|
|
177
|
-
**
|
|
352
|
+
**Security-focused (strict):**
|
|
178
353
|
```json
|
|
179
354
|
{
|
|
180
355
|
"minSeverity": "critical",
|
|
181
|
-
"minScore":
|
|
356
|
+
"minScore": 9
|
|
182
357
|
}
|
|
183
358
|
```
|
|
184
359
|
|
|
185
|
-
**
|
|
360
|
+
**Balanced (recommended):**
|
|
186
361
|
```json
|
|
187
362
|
{
|
|
188
|
-
"ignoreSeverity": ["low"]
|
|
363
|
+
"ignoreSeverity": ["low"],
|
|
364
|
+
"minScore": 7
|
|
189
365
|
}
|
|
190
366
|
```
|
|
191
367
|
|
|
192
|
-
**
|
|
368
|
+
**Relaxed (development):**
|
|
193
369
|
```json
|
|
194
370
|
{
|
|
195
|
-
"
|
|
196
|
-
"
|
|
371
|
+
"ignoreSeverity": ["low", "medium"],
|
|
372
|
+
"minScore": 5
|
|
197
373
|
}
|
|
198
374
|
```
|
|
199
375
|
|
|
200
|
-
## 🔧 Auto-Fix Command
|
|
376
|
+
## 🔧 Auto-Fix Command (v2.1)
|
|
201
377
|
|
|
202
|
-
DevCompass can
|
|
378
|
+
DevCompass can **automatically fix issues** in your project!
|
|
203
379
|
|
|
204
380
|
### What it does:
|
|
205
381
|
- 🔴 **Fixes critical security issues** - Upgrades packages with known vulnerabilities
|
|
@@ -220,67 +396,6 @@ devcompass fix -y
|
|
|
220
396
|
devcompass fix --path /path/to/project
|
|
221
397
|
```
|
|
222
398
|
|
|
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
399
|
### Safety Features
|
|
285
400
|
- ✅ Shows what will be changed before applying
|
|
286
401
|
- ✅ Requires confirmation (unless `--yes` flag used)
|
|
@@ -300,74 +415,7 @@ devcompass fix
|
|
|
300
415
|
devcompass analyze
|
|
301
416
|
```
|
|
302
417
|
|
|
303
|
-
##
|
|
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
|
-
## 🚨 Ecosystem Intelligence
|
|
418
|
+
## 🚨 Ecosystem Intelligence (v2.0)
|
|
371
419
|
|
|
372
420
|
DevCompass tracks **real-world issues** in popular packages and warns you before they break production!
|
|
373
421
|
|
|
@@ -420,11 +468,12 @@ DevCompass won't flag these as unused (they're typically used in config files):
|
|
|
420
468
|
- Shows current vs latest versions
|
|
421
469
|
- Indicates update type (major/minor/patch)
|
|
422
470
|
|
|
423
|
-
### Health Score (Enhanced in v2.
|
|
471
|
+
### Health Score (Enhanced in v2.3)
|
|
424
472
|
Calculated from 0-10 based on:
|
|
425
473
|
- Percentage of unused dependencies (−4 points per 100%)
|
|
426
474
|
- Percentage of outdated packages (−3 points per 100%)
|
|
427
475
|
- Ecosystem alerts by severity (−0.2 to −2.0 per issue)
|
|
476
|
+
- Security vulnerabilities by severity (−0.2 to −2.5 per issue)
|
|
428
477
|
- Higher score = healthier project
|
|
429
478
|
|
|
430
479
|
## ⚙️ Commands & Options
|
|
@@ -511,6 +560,21 @@ if [ $? -ne 0 ]; then
|
|
|
511
560
|
fi
|
|
512
561
|
```
|
|
513
562
|
|
|
563
|
+
### Security-Focused Workflow
|
|
564
|
+
```bash
|
|
565
|
+
# 1. Run security scan
|
|
566
|
+
devcompass analyze
|
|
567
|
+
|
|
568
|
+
# 2. Check for critical vulnerabilities
|
|
569
|
+
devcompass analyze --json | jq '.security.critical'
|
|
570
|
+
|
|
571
|
+
# 3. Auto-fix if possible
|
|
572
|
+
npm audit fix
|
|
573
|
+
|
|
574
|
+
# 4. Verify fixes
|
|
575
|
+
devcompass analyze
|
|
576
|
+
```
|
|
577
|
+
|
|
514
578
|
## ⚠️ Known Issues & Best Practices
|
|
515
579
|
|
|
516
580
|
### Installation
|
|
@@ -543,12 +607,14 @@ If you encounter a false positive, please [report it](https://github.com/AjayBTh
|
|
|
543
607
|
|
|
544
608
|
1. **Run regularly** - Add to your CI/CD pipeline or git hooks
|
|
545
609
|
2. **Use fix command** - Let DevCompass handle routine maintenance
|
|
546
|
-
3. **
|
|
547
|
-
4. **
|
|
548
|
-
5. **
|
|
549
|
-
6. **
|
|
550
|
-
7. **
|
|
551
|
-
8. **
|
|
610
|
+
3. **Check security first** - Prioritize fixing critical vulnerabilities
|
|
611
|
+
4. **Monitor bundle size** - Keep an eye on heavy packages
|
|
612
|
+
5. **Review licenses** - Ensure compliance with your legal requirements
|
|
613
|
+
6. **Configure severity levels** - Filter out noise with `minSeverity`
|
|
614
|
+
7. **Enable CI mode** - Catch issues before they reach production
|
|
615
|
+
8. **Use JSON output** - Integrate with your monitoring tools
|
|
616
|
+
9. **Review major updates** - Always check changelogs before major version bumps
|
|
617
|
+
10. **Verify before uninstalling** - DevCompass helps identify candidates, but always verify
|
|
552
618
|
|
|
553
619
|
## 🤝 Contributing
|
|
554
620
|
|
|
@@ -632,18 +698,21 @@ Check out DevCompass stats:
|
|
|
632
698
|
|
|
633
699
|
## 🌟 What's Next?
|
|
634
700
|
|
|
635
|
-
### Roadmap (v2.
|
|
701
|
+
### Roadmap (v2.4+)
|
|
636
702
|
- [x] ~~Automatic fix command~~ ✅ **Added in v2.1!**
|
|
637
703
|
- [x] ~~CI/CD integration with JSON output~~ ✅ **Added in v2.2!**
|
|
638
704
|
- [x] ~~Smart caching system~~ ✅ **Added in v2.2!**
|
|
639
705
|
- [x] ~~Custom ignore rules via config file~~ ✅ **Added in v2.2!**
|
|
640
|
-
- [
|
|
641
|
-
- [
|
|
642
|
-
- [
|
|
643
|
-
- [ ]
|
|
644
|
-
- [ ]
|
|
645
|
-
- [ ]
|
|
646
|
-
- [ ]
|
|
706
|
+
- [x] ~~npm audit integration~~ ✅ **Added in v2.3!**
|
|
707
|
+
- [x] ~~Bundle size analysis~~ ✅ **Added in v2.3!**
|
|
708
|
+
- [x] ~~License compliance checker~~ ✅ **Added in v2.3!**
|
|
709
|
+
- [ ] GitHub Issues API for real-time issue tracking (v2.4.0)
|
|
710
|
+
- [ ] Automated security patch suggestions (v2.4.0)
|
|
711
|
+
- [ ] Dependency graph visualization (v2.5.0)
|
|
712
|
+
- [ ] Web dashboard for team health monitoring (v2.5.0)
|
|
713
|
+
- [ ] More tracked packages (React, Next.js, Vue, Angular) (v2.5.0)
|
|
714
|
+
- [ ] Team collaboration features (v2.6.0)
|
|
715
|
+
- [ ] Slack/Discord notifications (v2.6.0)
|
|
647
716
|
|
|
648
717
|
Want to contribute? Pick an item and open an issue! 🚀
|
|
649
718
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devcompass",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
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",
|
|
@@ -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
|
}
|