codeslick-cli 1.0.3 → 1.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.
- package/README.md +73 -21
- package/bin/codeslick.cjs +21 -2
- package/dist/packages/cli/src/commands/scan.d.ts +3 -0
- package/dist/packages/cli/src/commands/scan.d.ts.map +1 -1
- package/dist/packages/cli/src/commands/scan.js +103 -24
- package/dist/packages/cli/src/commands/scan.js.map +1 -1
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts +28 -2
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts.map +1 -1
- package/dist/packages/cli/src/reporters/cli-reporter.js +393 -4
- package/dist/packages/cli/src/reporters/cli-reporter.js.map +1 -1
- package/dist/packages/cli/src/scanner/local-scanner.d.ts +5 -1
- package/dist/packages/cli/src/scanner/local-scanner.d.ts.map +1 -1
- package/dist/packages/cli/src/scanner/local-scanner.js +110 -16
- package/dist/packages/cli/src/scanner/local-scanner.js.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.d.ts.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.js +24 -16
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.js.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.js +4 -12
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.js +22 -9
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.js.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.js +28 -13
- package/dist/src/lib/analyzers/javascript-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.js +44 -18
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.js.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.js +21 -13
- package/dist/src/lib/analyzers/python-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/validators/context-checker.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/validators/context-checker.js +21 -0
- package/dist/src/lib/analyzers/secrets/validators/context-checker.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.js +4 -12
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.js +25 -9
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.js +14 -4
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/type-checker.d.ts +32 -0
- package/dist/src/lib/analyzers/typescript/type-checker.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/type-checker.js +264 -22
- package/dist/src/lib/analyzers/typescript/type-checker.js.map +1 -1
- package/dist/src/lib/analyzers/typescript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript-analyzer.js +27 -23
- package/dist/src/lib/analyzers/typescript-analyzer.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/scan.ts +77 -25
- package/src/reporters/cli-reporter.ts +449 -4
- package/src/scanner/local-scanner.ts +132 -19
|
@@ -43,6 +43,7 @@ export interface ScannerConfig {
|
|
|
43
43
|
severityThreshold?: 'critical' | 'high' | 'medium' | 'low';
|
|
44
44
|
exclude?: string[];
|
|
45
45
|
autofix?: boolean;
|
|
46
|
+
quickMode?: boolean; // Skip deep TypeScript type checking for speed
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/**
|
|
@@ -72,21 +73,27 @@ export function detectLanguage(filePath: string): SupportedLanguage | null {
|
|
|
72
73
|
|
|
73
74
|
/**
|
|
74
75
|
* Check if file should be excluded based on patterns
|
|
76
|
+
* Uses fast regex-based pattern matching (no filesystem scanning)
|
|
75
77
|
*/
|
|
76
78
|
export function shouldExclude(filePath: string, excludePatterns: string[]): boolean {
|
|
77
79
|
const relativePath = relative(process.cwd(), filePath);
|
|
80
|
+
// Also check with forward slashes for cross-platform compatibility
|
|
81
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
78
82
|
|
|
79
83
|
for (const pattern of excludePatterns) {
|
|
80
|
-
// Convert glob pattern to regex
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
// Convert glob pattern to regex for fast matching
|
|
85
|
+
// Order matters: escape dots first, then handle glob patterns
|
|
86
|
+
const regexPattern = pattern
|
|
87
|
+
.replace(/\./g, '\\.') // Escape dots
|
|
88
|
+
.replace(/\*\*/g, '<<<GLOBSTAR>>>') // Temp placeholder for **
|
|
89
|
+
.replace(/\*/g, '[^/]*') // * matches anything except /
|
|
90
|
+
.replace(/<<<GLOBSTAR>>>/g, '.*') // ** matches anything including /
|
|
91
|
+
.replace(/\?/g, '.') // ? matches single char
|
|
92
|
+
.replace(/\{([^}]+)\}/g, (_, p1) => `(${p1.split(',').join('|')})`); // {a,b} -> (a|b)
|
|
93
|
+
|
|
94
|
+
const regex = new RegExp('^' + regexPattern + '$');
|
|
95
|
+
|
|
96
|
+
if (regex.test(normalizedPath) || regex.test(relativePath)) {
|
|
90
97
|
return true;
|
|
91
98
|
}
|
|
92
99
|
}
|
|
@@ -141,6 +148,8 @@ export async function scanFile(
|
|
|
141
148
|
const code = await readFile(filePath, 'utf-8');
|
|
142
149
|
|
|
143
150
|
// Import analyzer dynamically based on language
|
|
151
|
+
// Pass quickMode option to skip expensive type checking
|
|
152
|
+
const analyzerOptions = { quickMode: config.quickMode || false };
|
|
144
153
|
let result: AnalyzerResult;
|
|
145
154
|
|
|
146
155
|
switch (language) {
|
|
@@ -149,7 +158,7 @@ export async function scanFile(
|
|
|
149
158
|
'../../../../src/lib/analyzers/javascript-analyzer'
|
|
150
159
|
);
|
|
151
160
|
const analyzer = new JavaScriptAnalyzer();
|
|
152
|
-
result = await analyzer.analyze({ code, filename: filePath });
|
|
161
|
+
result = await analyzer.analyze({ code, filename: filePath, options: analyzerOptions });
|
|
153
162
|
break;
|
|
154
163
|
}
|
|
155
164
|
|
|
@@ -158,21 +167,21 @@ export async function scanFile(
|
|
|
158
167
|
'../../../../src/lib/analyzers/typescript-analyzer'
|
|
159
168
|
);
|
|
160
169
|
const analyzer = new TypeScriptAnalyzer();
|
|
161
|
-
result = await analyzer.analyze({ code, filename: filePath });
|
|
170
|
+
result = await analyzer.analyze({ code, filename: filePath, options: analyzerOptions });
|
|
162
171
|
break;
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
case 'python': {
|
|
166
175
|
const { PythonAnalyzer } = await import('../../../../src/lib/analyzers/python-analyzer');
|
|
167
176
|
const analyzer = new PythonAnalyzer();
|
|
168
|
-
result = await analyzer.analyze({ code, filename: filePath });
|
|
177
|
+
result = await analyzer.analyze({ code, filename: filePath, options: analyzerOptions });
|
|
169
178
|
break;
|
|
170
179
|
}
|
|
171
180
|
|
|
172
181
|
case 'java': {
|
|
173
182
|
const { JavaAnalyzer } = await import('../../../../src/lib/analyzers/java-analyzer');
|
|
174
183
|
const analyzer = new JavaAnalyzer();
|
|
175
|
-
result = await analyzer.analyze({ code, filename: filePath });
|
|
184
|
+
result = await analyzer.analyze({ code, filename: filePath, options: analyzerOptions });
|
|
176
185
|
break;
|
|
177
186
|
}
|
|
178
187
|
|
|
@@ -200,7 +209,9 @@ export async function scanFile(
|
|
|
200
209
|
/**
|
|
201
210
|
* Scan multiple files for security vulnerabilities
|
|
202
211
|
*
|
|
203
|
-
*
|
|
212
|
+
* OPTIMIZED (Jan 15, 2026): Uses batch TypeScript compilation for 17x speedup
|
|
213
|
+
* - TypeScript files: Batch processed together (single ts.createProgram)
|
|
214
|
+
* - Other files: Processed in parallel as before
|
|
204
215
|
*
|
|
205
216
|
* @param filePaths - Array of absolute file paths
|
|
206
217
|
* @param config - Scanner configuration
|
|
@@ -210,11 +221,113 @@ export async function scanFiles(
|
|
|
210
221
|
filePaths: string[],
|
|
211
222
|
config: ScannerConfig = {}
|
|
212
223
|
): Promise<FileScanResult[]> {
|
|
213
|
-
//
|
|
214
|
-
const
|
|
224
|
+
// Separate TypeScript files from others for batch processing
|
|
225
|
+
const tsFiles: string[] = [];
|
|
226
|
+
const otherFiles: string[] = [];
|
|
227
|
+
|
|
228
|
+
for (const filePath of filePaths) {
|
|
229
|
+
const language = detectLanguage(filePath);
|
|
230
|
+
if (language === 'typescript') {
|
|
231
|
+
// Check exclusions before adding to batch
|
|
232
|
+
if (!config.exclude || !shouldExclude(filePath, config.exclude)) {
|
|
233
|
+
tsFiles.push(filePath);
|
|
234
|
+
}
|
|
235
|
+
} else if (language) {
|
|
236
|
+
otherFiles.push(filePath);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const results: FileScanResult[] = [];
|
|
241
|
+
|
|
242
|
+
// Batch process TypeScript files (17x faster)
|
|
243
|
+
if (tsFiles.length > 0 && !config.quickMode) {
|
|
244
|
+
const batchResults = await scanTypeScriptBatch(tsFiles, config);
|
|
245
|
+
results.push(...batchResults);
|
|
246
|
+
} else if (tsFiles.length > 0 && config.quickMode) {
|
|
247
|
+
// Quick mode: skip type checking, use parallel processing
|
|
248
|
+
const tsResults = await Promise.all(tsFiles.map((path) => scanFile(path, config)));
|
|
249
|
+
results.push(...tsResults.filter((r): r is FileScanResult => r !== null));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Process other files in parallel (JS, Python, Java)
|
|
253
|
+
if (otherFiles.length > 0) {
|
|
254
|
+
const otherResults = await Promise.all(otherFiles.map((path) => scanFile(path, config)));
|
|
255
|
+
results.push(...otherResults.filter((r): r is FileScanResult => r !== null));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return results;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Batch scan TypeScript files using single ts.createProgram
|
|
263
|
+
* This is 17x faster than scanning each file individually
|
|
264
|
+
*/
|
|
265
|
+
async function scanTypeScriptBatch(
|
|
266
|
+
filePaths: string[],
|
|
267
|
+
_config: ScannerConfig = {}
|
|
268
|
+
): Promise<FileScanResult[]> {
|
|
269
|
+
const { readFile } = await import('fs/promises');
|
|
270
|
+
const { relative } = await import('path');
|
|
271
|
+
|
|
272
|
+
// Import batch diagnostics function
|
|
273
|
+
const { getBatchTypeScriptDiagnostics, convertDiagnosticsToIssues } = await import(
|
|
274
|
+
'../../../../src/lib/analyzers/typescript/type-checker'
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// Get batch diagnostics for all TypeScript files at once
|
|
278
|
+
const batchResult = getBatchTypeScriptDiagnostics(filePaths);
|
|
279
|
+
|
|
280
|
+
// Import TypeScript analyzer for security checks (runs separately)
|
|
281
|
+
const { TypeScriptAnalyzer } = await import(
|
|
282
|
+
'../../../../src/lib/analyzers/typescript-analyzer'
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const results: FileScanResult[] = [];
|
|
286
|
+
|
|
287
|
+
for (const filePath of filePaths) {
|
|
288
|
+
try {
|
|
289
|
+
const code = await readFile(filePath, 'utf-8');
|
|
290
|
+
|
|
291
|
+
// Run security analysis (regex-based, fast)
|
|
292
|
+
const analyzer = new TypeScriptAnalyzer();
|
|
293
|
+
// Use quickMode to skip the per-file type checking (we already did batch)
|
|
294
|
+
const result = await analyzer.analyze({ code, filename: filePath, options: { quickMode: true } });
|
|
295
|
+
|
|
296
|
+
// Add batch type diagnostics to the result
|
|
297
|
+
const fileDiagnostics = batchResult.diagnostics.get(filePath) || [];
|
|
298
|
+
if (fileDiagnostics.length > 0) {
|
|
299
|
+
const typeIssues = convertDiagnosticsToIssues(fileDiagnostics);
|
|
300
|
+
const typeVulnerabilities = typeIssues.map((issue: any) => ({
|
|
301
|
+
severity: issue.severity,
|
|
302
|
+
message: issue.message,
|
|
303
|
+
line: issue.line,
|
|
304
|
+
suggestion: issue.suggestion,
|
|
305
|
+
category: 'type-checking',
|
|
306
|
+
cvssScore: issue.cvssScore,
|
|
307
|
+
exploitLikelihood: issue.exploitLikelihood,
|
|
308
|
+
impact: issue.impact,
|
|
309
|
+
owasp: issue.owasp,
|
|
310
|
+
cwe: issue.cwe
|
|
311
|
+
}));
|
|
312
|
+
result.security.vulnerabilities.push(...typeVulnerabilities);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Count vulnerabilities
|
|
316
|
+
const counts = countVulnerabilities(result);
|
|
317
|
+
|
|
318
|
+
results.push({
|
|
319
|
+
filePath,
|
|
320
|
+
relativePath: relative(process.cwd(), filePath),
|
|
321
|
+
language: 'typescript',
|
|
322
|
+
result,
|
|
323
|
+
...counts,
|
|
324
|
+
});
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error(`Error scanning ${filePath}:`, error);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
215
329
|
|
|
216
|
-
|
|
217
|
-
return results.filter((r): r is FileScanResult => r !== null);
|
|
330
|
+
return results;
|
|
218
331
|
}
|
|
219
332
|
|
|
220
333
|
/**
|