@cyberhub/shieldpm 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +239 -0
  3. package/dist/analyzer/static.d.ts +35 -0
  4. package/dist/analyzer/static.d.ts.map +1 -0
  5. package/dist/analyzer/static.js +416 -0
  6. package/dist/analyzer/static.js.map +1 -0
  7. package/dist/analyzer/typosquat.d.ts +30 -0
  8. package/dist/analyzer/typosquat.d.ts.map +1 -0
  9. package/dist/analyzer/typosquat.js +211 -0
  10. package/dist/analyzer/typosquat.js.map +1 -0
  11. package/dist/cli.d.ts +10 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +621 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/diff/dependency.d.ts +51 -0
  16. package/dist/diff/dependency.d.ts.map +1 -0
  17. package/dist/diff/dependency.js +222 -0
  18. package/dist/diff/dependency.js.map +1 -0
  19. package/dist/fingerprint/profile.d.ts +68 -0
  20. package/dist/fingerprint/profile.d.ts.map +1 -0
  21. package/dist/fingerprint/profile.js +233 -0
  22. package/dist/fingerprint/profile.js.map +1 -0
  23. package/dist/index.d.ts +21 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +22 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/monitor/permissions.d.ts +45 -0
  28. package/dist/monitor/permissions.d.ts.map +1 -0
  29. package/dist/monitor/permissions.js +265 -0
  30. package/dist/monitor/permissions.js.map +1 -0
  31. package/dist/sandbox/runner.d.ts +46 -0
  32. package/dist/sandbox/runner.d.ts.map +1 -0
  33. package/dist/sandbox/runner.js +216 -0
  34. package/dist/sandbox/runner.js.map +1 -0
  35. package/dist/utils/colors.d.ts +31 -0
  36. package/dist/utils/colors.d.ts.map +1 -0
  37. package/dist/utils/colors.js +54 -0
  38. package/dist/utils/colors.js.map +1 -0
  39. package/dist/utils/logger.d.ts +26 -0
  40. package/dist/utils/logger.d.ts.map +1 -0
  41. package/dist/utils/logger.js +77 -0
  42. package/dist/utils/logger.js.map +1 -0
  43. package/package.json +24 -0
  44. package/src/analyzer/static.ts +483 -0
  45. package/src/analyzer/typosquat.ts +272 -0
  46. package/src/cli.ts +700 -0
  47. package/src/diff/dependency.ts +297 -0
  48. package/src/fingerprint/profile.ts +333 -0
  49. package/src/index.ts +34 -0
  50. package/src/monitor/permissions.ts +330 -0
  51. package/src/sandbox/runner.ts +302 -0
  52. package/src/utils/colors.ts +58 -0
  53. package/src/utils/logger.ts +87 -0
  54. package/tsconfig.json +19 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typosquat.js","sourceRoot":"","sources":["../../src/analyzer/typosquat.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ShieldPM — CLI Entry Point
4
+ * Runtime-aware package firewall for Node.js
5
+ *
6
+ * Usage:
7
+ * shieldpm <command> [options]
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
package/dist/cli.js ADDED
@@ -0,0 +1,621 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ShieldPM — CLI Entry Point
4
+ * Runtime-aware package firewall for Node.js
5
+ *
6
+ * Usage:
7
+ * shieldpm <command> [options]
8
+ */
9
+ import { readFile } from 'node:fs/promises';
10
+ import { resolve, join } from 'node:path';
11
+ import { execSync } from 'node:child_process';
12
+ import { existsSync } from 'node:fs';
13
+ import { bold, cyan, red, green, yellow, dim, boldRed, boldGreen, boldYellow, boldCyan } from './utils/colors.js';
14
+ import log from './utils/logger.js';
15
+ import { analyzePackage } from './analyzer/static.js';
16
+ import { checkTyposquatting } from './analyzer/typosquat.js';
17
+ import { runSandboxed } from './sandbox/runner.js';
18
+ import { loadManifest, saveManifest, generateManifest } from './monitor/permissions.js';
19
+ import { generateProfile, saveProfile } from './fingerprint/profile.js';
20
+ import { diffLockfiles } from './diff/dependency.js';
21
+ // ── Version ──────────────────────────────────────────────────────────────
22
+ const VERSION = '0.1.0';
23
+ // ── ASCII banner ─────────────────────────────────────────────────────────
24
+ function printBanner() {
25
+ console.log(boldCyan(`
26
+ ███████╗██╗ ██╗██╗███████╗██╗ ██████╗ ██████╗ ███╗ ███╗
27
+ ██╔════╝██║ ██║██║██╔════╝██║ ██╔══██╗██╔══██╗████╗ ████║
28
+ ███████╗███████║██║█████╗ ██║ ██║ ██║██████╔╝██╔████╔██║
29
+ ╚════██║██╔══██║██║██╔══╝ ██║ ██║ ██║██╔═══╝ ██║╚██╔╝██║
30
+ ███████║██║ ██║██║███████╗███████╗██████╔╝██║ ██║ ╚═╝ ██║
31
+ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝
32
+ `));
33
+ console.log(dim(' Runtime-aware package firewall for Node.js'));
34
+ console.log();
35
+ }
36
+ // ── Help text ────────────────────────────────────────────────────────────
37
+ function printHelp() {
38
+ printBanner();
39
+ console.log(bold(' USAGE'));
40
+ console.log(` ${cyan('shieldpm')} ${dim('<command>')} ${dim('[options]')}`);
41
+ console.log();
42
+ console.log(bold(' COMMANDS'));
43
+ const commands = [
44
+ ['install <package>', 'Install a package with protection checks'],
45
+ ['audit', 'Audit current project dependencies'],
46
+ ['audit --deep', 'Deep behavioral analysis of all dependencies'],
47
+ ['inspect <package>', 'Show what a package does (static analysis)'],
48
+ ['sandbox <command>', 'Run a command in a sandboxed environment'],
49
+ ['manifest generate', 'Auto-generate permission manifest'],
50
+ ['manifest enforce', 'Enforce permission manifest at runtime'],
51
+ ['diff', 'Show dependency changes since last lock'],
52
+ ['help', 'Show this help message'],
53
+ ['version', 'Show version'],
54
+ ];
55
+ const maxCmd = Math.max(...commands.map(([c]) => c.length));
56
+ for (const [cmd, desc] of commands) {
57
+ console.log(` ${green(cmd.padEnd(maxCmd + 2))} ${dim(desc)}`);
58
+ }
59
+ console.log();
60
+ console.log(bold(' OPTIONS'));
61
+ console.log(` ${green('--verbose')} ${dim('Enable debug logging')}`);
62
+ console.log(` ${green('--no-color')} ${dim('Disable colored output')}`);
63
+ console.log(` ${green('--json')} ${dim('Output results as JSON')}`);
64
+ console.log();
65
+ console.log(dim(' https://github.com/nrupaks/shieldpm'));
66
+ console.log();
67
+ }
68
+ // ── Formatters ───────────────────────────────────────────────────────────
69
+ function severityColor(severity) {
70
+ switch (severity) {
71
+ case 'critical': return boldRed;
72
+ case 'high': return red;
73
+ case 'medium': return yellow;
74
+ case 'low': return dim;
75
+ default: return dim;
76
+ }
77
+ }
78
+ function scoreBar(score) {
79
+ const filled = Math.round(score);
80
+ const empty = 10 - filled;
81
+ const color = score >= 7 ? red : score >= 4 ? yellow : green;
82
+ return color('\u2588'.repeat(filled)) + dim('\u2591'.repeat(empty)) + ` ${score}/10`;
83
+ }
84
+ function printFindings(findings, limit = 20) {
85
+ const shown = findings.slice(0, limit);
86
+ for (const f of shown) {
87
+ const sev = severityColor(f.severity)(f.severity.toUpperCase().padEnd(8));
88
+ console.log(` ${sev} ${dim(f.file + ':' + f.line)} ${f.message}`);
89
+ if (f.snippet) {
90
+ console.log(` ${dim(f.snippet)}`);
91
+ }
92
+ }
93
+ if (findings.length > limit) {
94
+ console.log(dim(` ... and ${findings.length - limit} more findings`));
95
+ }
96
+ }
97
+ function printRiskReport(report, packageName) {
98
+ const label = packageName ? ` for ${bold(packageName)}` : '';
99
+ log.header(`Risk Report${label}`);
100
+ console.log();
101
+ console.log(` ${bold('Score:')} ${scoreBar(report.score)}`);
102
+ console.log(` ${bold('Files:')} ${report.filesScanned} scanned`);
103
+ console.log(` ${bold('Result:')} ${report.summary}`);
104
+ console.log();
105
+ if (Object.keys(report.categoryCounts).length > 0) {
106
+ console.log(bold(' Category Breakdown:'));
107
+ for (const [cat, count] of Object.entries(report.categoryCounts)) {
108
+ console.log(` ${dim(cat.padEnd(24))} ${count} finding${count !== 1 ? 's' : ''}`);
109
+ }
110
+ console.log();
111
+ }
112
+ if (report.findings.length > 0) {
113
+ console.log(bold(' Findings:'));
114
+ printFindings(report.findings);
115
+ console.log();
116
+ }
117
+ }
118
+ // ── Command handlers ─────────────────────────────────────────────────────
119
+ async function cmdInstall(packageName, flags) {
120
+ log.header(`Installing ${packageName} with protection`);
121
+ console.log();
122
+ // Step 1: Typosquatting check
123
+ log.info('Checking for typosquatting...');
124
+ const typoResult = checkTyposquatting(packageName);
125
+ if (typoResult) {
126
+ console.log();
127
+ console.log(boldRed(' !! TYPOSQUATTING WARNING !!'));
128
+ console.log(red(` "${packageName}" looks suspicious:`));
129
+ console.log(yellow(` Similar to: ${bold(typoResult.similarTo)}`));
130
+ console.log(yellow(` Method: ${typoResult.method} (distance: ${typoResult.distance})`));
131
+ console.log(yellow(` ${typoResult.reason}`));
132
+ console.log();
133
+ console.log(red(' Installation blocked. If this is intentional, use --force.'));
134
+ if (!flags.has('--force')) {
135
+ process.exitCode = 1;
136
+ return;
137
+ }
138
+ console.log(yellow(' --force flag set, continuing anyway...'));
139
+ console.log();
140
+ }
141
+ else {
142
+ log.success('No typosquatting detected');
143
+ }
144
+ // Step 2: Install the package
145
+ log.info(`Running npm install ${packageName}...`);
146
+ try {
147
+ execSync(`npm install --ignore-scripts ${packageName}`, {
148
+ stdio: 'inherit',
149
+ cwd: process.cwd(),
150
+ });
151
+ }
152
+ catch {
153
+ log.error('npm install failed');
154
+ process.exitCode = 1;
155
+ return;
156
+ }
157
+ // Step 3: Static analysis
158
+ const pkgDir = resolve('node_modules', packageName);
159
+ if (existsSync(pkgDir)) {
160
+ log.info('Running static analysis...');
161
+ const report = await analyzePackage(pkgDir);
162
+ printRiskReport(report, packageName);
163
+ if (report.score >= 7) {
164
+ log.error('Package has critical risk score. Review findings above.');
165
+ console.log(yellow(' To keep the package, add it to your shieldpm.json manifest.'));
166
+ }
167
+ else if (report.score >= 4) {
168
+ log.warn('Package has elevated risk. Review findings above.');
169
+ }
170
+ else {
171
+ log.success('Package appears safe.');
172
+ }
173
+ // Step 4: Generate profile
174
+ try {
175
+ const pkgJson = JSON.parse(await readFile(join(pkgDir, 'package.json'), 'utf-8'));
176
+ const profile = await generateProfile(pkgDir, packageName, pkgJson.version ?? '0.0.0');
177
+ const profilePath = await saveProfile(process.cwd(), profile);
178
+ log.info(`Behavioral profile saved: ${dim(profilePath)}`);
179
+ }
180
+ catch {
181
+ log.warn('Could not generate behavioral profile');
182
+ }
183
+ // Step 5: Run postinstall in sandbox if present
184
+ try {
185
+ const pkgJson = JSON.parse(await readFile(join(pkgDir, 'package.json'), 'utf-8'));
186
+ const postinstall = pkgJson.scripts?.postinstall || pkgJson.scripts?.install;
187
+ if (postinstall) {
188
+ log.info('Running postinstall script in sandbox...');
189
+ const result = await runSandboxed('sh', ['-c', postinstall], {
190
+ cwd: pkgDir,
191
+ timeout: 30_000,
192
+ blockNetwork: true,
193
+ blockEnv: true,
194
+ });
195
+ if (result.warnings.length > 0) {
196
+ for (const w of result.warnings)
197
+ log.warn(w);
198
+ }
199
+ if (result.blocked.length > 0) {
200
+ for (const b of result.blocked)
201
+ log.info(`Blocked: ${b}`);
202
+ }
203
+ if (result.exitCode === 0) {
204
+ log.success('Postinstall completed in sandbox');
205
+ }
206
+ else {
207
+ log.warn(`Postinstall exited with code ${result.exitCode}`);
208
+ }
209
+ }
210
+ }
211
+ catch {
212
+ // No postinstall — fine
213
+ }
214
+ }
215
+ console.log();
216
+ }
217
+ async function cmdAudit(deep, flags) {
218
+ log.header(deep ? 'Deep Dependency Audit' : 'Dependency Audit');
219
+ console.log();
220
+ const nodeModules = resolve('node_modules');
221
+ if (!existsSync(nodeModules)) {
222
+ log.error('No node_modules found. Run npm install first.');
223
+ process.exitCode = 1;
224
+ return;
225
+ }
226
+ // Read top-level dependencies from package.json
227
+ let deps = [];
228
+ try {
229
+ const pkgJson = JSON.parse(await readFile(resolve('package.json'), 'utf-8'));
230
+ deps = [
231
+ ...Object.keys(pkgJson.dependencies ?? {}),
232
+ ...Object.keys(pkgJson.devDependencies ?? {}),
233
+ ];
234
+ }
235
+ catch {
236
+ log.error('Cannot read package.json');
237
+ process.exitCode = 1;
238
+ return;
239
+ }
240
+ if (deps.length === 0) {
241
+ log.info('No dependencies found.');
242
+ return;
243
+ }
244
+ log.info(`Scanning ${deps.length} dependencies...`);
245
+ console.log();
246
+ let totalScore = 0;
247
+ let maxScore = 0;
248
+ let maxPkg = '';
249
+ const highRisk = [];
250
+ for (const dep of deps) {
251
+ const pkgDir = resolve('node_modules', dep);
252
+ if (!existsSync(pkgDir)) {
253
+ log.warn(`${dep}: not found in node_modules`);
254
+ continue;
255
+ }
256
+ const report = await analyzePackage(pkgDir);
257
+ totalScore += report.score;
258
+ if (report.score > maxScore) {
259
+ maxScore = report.score;
260
+ maxPkg = dep;
261
+ }
262
+ const scoreStr = report.score >= 7
263
+ ? boldRed(report.score.toFixed(1))
264
+ : report.score >= 4
265
+ ? boldYellow(report.score.toFixed(1))
266
+ : boldGreen(report.score.toFixed(1));
267
+ console.log(` ${scoreStr.padStart(18)} ${dep} ${dim(`(${report.findings.length} findings)`)}`);
268
+ if (report.score >= 4) {
269
+ highRisk.push({ name: dep, score: report.score });
270
+ }
271
+ if (deep && report.findings.length > 0) {
272
+ printFindings(report.findings, 5);
273
+ console.log();
274
+ }
275
+ // Typosquatting check
276
+ const typo = checkTyposquatting(dep);
277
+ if (typo) {
278
+ console.log(yellow(` Typosquatting alert: similar to "${typo.similarTo}" (${typo.method})`));
279
+ }
280
+ }
281
+ console.log();
282
+ console.log(bold(' Summary:'));
283
+ console.log(` Packages scanned: ${deps.length}`);
284
+ console.log(` Average risk score: ${(totalScore / deps.length).toFixed(1)}/10`);
285
+ console.log(` Highest risk: ${maxPkg} (${maxScore.toFixed(1)}/10)`);
286
+ if (highRisk.length > 0) {
287
+ console.log();
288
+ console.log(boldYellow(` ${highRisk.length} package(s) need attention:`));
289
+ for (const pkg of highRisk) {
290
+ console.log(yellow(` - ${pkg.name} (${pkg.score.toFixed(1)}/10)`));
291
+ }
292
+ }
293
+ console.log();
294
+ if (flags.has('--json')) {
295
+ // Could output machine-readable results; for now just note it
296
+ log.info('JSON output: use --json with programmatic API for structured data');
297
+ }
298
+ }
299
+ async function cmdInspect(packageName) {
300
+ // Try node_modules first, then treat as a directory
301
+ let targetDir = resolve('node_modules', packageName);
302
+ if (!existsSync(targetDir)) {
303
+ targetDir = resolve(packageName);
304
+ }
305
+ if (!existsSync(targetDir)) {
306
+ log.error(`Package "${packageName}" not found in node_modules or as a path.`);
307
+ log.info('Install it first: shieldpm install ' + packageName);
308
+ process.exitCode = 1;
309
+ return;
310
+ }
311
+ const report = await analyzePackage(targetDir);
312
+ printRiskReport(report, packageName);
313
+ // Also show imports and profile
314
+ try {
315
+ const pkgJson = JSON.parse(await readFile(join(targetDir, 'package.json'), 'utf-8'));
316
+ const version = pkgJson.version ?? 'unknown';
317
+ const profile = await generateProfile(targetDir, packageName, version);
318
+ if (profile.imports.length > 0) {
319
+ console.log(bold(' Imports:'));
320
+ for (const imp of profile.imports.slice(0, 30)) {
321
+ const isBuiltin = !imp.startsWith('.') && !imp.startsWith('/');
322
+ console.log(` ${isBuiltin ? cyan(imp) : dim(imp)}`);
323
+ }
324
+ if (profile.imports.length > 30) {
325
+ console.log(dim(` ... and ${profile.imports.length - 30} more`));
326
+ }
327
+ console.log();
328
+ }
329
+ if (profile.networkEndpoints.length > 0) {
330
+ console.log(boldYellow(' Network Endpoints:'));
331
+ for (const ep of profile.networkEndpoints) {
332
+ console.log(` ${yellow(ep)}`);
333
+ }
334
+ console.log();
335
+ }
336
+ if (profile.nativeBindings.length > 0) {
337
+ console.log(bold(' Native Bindings:'));
338
+ for (const nb of profile.nativeBindings) {
339
+ console.log(` ${red(nb)}`);
340
+ }
341
+ console.log();
342
+ }
343
+ }
344
+ catch {
345
+ // Not a valid package directory
346
+ }
347
+ }
348
+ async function cmdSandbox(command) {
349
+ if (command.length === 0) {
350
+ log.error('No command specified. Usage: shieldpm sandbox <command>');
351
+ process.exitCode = 1;
352
+ return;
353
+ }
354
+ const fullCommand = command.join(' ');
355
+ log.header(`Sandboxing: ${fullCommand}`);
356
+ console.log();
357
+ log.info('Environment: network blocked, env stripped, 30s timeout');
358
+ console.log();
359
+ const result = await runSandboxed(command[0], command.slice(1), {
360
+ timeout: 30_000,
361
+ blockNetwork: true,
362
+ blockEnv: true,
363
+ });
364
+ if (result.stdout) {
365
+ console.log(dim(' --- stdout ---'));
366
+ console.log(result.stdout);
367
+ }
368
+ if (result.stderr) {
369
+ console.log(dim(' --- stderr ---'));
370
+ console.log(result.stderr);
371
+ }
372
+ console.log();
373
+ if (result.blocked.length > 0) {
374
+ console.log(bold(' Blocked:'));
375
+ for (const b of result.blocked) {
376
+ console.log(` ${red(b)}`);
377
+ }
378
+ }
379
+ if (result.warnings.length > 0) {
380
+ console.log(bold(' Warnings:'));
381
+ for (const w of result.warnings) {
382
+ console.log(` ${yellow(w)}`);
383
+ }
384
+ }
385
+ console.log();
386
+ console.log(` Exit code: ${result.exitCode ?? 'killed'}`);
387
+ console.log(` Duration: ${result.durationMs}ms`);
388
+ if (result.timedOut) {
389
+ console.log(boldRed(' TIMED OUT'));
390
+ }
391
+ console.log();
392
+ process.exitCode = result.exitCode ?? 1;
393
+ }
394
+ async function cmdManifestGenerate() {
395
+ log.header('Generating Permission Manifest');
396
+ console.log();
397
+ const projectDir = process.cwd();
398
+ log.info('Scanning dependencies...');
399
+ const manifest = await generateManifest(projectDir);
400
+ const pkgCount = Object.keys(manifest.permissions).length;
401
+ if (pkgCount === 0) {
402
+ log.warn('No packages found in node_modules.');
403
+ return;
404
+ }
405
+ const path = await saveManifest(manifest, projectDir);
406
+ log.success(`Generated manifest for ${pkgCount} packages`);
407
+ log.info(`Saved to: ${dim(path)}`);
408
+ console.log();
409
+ console.log(dim(' Review the manifest and fill in allowed destinations/paths.'));
410
+ console.log(dim(' Then run: shieldpm manifest enforce'));
411
+ console.log();
412
+ }
413
+ async function cmdManifestEnforce() {
414
+ log.header('Enforcing Permission Manifest');
415
+ console.log();
416
+ const manifest = await loadManifest();
417
+ if (!manifest) {
418
+ log.error('No shieldpm.json found. Run: shieldpm manifest generate');
419
+ process.exitCode = 1;
420
+ return;
421
+ }
422
+ const pkgCount = Object.keys(manifest.permissions).length;
423
+ log.info(`Loaded manifest with ${pkgCount} package rules`);
424
+ console.log();
425
+ // Validate that all installed deps have manifest entries
426
+ try {
427
+ const pkgJson = JSON.parse(await readFile(resolve('package.json'), 'utf-8'));
428
+ const deps = [
429
+ ...Object.keys(pkgJson.dependencies ?? {}),
430
+ ...Object.keys(pkgJson.devDependencies ?? {}),
431
+ ];
432
+ let missing = 0;
433
+ let covered = 0;
434
+ for (const dep of deps) {
435
+ if (manifest.permissions[dep]) {
436
+ covered++;
437
+ const perms = manifest.permissions[dep];
438
+ const restrictions = [];
439
+ if (perms.net === false)
440
+ restrictions.push('net:blocked');
441
+ else if (Array.isArray(perms.net))
442
+ restrictions.push(`net:${perms.net.length} rules`);
443
+ if (perms.fs === false)
444
+ restrictions.push('fs:blocked');
445
+ else if (Array.isArray(perms.fs))
446
+ restrictions.push(`fs:${perms.fs.length} paths`);
447
+ if (perms.native)
448
+ restrictions.push('native:yes');
449
+ if (perms.exec)
450
+ restrictions.push('exec:yes');
451
+ console.log(` ${green('\u2713')} ${dep} ${dim(`[${restrictions.join(', ')}]`)}`);
452
+ }
453
+ else {
454
+ missing++;
455
+ console.log(` ${red('\u2717')} ${dep} ${red('(no manifest entry — will be fully restricted)')}`);
456
+ }
457
+ }
458
+ console.log();
459
+ console.log(` Covered: ${covered}/${deps.length}`);
460
+ if (missing > 0) {
461
+ log.warn(`${missing} packages have no manifest entry and will be fully restricted.`);
462
+ log.info('Run: shieldpm manifest generate');
463
+ }
464
+ else {
465
+ log.success('All dependencies have manifest entries.');
466
+ }
467
+ }
468
+ catch {
469
+ log.error('Cannot read package.json');
470
+ process.exitCode = 1;
471
+ }
472
+ console.log();
473
+ }
474
+ async function cmdDiff() {
475
+ log.header('Dependency Diff');
476
+ console.log();
477
+ // Look for git-tracked package-lock.json
478
+ const lockPath = resolve('package-lock.json');
479
+ if (!existsSync(lockPath)) {
480
+ log.error('No package-lock.json found.');
481
+ process.exitCode = 1;
482
+ return;
483
+ }
484
+ // Get the last committed version
485
+ let oldLock;
486
+ try {
487
+ oldLock = execSync('git show HEAD:package-lock.json', {
488
+ encoding: 'utf-8',
489
+ stdio: ['pipe', 'pipe', 'pipe'],
490
+ });
491
+ }
492
+ catch {
493
+ log.error('Could not get previous package-lock.json from git.');
494
+ log.info('This command requires a git repository with a committed package-lock.json.');
495
+ process.exitCode = 1;
496
+ return;
497
+ }
498
+ const newLock = await readFile(lockPath, 'utf-8');
499
+ const report = diffLockfiles(oldLock, newLock);
500
+ console.log(` ${dim('Old packages:')} ${report.oldPackageCount}`);
501
+ console.log(` ${dim('New packages:')} ${report.newPackageCount}`);
502
+ console.log();
503
+ if (report.added.length > 0) {
504
+ console.log(bold(green(` + ${report.added.length} added:`)));
505
+ for (const pkg of report.added) {
506
+ console.log(` ${green('+')} ${pkg.name} ${dim(pkg.newVersion ?? '')}`);
507
+ for (const flag of pkg.flags) {
508
+ console.log(` ${yellow('!')} ${flag.message}`);
509
+ }
510
+ }
511
+ console.log();
512
+ }
513
+ if (report.removed.length > 0) {
514
+ console.log(bold(red(` - ${report.removed.length} removed:`)));
515
+ for (const pkg of report.removed) {
516
+ console.log(` ${red('-')} ${pkg.name} ${dim(pkg.oldVersion ?? '')}`);
517
+ }
518
+ console.log();
519
+ }
520
+ if (report.changed.length > 0) {
521
+ console.log(bold(yellow(` ~ ${report.changed.length} changed:`)));
522
+ for (const pkg of report.changed) {
523
+ console.log(` ${yellow('~')} ${pkg.name} ${dim((pkg.oldVersion ?? '') + ' -> ' + (pkg.newVersion ?? ''))}`);
524
+ for (const flag of pkg.flags) {
525
+ console.log(` ${yellow('!')} ${flag.message}`);
526
+ }
527
+ }
528
+ console.log();
529
+ }
530
+ if (report.flags.length > 0) {
531
+ console.log(boldYellow(` ${report.flags.length} flag(s) to review`));
532
+ }
533
+ else if (report.added.length === 0 && report.removed.length === 0 && report.changed.length === 0) {
534
+ log.success('No dependency changes detected.');
535
+ }
536
+ console.log();
537
+ }
538
+ // ── Arg parsing ──────────────────────────────────────────────────────────
539
+ function parseArgs(argv) {
540
+ const args = [];
541
+ const flags = new Set();
542
+ for (const arg of argv.slice(2)) {
543
+ if (arg.startsWith('--')) {
544
+ flags.add(arg);
545
+ }
546
+ else {
547
+ args.push(arg);
548
+ }
549
+ }
550
+ return {
551
+ command: args[0] ?? 'help',
552
+ args: args.slice(1),
553
+ flags,
554
+ };
555
+ }
556
+ // ── Main ─────────────────────────────────────────────────────────────────
557
+ async function main() {
558
+ const { command, args, flags } = parseArgs(process.argv);
559
+ if (flags.has('--verbose')) {
560
+ log.setLevel('debug');
561
+ }
562
+ switch (command) {
563
+ case 'install':
564
+ if (!args[0]) {
565
+ log.error('Missing package name. Usage: shieldpm install <package>');
566
+ process.exitCode = 1;
567
+ return;
568
+ }
569
+ await cmdInstall(args[0], flags);
570
+ break;
571
+ case 'audit':
572
+ await cmdAudit(flags.has('--deep'), flags);
573
+ break;
574
+ case 'inspect':
575
+ if (!args[0]) {
576
+ log.error('Missing package name. Usage: shieldpm inspect <package>');
577
+ process.exitCode = 1;
578
+ return;
579
+ }
580
+ await cmdInspect(args[0]);
581
+ break;
582
+ case 'sandbox':
583
+ await cmdSandbox(args);
584
+ break;
585
+ case 'manifest':
586
+ if (args[0] === 'generate') {
587
+ await cmdManifestGenerate();
588
+ }
589
+ else if (args[0] === 'enforce') {
590
+ await cmdManifestEnforce();
591
+ }
592
+ else {
593
+ log.error('Unknown manifest subcommand. Use: manifest generate | manifest enforce');
594
+ process.exitCode = 1;
595
+ }
596
+ break;
597
+ case 'diff':
598
+ await cmdDiff();
599
+ break;
600
+ case 'version':
601
+ case '--version':
602
+ case '-v':
603
+ console.log(`shieldpm v${VERSION}`);
604
+ break;
605
+ case 'help':
606
+ case '--help':
607
+ case '-h':
608
+ printHelp();
609
+ break;
610
+ default:
611
+ log.error(`Unknown command: "${command}"`);
612
+ console.log(dim(' Run "shieldpm help" for usage information.'));
613
+ process.exitCode = 1;
614
+ break;
615
+ }
616
+ }
617
+ main().catch((err) => {
618
+ log.error(err instanceof Error ? err.message : String(err));
619
+ process.exitCode = 1;
620
+ });
621
+ //# sourceMappingURL=cli.js.map