pnpm-catalog-updates 0.5.6 → 0.6.5

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 (220) hide show
  1. package/bin/pcu.js +1 -1
  2. package/dist/index.js +9559 -32
  3. package/dist/index.js.map +1 -1
  4. package/package.json +61 -103
  5. package/src/cli/commands/checkCommand.ts +227 -0
  6. package/src/cli/commands/initCommand.ts +394 -0
  7. package/src/cli/commands/securityCommand.ts +569 -0
  8. package/src/cli/commands/updateCommand.ts +245 -0
  9. package/src/cli/formatters/outputFormatter.ts +830 -0
  10. package/src/cli/formatters/progressBar.ts +700 -0
  11. package/src/cli/index.ts +565 -0
  12. package/src/cli/interactive/interactivePrompts.ts +517 -0
  13. package/src/cli/options/globalOptions.ts +380 -0
  14. package/src/cli/options/index.ts +5 -0
  15. package/src/cli/themes/colorTheme.ts +379 -0
  16. package/src/cli/validators/commandValidator.ts +395 -0
  17. package/src/cli/validators/index.ts +5 -0
  18. package/src/index.ts +4 -0
  19. package/LICENSE +0 -21
  20. package/README.ja.md +0 -582
  21. package/README.md +0 -690
  22. package/README.zh-CN.md +0 -630
  23. package/dist/application/services/CatalogUpdateService.d.ts +0 -209
  24. package/dist/application/services/CatalogUpdateService.d.ts.map +0 -1
  25. package/dist/application/services/CatalogUpdateService.js +0 -836
  26. package/dist/application/services/CatalogUpdateService.js.map +0 -1
  27. package/dist/application/services/WorkspaceService.d.ts +0 -139
  28. package/dist/application/services/WorkspaceService.d.ts.map +0 -1
  29. package/dist/application/services/WorkspaceService.js +0 -340
  30. package/dist/application/services/WorkspaceService.js.map +0 -1
  31. package/dist/cli/commands/CheckCommand.d.ts +0 -40
  32. package/dist/cli/commands/CheckCommand.d.ts.map +0 -1
  33. package/dist/cli/commands/CheckCommand.js +0 -177
  34. package/dist/cli/commands/CheckCommand.js.map +0 -1
  35. package/dist/cli/commands/InitCommand.d.ts +0 -53
  36. package/dist/cli/commands/InitCommand.d.ts.map +0 -1
  37. package/dist/cli/commands/InitCommand.js +0 -338
  38. package/dist/cli/commands/InitCommand.js.map +0 -1
  39. package/dist/cli/commands/SecurityCommand.d.ts +0 -113
  40. package/dist/cli/commands/SecurityCommand.d.ts.map +0 -1
  41. package/dist/cli/commands/SecurityCommand.js +0 -410
  42. package/dist/cli/commands/SecurityCommand.js.map +0 -1
  43. package/dist/cli/commands/UpdateCommand.d.ts +0 -44
  44. package/dist/cli/commands/UpdateCommand.d.ts.map +0 -1
  45. package/dist/cli/commands/UpdateCommand.js +0 -189
  46. package/dist/cli/commands/UpdateCommand.js.map +0 -1
  47. package/dist/cli/formatters/OutputFormatter.d.ts +0 -116
  48. package/dist/cli/formatters/OutputFormatter.d.ts.map +0 -1
  49. package/dist/cli/formatters/OutputFormatter.js +0 -664
  50. package/dist/cli/formatters/OutputFormatter.js.map +0 -1
  51. package/dist/cli/formatters/ProgressBar.d.ts +0 -195
  52. package/dist/cli/formatters/ProgressBar.d.ts.map +0 -1
  53. package/dist/cli/formatters/ProgressBar.js +0 -622
  54. package/dist/cli/formatters/ProgressBar.js.map +0 -1
  55. package/dist/cli/index.d.ts +0 -12
  56. package/dist/cli/index.d.ts.map +0 -1
  57. package/dist/cli/index.js +0 -492
  58. package/dist/cli/index.js.map +0 -1
  59. package/dist/cli/interactive/InteractivePrompts.d.ts +0 -85
  60. package/dist/cli/interactive/InteractivePrompts.d.ts.map +0 -1
  61. package/dist/cli/interactive/InteractivePrompts.js +0 -434
  62. package/dist/cli/interactive/InteractivePrompts.js.map +0 -1
  63. package/dist/cli/options/GlobalOptions.d.ts +0 -117
  64. package/dist/cli/options/GlobalOptions.d.ts.map +0 -1
  65. package/dist/cli/options/GlobalOptions.js +0 -278
  66. package/dist/cli/options/GlobalOptions.js.map +0 -1
  67. package/dist/cli/options/index.d.ts +0 -5
  68. package/dist/cli/options/index.d.ts.map +0 -1
  69. package/dist/cli/options/index.js +0 -5
  70. package/dist/cli/options/index.js.map +0 -1
  71. package/dist/cli/themes/ColorTheme.d.ts +0 -211
  72. package/dist/cli/themes/ColorTheme.d.ts.map +0 -1
  73. package/dist/cli/themes/ColorTheme.js +0 -267
  74. package/dist/cli/themes/ColorTheme.js.map +0 -1
  75. package/dist/cli/validators/CommandValidator.d.ts +0 -60
  76. package/dist/cli/validators/CommandValidator.d.ts.map +0 -1
  77. package/dist/cli/validators/CommandValidator.js +0 -319
  78. package/dist/cli/validators/CommandValidator.js.map +0 -1
  79. package/dist/cli/validators/index.d.ts +0 -5
  80. package/dist/cli/validators/index.d.ts.map +0 -1
  81. package/dist/cli/validators/index.js +0 -5
  82. package/dist/cli/validators/index.js.map +0 -1
  83. package/dist/common/config/Config.d.ts +0 -142
  84. package/dist/common/config/Config.d.ts.map +0 -1
  85. package/dist/common/config/Config.js +0 -382
  86. package/dist/common/config/Config.js.map +0 -1
  87. package/dist/common/config/ConfigLoader.d.ts +0 -49
  88. package/dist/common/config/ConfigLoader.d.ts.map +0 -1
  89. package/dist/common/config/ConfigLoader.js +0 -180
  90. package/dist/common/config/ConfigLoader.js.map +0 -1
  91. package/dist/common/config/PackageFilterConfig.d.ts +0 -56
  92. package/dist/common/config/PackageFilterConfig.d.ts.map +0 -1
  93. package/dist/common/config/PackageFilterConfig.js +0 -94
  94. package/dist/common/config/PackageFilterConfig.js.map +0 -1
  95. package/dist/common/config/index.d.ts +0 -8
  96. package/dist/common/config/index.d.ts.map +0 -1
  97. package/dist/common/config/index.js +0 -8
  98. package/dist/common/config/index.js.map +0 -1
  99. package/dist/common/error-handling/ErrorTracker.d.ts +0 -48
  100. package/dist/common/error-handling/ErrorTracker.d.ts.map +0 -1
  101. package/dist/common/error-handling/ErrorTracker.js +0 -93
  102. package/dist/common/error-handling/ErrorTracker.js.map +0 -1
  103. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts +0 -74
  104. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts.map +0 -1
  105. package/dist/common/error-handling/UserFriendlyErrorHandler.js +0 -703
  106. package/dist/common/error-handling/UserFriendlyErrorHandler.js.map +0 -1
  107. package/dist/common/error-handling/index.d.ts +0 -11
  108. package/dist/common/error-handling/index.d.ts.map +0 -1
  109. package/dist/common/error-handling/index.js +0 -9
  110. package/dist/common/error-handling/index.js.map +0 -1
  111. package/dist/common/logger/Logger.d.ts +0 -110
  112. package/dist/common/logger/Logger.d.ts.map +0 -1
  113. package/dist/common/logger/Logger.js +0 -289
  114. package/dist/common/logger/Logger.js.map +0 -1
  115. package/dist/common/logger/index.d.ts +0 -6
  116. package/dist/common/logger/index.d.ts.map +0 -1
  117. package/dist/common/logger/index.js +0 -6
  118. package/dist/common/logger/index.js.map +0 -1
  119. package/dist/common/types/cli.d.ts +0 -265
  120. package/dist/common/types/cli.d.ts.map +0 -1
  121. package/dist/common/types/cli.js +0 -5
  122. package/dist/common/types/cli.js.map +0 -1
  123. package/dist/common/types/core.d.ts +0 -270
  124. package/dist/common/types/core.d.ts.map +0 -1
  125. package/dist/common/types/core.js +0 -32
  126. package/dist/common/types/core.js.map +0 -1
  127. package/dist/common/types/index.d.ts +0 -8
  128. package/dist/common/types/index.d.ts.map +0 -1
  129. package/dist/common/types/index.js +0 -8
  130. package/dist/common/types/index.js.map +0 -1
  131. package/dist/common/utils/VersionChecker.d.ts +0 -54
  132. package/dist/common/utils/VersionChecker.d.ts.map +0 -1
  133. package/dist/common/utils/VersionChecker.js +0 -180
  134. package/dist/common/utils/VersionChecker.js.map +0 -1
  135. package/dist/common/utils/async.d.ts +0 -74
  136. package/dist/common/utils/async.d.ts.map +0 -1
  137. package/dist/common/utils/async.js +0 -228
  138. package/dist/common/utils/async.js.map +0 -1
  139. package/dist/common/utils/format.d.ts +0 -32
  140. package/dist/common/utils/format.d.ts.map +0 -1
  141. package/dist/common/utils/format.js +0 -121
  142. package/dist/common/utils/format.js.map +0 -1
  143. package/dist/common/utils/git.d.ts +0 -44
  144. package/dist/common/utils/git.d.ts.map +0 -1
  145. package/dist/common/utils/git.js +0 -147
  146. package/dist/common/utils/git.js.map +0 -1
  147. package/dist/common/utils/index.d.ts +0 -12
  148. package/dist/common/utils/index.d.ts.map +0 -1
  149. package/dist/common/utils/index.js +0 -12
  150. package/dist/common/utils/index.js.map +0 -1
  151. package/dist/common/utils/string.d.ts +0 -56
  152. package/dist/common/utils/string.d.ts.map +0 -1
  153. package/dist/common/utils/string.js +0 -134
  154. package/dist/common/utils/string.js.map +0 -1
  155. package/dist/common/utils/validation.d.ts +0 -88
  156. package/dist/common/utils/validation.d.ts.map +0 -1
  157. package/dist/common/utils/validation.js +0 -308
  158. package/dist/common/utils/validation.js.map +0 -1
  159. package/dist/domain/entities/Catalog.d.ts +0 -117
  160. package/dist/domain/entities/Catalog.d.ts.map +0 -1
  161. package/dist/domain/entities/Catalog.js +0 -240
  162. package/dist/domain/entities/Catalog.js.map +0 -1
  163. package/dist/domain/entities/Package.d.ts +0 -143
  164. package/dist/domain/entities/Package.d.ts.map +0 -1
  165. package/dist/domain/entities/Package.js +0 -272
  166. package/dist/domain/entities/Package.js.map +0 -1
  167. package/dist/domain/entities/Workspace.d.ts +0 -95
  168. package/dist/domain/entities/Workspace.d.ts.map +0 -1
  169. package/dist/domain/entities/Workspace.js +0 -173
  170. package/dist/domain/entities/Workspace.js.map +0 -1
  171. package/dist/domain/repositories/WorkspaceRepository.d.ts +0 -41
  172. package/dist/domain/repositories/WorkspaceRepository.d.ts.map +0 -1
  173. package/dist/domain/repositories/WorkspaceRepository.js +0 -8
  174. package/dist/domain/repositories/WorkspaceRepository.js.map +0 -1
  175. package/dist/domain/value-objects/CatalogCollection.d.ts +0 -106
  176. package/dist/domain/value-objects/CatalogCollection.d.ts.map +0 -1
  177. package/dist/domain/value-objects/CatalogCollection.js +0 -230
  178. package/dist/domain/value-objects/CatalogCollection.js.map +0 -1
  179. package/dist/domain/value-objects/PackageCollection.d.ts +0 -122
  180. package/dist/domain/value-objects/PackageCollection.d.ts.map +0 -1
  181. package/dist/domain/value-objects/PackageCollection.js +0 -263
  182. package/dist/domain/value-objects/PackageCollection.js.map +0 -1
  183. package/dist/domain/value-objects/Version.d.ts +0 -141
  184. package/dist/domain/value-objects/Version.d.ts.map +0 -1
  185. package/dist/domain/value-objects/Version.js +0 -268
  186. package/dist/domain/value-objects/Version.js.map +0 -1
  187. package/dist/domain/value-objects/WorkspaceConfig.d.ts +0 -144
  188. package/dist/domain/value-objects/WorkspaceConfig.d.ts.map +0 -1
  189. package/dist/domain/value-objects/WorkspaceConfig.js +0 -357
  190. package/dist/domain/value-objects/WorkspaceConfig.js.map +0 -1
  191. package/dist/domain/value-objects/WorkspaceId.d.ts +0 -51
  192. package/dist/domain/value-objects/WorkspaceId.d.ts.map +0 -1
  193. package/dist/domain/value-objects/WorkspaceId.js +0 -104
  194. package/dist/domain/value-objects/WorkspaceId.js.map +0 -1
  195. package/dist/domain/value-objects/WorkspacePath.d.ts +0 -75
  196. package/dist/domain/value-objects/WorkspacePath.d.ts.map +0 -1
  197. package/dist/domain/value-objects/WorkspacePath.js +0 -128
  198. package/dist/domain/value-objects/WorkspacePath.js.map +0 -1
  199. package/dist/index.d.ts +0 -25
  200. package/dist/index.d.ts.map +0 -1
  201. package/dist/infrastructure/cache/Cache.d.ts +0 -161
  202. package/dist/infrastructure/cache/Cache.d.ts.map +0 -1
  203. package/dist/infrastructure/cache/Cache.js +0 -398
  204. package/dist/infrastructure/cache/Cache.js.map +0 -1
  205. package/dist/infrastructure/cache/index.d.ts +0 -6
  206. package/dist/infrastructure/cache/index.d.ts.map +0 -1
  207. package/dist/infrastructure/cache/index.js +0 -6
  208. package/dist/infrastructure/cache/index.js.map +0 -1
  209. package/dist/infrastructure/external-services/NpmRegistryService.d.ts +0 -145
  210. package/dist/infrastructure/external-services/NpmRegistryService.d.ts.map +0 -1
  211. package/dist/infrastructure/external-services/NpmRegistryService.js +0 -466
  212. package/dist/infrastructure/external-services/NpmRegistryService.js.map +0 -1
  213. package/dist/infrastructure/file-system/FileSystemService.d.ts +0 -120
  214. package/dist/infrastructure/file-system/FileSystemService.d.ts.map +0 -1
  215. package/dist/infrastructure/file-system/FileSystemService.js +0 -663
  216. package/dist/infrastructure/file-system/FileSystemService.js.map +0 -1
  217. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts +0 -57
  218. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts.map +0 -1
  219. package/dist/infrastructure/repositories/FileWorkspaceRepository.js +0 -179
  220. package/dist/infrastructure/repositories/FileWorkspaceRepository.js.map +0 -1
@@ -0,0 +1,569 @@
1
+ /**
2
+ * Security Command
3
+ *
4
+ * CLI command to perform security vulnerability scanning and automated fixes.
5
+ * Integrates with npm audit and snyk for comprehensive security analysis.
6
+ */
7
+
8
+ import { spawnSync } from 'child_process';
9
+ import * as fs from 'fs-extra';
10
+ import * as path from 'path';
11
+ import { OutputFormatter, OutputFormat } from '../formatters/outputFormatter.js';
12
+ import { ProgressBar } from '../formatters/progressBar.js';
13
+ import { StyledText, ThemeManager } from '../themes/colorTheme.js';
14
+
15
+ export interface SecurityCommandOptions {
16
+ workspace?: string;
17
+ format?: OutputFormat;
18
+ audit?: boolean;
19
+ fixVulns?: boolean;
20
+ severity?: 'low' | 'moderate' | 'high' | 'critical';
21
+ includeDev?: boolean;
22
+ snyk?: boolean;
23
+ verbose?: boolean;
24
+ color?: boolean;
25
+ }
26
+
27
+ export interface SecurityReport {
28
+ summary: {
29
+ totalVulnerabilities: number;
30
+ critical: number;
31
+ high: number;
32
+ moderate: number;
33
+ low: number;
34
+ info: number;
35
+ };
36
+ vulnerabilities: Vulnerability[];
37
+ recommendations: SecurityRecommendation[];
38
+ metadata: {
39
+ scanDate: string;
40
+ scanTools: string[];
41
+ workspacePath: string;
42
+ };
43
+ }
44
+
45
+ export interface Vulnerability {
46
+ id: string;
47
+ package: string;
48
+ severity: 'low' | 'moderate' | 'high' | 'critical';
49
+ title: string;
50
+ url: string;
51
+ range: string;
52
+ fixAvailable: boolean | string;
53
+ fixVersion?: string;
54
+ paths: string[];
55
+ cwe?: string[];
56
+ cve?: string[];
57
+ }
58
+
59
+ export interface SecurityRecommendation {
60
+ package: string;
61
+ currentVersion: string;
62
+ recommendedVersion: string;
63
+ type: 'update' | 'remove' | 'replace';
64
+ reason: string;
65
+ impact: string;
66
+ }
67
+
68
+ export class SecurityCommand {
69
+ constructor(private readonly outputFormatter: OutputFormatter) {}
70
+
71
+ /**
72
+ * Execute the security command
73
+ */
74
+ async execute(options: SecurityCommandOptions = {}): Promise<void> {
75
+ let progressBar: ProgressBar | undefined;
76
+
77
+ try {
78
+ // Initialize theme
79
+ ThemeManager.setTheme('default');
80
+
81
+ // Show loading with progress bar
82
+ progressBar = new ProgressBar({
83
+ text: 'Performing security analysis...',
84
+ });
85
+ progressBar.start();
86
+
87
+ if (options.verbose) {
88
+ console.log(StyledText.iconAnalysis('Security vulnerability scanning'));
89
+ console.log(StyledText.muted(`Workspace: ${options.workspace || process.cwd()}`));
90
+ console.log(StyledText.muted(`Severity filter: ${options.severity || 'all'}`));
91
+ console.log('');
92
+ }
93
+
94
+ // Execute security scan
95
+ const report = await this.performSecurityScan(options);
96
+
97
+ progressBar.succeed('Security analysis completed');
98
+
99
+ // Format and display results
100
+ const formattedOutput = this.outputFormatter.formatSecurityReport(report);
101
+ console.log(formattedOutput);
102
+
103
+ // Show recommendations if available
104
+ if (report.recommendations.length > 0) {
105
+ this.showRecommendations(report);
106
+ }
107
+
108
+ // Auto-fix vulnerabilities if requested
109
+ if (options.fixVulns) {
110
+ await this.autoFixVulnerabilities(report, options);
111
+ }
112
+
113
+ // Exit with appropriate code based on findings
114
+ const exitCode = report.summary.critical > 0 ? 1 : 0;
115
+ process.exit(exitCode);
116
+ } catch (error) {
117
+ if (progressBar) {
118
+ progressBar.fail('Security analysis failed');
119
+ }
120
+
121
+ console.error(StyledText.iconError('Error performing security scan:'));
122
+ console.error(StyledText.error(String(error)));
123
+
124
+ if (options.verbose && error instanceof Error) {
125
+ console.error(StyledText.muted('Stack trace:'));
126
+ console.error(StyledText.muted(error.stack || 'No stack trace available'));
127
+ }
128
+
129
+ process.exit(1);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Perform comprehensive security scan
135
+ */
136
+ private async performSecurityScan(options: SecurityCommandOptions): Promise<SecurityReport> {
137
+ const workspacePath = options.workspace || process.cwd();
138
+ const vulnerabilities: Vulnerability[] = [];
139
+ const recommendations: SecurityRecommendation[] = [];
140
+
141
+ // Check if package.json exists
142
+ const packageJsonPath = path.join(workspacePath, 'package.json');
143
+ if (!(await fs.pathExists(packageJsonPath))) {
144
+ throw new Error(`No package.json found in ${workspacePath}`);
145
+ }
146
+
147
+ // Run npm audit
148
+ if (options.audit !== false) {
149
+ const npmVulns = await this.runNpmAudit(workspacePath, options);
150
+ vulnerabilities.push(...npmVulns);
151
+ }
152
+
153
+ // Run snyk scan if available
154
+ if (options.snyk) {
155
+ const snykVulns = await this.runSnykScan(workspacePath, options);
156
+ vulnerabilities.push(...snykVulns);
157
+ }
158
+
159
+ // Generate recommendations
160
+ recommendations.push(...this.generateRecommendations(vulnerabilities));
161
+
162
+ // Filter by severity if specified
163
+ const filteredVulnerabilities = options.severity
164
+ ? vulnerabilities.filter(
165
+ (v) => this.severityToNumber(v.severity) >= this.severityToNumber(options.severity!)
166
+ )
167
+ : vulnerabilities;
168
+
169
+ return {
170
+ summary: this.generateSummary(filteredVulnerabilities),
171
+ vulnerabilities: filteredVulnerabilities,
172
+ recommendations: recommendations,
173
+ metadata: {
174
+ scanDate: new Date().toISOString(),
175
+ scanTools: ['npm-audit', ...(options.snyk ? ['snyk'] : [])],
176
+ workspacePath: workspacePath,
177
+ },
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Run npm audit scan
183
+ */
184
+ private async runNpmAudit(
185
+ workspacePath: string,
186
+ options: SecurityCommandOptions
187
+ ): Promise<Vulnerability[]> {
188
+ const auditArgs = ['audit', '--json'];
189
+
190
+ if (!options.includeDev) {
191
+ auditArgs.push('--omit=dev');
192
+ }
193
+
194
+ const result = spawnSync('npm', auditArgs, {
195
+ cwd: workspacePath,
196
+ encoding: 'utf8',
197
+ stdio: ['pipe', 'pipe', 'pipe'],
198
+ });
199
+
200
+ if (result.error) {
201
+ throw new Error(`npm audit failed: ${result.error.message}`);
202
+ }
203
+
204
+ if (result.status === 1) {
205
+ // npm audit returns 1 when vulnerabilities are found
206
+ try {
207
+ const auditData = JSON.parse(result.stdout);
208
+ return this.parseNpmAuditResults(auditData);
209
+ } catch (parseError) {
210
+ throw new Error(`Failed to parse npm audit output: ${parseError}`);
211
+ }
212
+ } else if (result.status === 0) {
213
+ try {
214
+ const auditData = JSON.parse(result.stdout);
215
+ return this.parseNpmAuditResults(auditData);
216
+ } catch (parseError) {
217
+ throw new Error(`Failed to parse npm audit output: ${parseError}`);
218
+ }
219
+ } else {
220
+ throw new Error(`npm audit failed with status ${result.status}: ${result.stderr}`);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Run snyk scan
226
+ */
227
+ private async runSnykScan(
228
+ workspacePath: string,
229
+ options: SecurityCommandOptions
230
+ ): Promise<Vulnerability[]> {
231
+ try {
232
+ // Check if snyk is installed
233
+ const versionResult = spawnSync('snyk', ['--version'], { stdio: 'pipe' });
234
+ if (versionResult.error) {
235
+ throw versionResult.error;
236
+ }
237
+
238
+ const snykArgs = ['test', '--json'];
239
+
240
+ if (!options.includeDev) {
241
+ snykArgs.push('--dev');
242
+ }
243
+
244
+ const result = spawnSync('snyk', snykArgs, {
245
+ cwd: workspacePath,
246
+ encoding: 'utf8',
247
+ stdio: ['pipe', 'pipe', 'pipe'],
248
+ });
249
+
250
+ if (result.error) {
251
+ throw result.error;
252
+ }
253
+
254
+ if (result.status !== 0 && result.status !== 1) {
255
+ throw new Error(`Snyk scan failed with status ${result.status}: ${result.stderr}`);
256
+ }
257
+
258
+ const snykData = JSON.parse(result.stdout);
259
+ return this.parseSnykResults(snykData);
260
+ } catch (error: any) {
261
+ if (error.code === 'ENOENT') {
262
+ console.warn(StyledText.iconWarning('Snyk not found. Install with: npm install -g snyk'));
263
+ return [];
264
+ }
265
+ throw new Error(`Snyk scan failed: ${error.message}`);
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Parse npm audit results
271
+ */
272
+ private parseNpmAuditResults(auditData: any): Vulnerability[] {
273
+ const vulnerabilities: Vulnerability[] = [];
274
+
275
+ if (!auditData.vulnerabilities) {
276
+ return vulnerabilities;
277
+ }
278
+
279
+ for (const [id, vuln] of Object.entries(auditData.vulnerabilities)) {
280
+ const vulnerability = vuln as any;
281
+
282
+ vulnerabilities.push({
283
+ id: id,
284
+ package: vulnerability.name,
285
+ severity: vulnerability.severity,
286
+ title: vulnerability.title || vulnerability.name,
287
+ url: vulnerability.url || `https://npmjs.com/advisories/${id}`,
288
+ range: vulnerability.range,
289
+ fixAvailable: vulnerability.fixAvailable,
290
+ fixVersion: vulnerability.fixAvailable === true ? vulnerability.fixAvailable : undefined,
291
+ paths: vulnerability.via?.map((v: any) => v.source || v.name) || [vulnerability.name],
292
+ cwe: vulnerability.cwe,
293
+ cve: vulnerability.cve,
294
+ });
295
+ }
296
+
297
+ return vulnerabilities;
298
+ }
299
+
300
+ /**
301
+ * Parse snyk results
302
+ */
303
+ private parseSnykResults(snykData: any): Vulnerability[] {
304
+ const vulnerabilities: Vulnerability[] = [];
305
+
306
+ if (!snykData.vulnerabilities) {
307
+ return vulnerabilities;
308
+ }
309
+
310
+ for (const vuln of snykData.vulnerabilities) {
311
+ vulnerabilities.push({
312
+ id: vuln.id,
313
+ package: vuln.packageName,
314
+ severity: vuln.severity,
315
+ title: vuln.title,
316
+ url: vuln.url,
317
+ range: vuln.semver?.vulnerable?.join(' || ') || vuln.version,
318
+ fixAvailable: vuln.fixedIn?.length > 0,
319
+ fixVersion: vuln.fixedIn?.[0],
320
+ paths: vuln.from || [vuln.packageName],
321
+ cwe: vuln.identifiers?.CWE || [],
322
+ cve: vuln.identifiers?.CVE || [],
323
+ });
324
+ }
325
+
326
+ return vulnerabilities;
327
+ }
328
+
329
+ /**
330
+ * Generate security recommendations
331
+ */
332
+ private generateRecommendations(vulnerabilities: Vulnerability[]): SecurityRecommendation[] {
333
+ const recommendations: SecurityRecommendation[] = [];
334
+ const packages = new Set(vulnerabilities.map((v) => v.package));
335
+
336
+ for (const pkg of packages) {
337
+ const pkgVulns = vulnerabilities.filter((v) => v.package === pkg);
338
+ const criticalVulns = pkgVulns.filter(
339
+ (v) => v.severity === 'critical' || v.severity === 'high'
340
+ );
341
+
342
+ if (criticalVulns.length > 0) {
343
+ const fixVersions = [
344
+ ...new Set(
345
+ criticalVulns.map((v) => v.fixVersion).filter((v) => v && typeof v === 'string')
346
+ ),
347
+ ];
348
+
349
+ if (fixVersions.length > 0) {
350
+ const currentVersion = pkgVulns[0]?.range?.split(' ')[0] || 'unknown';
351
+ const recommendedVersion = fixVersions[0] || 'unknown';
352
+
353
+ recommendations.push({
354
+ package: pkg,
355
+ currentVersion: currentVersion,
356
+ recommendedVersion: recommendedVersion,
357
+ type: 'update',
358
+ reason: `${criticalVulns.length} critical vulnerabilities found`,
359
+ impact: 'High - Security vulnerability fix',
360
+ });
361
+ }
362
+ }
363
+ }
364
+
365
+ return recommendations;
366
+ }
367
+
368
+ /**
369
+ * Generate summary from vulnerabilities
370
+ */
371
+ private generateSummary(vulnerabilities: Vulnerability[]): SecurityReport['summary'] {
372
+ const summary = {
373
+ totalVulnerabilities: vulnerabilities.length,
374
+ critical: 0,
375
+ high: 0,
376
+ moderate: 0,
377
+ low: 0,
378
+ info: 0,
379
+ };
380
+
381
+ for (const vuln of vulnerabilities) {
382
+ const severity = vuln.severity as string;
383
+ switch (severity) {
384
+ case 'critical':
385
+ summary.critical++;
386
+ break;
387
+ case 'high':
388
+ summary.high++;
389
+ break;
390
+ case 'moderate':
391
+ summary.moderate++;
392
+ break;
393
+ case 'low':
394
+ summary.low++;
395
+ break;
396
+ case 'info':
397
+ summary.info++;
398
+ break;
399
+ default:
400
+ summary.info++;
401
+ break;
402
+ }
403
+ }
404
+
405
+ return summary;
406
+ }
407
+
408
+ /**
409
+ * Convert severity string to number for filtering
410
+ */
411
+ private severityToNumber(severity: string): number {
412
+ switch (severity) {
413
+ case 'critical':
414
+ return 4;
415
+ case 'high':
416
+ return 3;
417
+ case 'moderate':
418
+ return 2;
419
+ case 'low':
420
+ return 1;
421
+ case 'info':
422
+ return 0;
423
+ default:
424
+ return 0;
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Show security recommendations
430
+ */
431
+ private showRecommendations(report: SecurityReport): void {
432
+ if (report.recommendations.length === 0) {
433
+ return;
434
+ }
435
+
436
+ console.log('\n' + StyledText.iconInfo('Security Recommendations:'));
437
+
438
+ for (const rec of report.recommendations) {
439
+ console.log(
440
+ ` ${StyledText.iconWarning()} ${rec.package}: ${rec.currentVersion} → ${rec.recommendedVersion}`
441
+ );
442
+ console.log(` ${StyledText.muted(rec.reason)}`);
443
+ console.log(` ${StyledText.muted(rec.impact)}`);
444
+ }
445
+
446
+ console.log('');
447
+ console.log(StyledText.iconUpdate('Run with --fix-vulns to apply automatic fixes'));
448
+ }
449
+
450
+ /**
451
+ * Auto-fix vulnerabilities
452
+ */
453
+ private async autoFixVulnerabilities(
454
+ report: SecurityReport,
455
+ options: SecurityCommandOptions
456
+ ): Promise<void> {
457
+ if (report.recommendations.length === 0) {
458
+ console.log(StyledText.iconSuccess('No security fixes available'));
459
+ return;
460
+ }
461
+
462
+ console.log('\n' + StyledText.iconUpdate('Applying security fixes...'));
463
+
464
+ const workspacePath = options.workspace || process.cwd();
465
+ const fixableVulns = report.recommendations.filter((r) => r.type === 'update');
466
+
467
+ if (fixableVulns.length === 0) {
468
+ console.log(StyledText.iconInfo('No automatic fixes available'));
469
+ return;
470
+ }
471
+
472
+ try {
473
+ // Run npm audit fix
474
+ const fixArgs = ['audit', 'fix'];
475
+ if (!options.includeDev) {
476
+ fixArgs.push('--omit=dev');
477
+ }
478
+
479
+ const result = spawnSync('npm', fixArgs, {
480
+ cwd: workspacePath,
481
+ encoding: 'utf8',
482
+ stdio: 'inherit',
483
+ });
484
+
485
+ if (result.error) {
486
+ throw result.error;
487
+ }
488
+
489
+ if (result.status !== 0) {
490
+ throw new Error(`npm audit fix failed with status ${result.status}`);
491
+ }
492
+
493
+ console.log(StyledText.iconSuccess('Security fixes applied successfully'));
494
+
495
+ // Re-run scan to verify fixes
496
+ console.log(StyledText.iconInfo('Re-running security scan to verify fixes...'));
497
+ const newReport = await this.performSecurityScan({ ...options, fixVulns: false });
498
+
499
+ if (newReport.summary.critical === 0 && newReport.summary.high === 0) {
500
+ console.log(
501
+ StyledText.iconSuccess('All critical and high severity vulnerabilities have been fixed!')
502
+ );
503
+ } else {
504
+ console.log(
505
+ StyledText.iconWarning(
506
+ `${newReport.summary.critical} critical and ${newReport.summary.high} high severity vulnerabilities remain`
507
+ )
508
+ );
509
+ }
510
+ } catch (error: any) {
511
+ console.error(StyledText.iconError('Failed to apply security fixes:'));
512
+ console.error(StyledText.error(error.message));
513
+ }
514
+ }
515
+
516
+ /**
517
+ * Validate command options
518
+ */
519
+ static validateOptions(options: SecurityCommandOptions): string[] {
520
+ const errors: string[] = [];
521
+
522
+ // Validate format
523
+ if (options.format && !['table', 'json', 'yaml', 'minimal'].includes(options.format)) {
524
+ errors.push('Invalid format. Must be one of: table, json, yaml, minimal');
525
+ }
526
+
527
+ // Validate severity
528
+ if (options.severity && !['low', 'moderate', 'high', 'critical'].includes(options.severity)) {
529
+ errors.push('Invalid severity. Must be one of: low, moderate, high, critical');
530
+ }
531
+
532
+ return errors;
533
+ }
534
+
535
+ /**
536
+ * Get command help text
537
+ */
538
+ static getHelpText(): string {
539
+ return `
540
+ Security vulnerability scanning and automated fixes
541
+
542
+ Usage:
543
+ pcu security [options]
544
+
545
+ Options:
546
+ --workspace <path> Workspace directory (default: current directory)
547
+ --format <type> Output format: table, json, yaml, minimal (default: table)
548
+ --audit Perform npm audit scan (default: true)
549
+ --fix-vulns Automatically fix vulnerabilities
550
+ --severity <level> Filter by severity: low, moderate, high, critical
551
+ --include-dev Include dev dependencies in scan
552
+ --snyk Include Snyk scan (requires snyk CLI)
553
+ --verbose Show detailed information
554
+ --no-color Disable colored output
555
+
556
+ Examples:
557
+ pcu security # Basic security scan
558
+ pcu security --fix-vulns # Scan and fix vulnerabilities
559
+ pcu security --severity high # Show only high severity issues
560
+ pcu security --snyk # Include Snyk scan
561
+ pcu security --format json # Output as JSON
562
+
563
+ Exit Codes:
564
+ 0 No vulnerabilities found
565
+ 1 Vulnerabilities found
566
+ 2 Error occurred
567
+ `;
568
+ }
569
+ }