codeslick-cli 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -11
- package/dist/packages/cli/src/commands/scan.d.ts.map +1 -1
- package/dist/packages/cli/src/commands/scan.js +7 -3
- package/dist/packages/cli/src/commands/scan.js.map +1 -1
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts +11 -0
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts.map +1 -1
- package/dist/packages/cli/src/reporters/cli-reporter.js +150 -45
- package/dist/packages/cli/src/reporters/cli-reporter.js.map +1 -1
- package/dist/packages/cli/src/scanner/local-scanner.d.ts +2 -2
- package/dist/packages/cli/src/scanner/local-scanner.d.ts.map +1 -1
- package/dist/packages/cli/src/scanner/local-scanner.js +49 -9
- package/dist/packages/cli/src/scanner/local-scanner.js.map +1 -1
- package/dist/src/lib/analyzers/go-analyzer.d.ts +12 -0
- package/dist/src/lib/analyzers/go-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/go-analyzer.js +113 -0
- package/dist/src/lib/analyzers/go-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/iac/pii-detector.d.ts +27 -0
- package/dist/src/lib/analyzers/iac/pii-detector.d.ts.map +1 -0
- package/dist/src/lib/analyzers/iac/pii-detector.js +199 -0
- package/dist/src/lib/analyzers/iac/pii-detector.js.map +1 -0
- package/dist/src/lib/analyzers/iac/pii-patterns.d.ts +43 -0
- package/dist/src/lib/analyzers/iac/pii-patterns.d.ts.map +1 -0
- package/dist/src/lib/analyzers/iac/pii-patterns.js +228 -0
- package/dist/src/lib/analyzers/iac/pii-patterns.js.map +1 -0
- package/dist/src/lib/analyzers/java-analyzer.d.ts +5 -0
- package/dist/src/lib/analyzers/java-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/java-analyzer.js +51 -0
- package/dist/src/lib/analyzers/java-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.d.ts +8 -4
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.js +109 -13
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.js.map +1 -1
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.js +7 -8
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.js.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.js +16 -12
- package/dist/src/lib/analyzers/javascript-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/kubernetes/checks/network-security.d.ts +33 -0
- package/dist/src/lib/analyzers/kubernetes/checks/network-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/network-security.js +184 -0
- package/dist/src/lib/analyzers/kubernetes/checks/network-security.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/pod-security.d.ts +60 -0
- package/dist/src/lib/analyzers/kubernetes/checks/pod-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/pod-security.js +418 -0
- package/dist/src/lib/analyzers/kubernetes/checks/pod-security.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/rbac-security.d.ts +44 -0
- package/dist/src/lib/analyzers/kubernetes/checks/rbac-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/rbac-security.js +275 -0
- package/dist/src/lib/analyzers/kubernetes/checks/rbac-security.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/resource-management.d.ts +32 -0
- package/dist/src/lib/analyzers/kubernetes/checks/resource-management.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/resource-management.js +176 -0
- package/dist/src/lib/analyzers/kubernetes/checks/resource-management.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/secrets-management.d.ts +38 -0
- package/dist/src/lib/analyzers/kubernetes/checks/secrets-management.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/secrets-management.js +266 -0
- package/dist/src/lib/analyzers/kubernetes/checks/secrets-management.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/service-security.d.ts +26 -0
- package/dist/src/lib/analyzers/kubernetes/checks/service-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/checks/service-security.js +120 -0
- package/dist/src/lib/analyzers/kubernetes/checks/service-security.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/parser.d.ts +74 -0
- package/dist/src/lib/analyzers/kubernetes/parser.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/parser.js +233 -0
- package/dist/src/lib/analyzers/kubernetes/parser.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/pii-detector.d.ts +34 -0
- package/dist/src/lib/analyzers/kubernetes/pii-detector.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/pii-detector.js +182 -0
- package/dist/src/lib/analyzers/kubernetes/pii-detector.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/types.d.ts +266 -0
- package/dist/src/lib/analyzers/kubernetes/types.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes/types.js +77 -0
- package/dist/src/lib/analyzers/kubernetes/types.js.map +1 -0
- package/dist/src/lib/analyzers/kubernetes-analyzer.d.ts +93 -0
- package/dist/src/lib/analyzers/kubernetes-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/kubernetes-analyzer.js +215 -0
- package/dist/src/lib/analyzers/kubernetes-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/python-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.js +32 -48
- package/dist/src/lib/analyzers/python-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/ai-providers.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/ai-providers.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/cloud-providers.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/cloud-providers.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/generic.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/generic.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/stripe.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/stripe.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/credentials.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/credentials.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/credentials.js +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/credentials.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/private-keys.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/private-keys.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/tokens.d.ts +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/tokens.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.d.ts +6 -32
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.js +48 -4
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/types.d.ts +40 -0
- package/dist/src/lib/analyzers/secrets/types.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/types.js +10 -0
- package/dist/src/lib/analyzers/secrets/types.js.map +1 -0
- package/dist/src/lib/analyzers/terraform/aws-checks.d.ts +71 -0
- package/dist/src/lib/analyzers/terraform/aws-checks.d.ts.map +1 -0
- package/dist/src/lib/analyzers/terraform/aws-checks.js +538 -0
- package/dist/src/lib/analyzers/terraform/aws-checks.js.map +1 -0
- package/dist/src/lib/analyzers/terraform/parser.d.ts +14 -0
- package/dist/src/lib/analyzers/terraform/parser.d.ts.map +1 -0
- package/dist/src/lib/analyzers/terraform/parser.js +237 -0
- package/dist/src/lib/analyzers/terraform/parser.js.map +1 -0
- package/dist/src/lib/analyzers/terraform/types.d.ts +70 -0
- package/dist/src/lib/analyzers/terraform/types.d.ts.map +1 -0
- package/dist/src/lib/analyzers/terraform/types.js +9 -0
- package/dist/src/lib/analyzers/terraform/types.js.map +1 -0
- package/dist/src/lib/analyzers/terraform-analyzer.d.ts +50 -0
- package/dist/src/lib/analyzers/terraform-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/terraform-analyzer.js +168 -0
- package/dist/src/lib/analyzers/terraform-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.js +23 -8
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.js.map +1 -1
- package/dist/src/lib/analyzers/typescript-analyzer.d.ts +5 -0
- package/dist/src/lib/analyzers/typescript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript-analyzer.js +76 -0
- package/dist/src/lib/analyzers/typescript-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/utils/false-positive-filter.d.ts +27 -0
- package/dist/src/lib/analyzers/utils/false-positive-filter.d.ts.map +1 -0
- package/dist/src/lib/analyzers/utils/false-positive-filter.js +176 -0
- package/dist/src/lib/analyzers/utils/false-positive-filter.js.map +1 -0
- package/dist/src/lib/security/epss-service.d.ts.map +1 -1
- package/dist/src/lib/security/epss-service.js +83 -50
- package/dist/src/lib/security/epss-service.js.map +1 -1
- package/dist/src/lib/security/severity-scoring.d.ts.map +1 -1
- package/dist/src/lib/security/severity-scoring.js +140 -0
- package/dist/src/lib/security/severity-scoring.js.map +1 -1
- package/dist/src/lib/types/index.d.ts +3 -3
- package/dist/src/lib/types/index.d.ts.map +1 -1
- package/dist/src/lib/utils/ignore-patterns.d.ts +60 -0
- package/dist/src/lib/utils/ignore-patterns.d.ts.map +1 -0
- package/dist/src/lib/utils/ignore-patterns.js +212 -0
- package/dist/src/lib/utils/ignore-patterns.js.map +1 -0
- package/package.json +10 -7
- package/src/commands/scan.ts +7 -3
- package/src/reporters/cli-reporter.ts +174 -48
- package/src/scanner/local-scanner.ts +65 -10
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CodeSlick Ignore Patterns Utility
|
|
4
|
+
*
|
|
5
|
+
* Handles .codeslickignore file processing and pattern matching
|
|
6
|
+
* to exclude files from security analysis (analyzers, tests, examples, etc.)
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.loadIgnorePatterns = loadIgnorePatterns;
|
|
13
|
+
exports.shouldIgnoreFile = shouldIgnoreFile;
|
|
14
|
+
exports.hasInlineSuppression = hasInlineSuppression;
|
|
15
|
+
exports.isAnalyzerImplementation = isAnalyzerImplementation;
|
|
16
|
+
exports.isEducationalContent = isEducationalContent;
|
|
17
|
+
exports.isTestFile = isTestFile;
|
|
18
|
+
exports.isLegitimateAPI = isLegitimateAPI;
|
|
19
|
+
exports.filterSuppressedVulnerabilities = filterSuppressedVulnerabilities;
|
|
20
|
+
exports.clearCache = clearCache;
|
|
21
|
+
const minimatch_1 = require("minimatch");
|
|
22
|
+
const fs_1 = __importDefault(require("fs"));
|
|
23
|
+
const path_1 = __importDefault(require("path"));
|
|
24
|
+
let cachedPatterns = null;
|
|
25
|
+
/**
|
|
26
|
+
* Load patterns from .codeslickignore file
|
|
27
|
+
*/
|
|
28
|
+
function loadIgnorePatterns(projectRoot = process.cwd()) {
|
|
29
|
+
// Return cached patterns if already loaded
|
|
30
|
+
if (cachedPatterns?.loaded && cachedPatterns.ignoreFile === projectRoot) {
|
|
31
|
+
return cachedPatterns.patterns;
|
|
32
|
+
}
|
|
33
|
+
const ignoreFilePath = path_1.default.join(projectRoot, '.codeslickignore');
|
|
34
|
+
const patterns = [];
|
|
35
|
+
try {
|
|
36
|
+
if (fs_1.default.existsSync(ignoreFilePath)) {
|
|
37
|
+
const content = fs_1.default.readFileSync(ignoreFilePath, 'utf-8');
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
// Skip empty lines and comments
|
|
42
|
+
if (trimmed === '' || trimmed.startsWith('#')) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
patterns.push(trimmed);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.warn(`Warning: Could not read .codeslickignore file: ${error}`);
|
|
51
|
+
}
|
|
52
|
+
// Cache the patterns
|
|
53
|
+
cachedPatterns = {
|
|
54
|
+
patterns,
|
|
55
|
+
loaded: true,
|
|
56
|
+
ignoreFile: projectRoot,
|
|
57
|
+
};
|
|
58
|
+
return patterns;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a file should be ignored based on .codeslickignore patterns
|
|
62
|
+
*/
|
|
63
|
+
function shouldIgnoreFile(filePath, projectRoot = process.cwd()) {
|
|
64
|
+
const patterns = loadIgnorePatterns(projectRoot);
|
|
65
|
+
if (patterns.length === 0) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Normalize path to use forward slashes (works on both Windows and Unix)
|
|
69
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
70
|
+
// Get relative path from project root
|
|
71
|
+
const relativePath = path_1.default.relative(projectRoot, filePath).replace(/\\/g, '/');
|
|
72
|
+
// Check against each pattern
|
|
73
|
+
for (const pattern of patterns) {
|
|
74
|
+
// Test both absolute and relative paths
|
|
75
|
+
if ((0, minimatch_1.minimatch)(normalizedPath, pattern, { dot: true, matchBase: true }) ||
|
|
76
|
+
(0, minimatch_1.minimatch)(relativePath, pattern, { dot: true, matchBase: true })) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if code contains an inline suppression comment
|
|
84
|
+
* Supports:
|
|
85
|
+
* // codeslick-ignore-next-line: reason (JavaScript, TypeScript, Java, Go, Terraform)
|
|
86
|
+
* # codeslick-ignore-next-line: reason (Python)
|
|
87
|
+
*
|
|
88
|
+
* @param code - The full source code
|
|
89
|
+
* @param lineNumber - 1-indexed line number where the issue was detected
|
|
90
|
+
*/
|
|
91
|
+
function hasInlineSuppression(code, lineNumber) {
|
|
92
|
+
const lines = code.split('\n');
|
|
93
|
+
// Check the line BEFORE the issue
|
|
94
|
+
// lineNumber is 1-indexed, so line 11 means we want to check line 10
|
|
95
|
+
// Array is 0-indexed, so line 10 is at index 9, which is lineNumber - 2
|
|
96
|
+
if (lineNumber > 1 && lineNumber <= lines.length) {
|
|
97
|
+
const previousLine = lines[lineNumber - 2]; // Fixed: was lineNumber - 1
|
|
98
|
+
// Match: // codeslick-ignore-next-line or // codeslick-ignore-next-line: reason
|
|
99
|
+
const slashMatch = previousLine.match(/\/\/\s*codeslick-ignore-next-line(?::\s*(.+))?/);
|
|
100
|
+
if (slashMatch) {
|
|
101
|
+
return {
|
|
102
|
+
suppressed: true,
|
|
103
|
+
reason: slashMatch[1]?.trim() || 'Inline suppression',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Match: # codeslick-ignore-next-line or # codeslick-ignore-next-line: reason (Python)
|
|
107
|
+
const hashMatch = previousLine.match(/#\s*codeslick-ignore-next-line(?::\s*(.+))?/);
|
|
108
|
+
if (hashMatch) {
|
|
109
|
+
return {
|
|
110
|
+
suppressed: true,
|
|
111
|
+
reason: hashMatch[1]?.trim() || 'Inline suppression',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { suppressed: false };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check if code is in an analyzer implementation file
|
|
119
|
+
* (contains detection patterns, not actual vulnerabilities)
|
|
120
|
+
*/
|
|
121
|
+
function isAnalyzerImplementation(filePath) {
|
|
122
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
123
|
+
// Analyzer implementation patterns
|
|
124
|
+
const analyzerPatterns = [
|
|
125
|
+
/\/analyzers\/.*-analyzer\.ts$/,
|
|
126
|
+
/\/analyzers\/.*\/checks\//,
|
|
127
|
+
/\/analyzers\/.*\/parser\.ts$/,
|
|
128
|
+
];
|
|
129
|
+
return analyzerPatterns.some(pattern => pattern.test(normalizedPath));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if code is educational/example content
|
|
133
|
+
* (may contain intentional vulnerabilities for demonstration)
|
|
134
|
+
*/
|
|
135
|
+
function isEducationalContent(filePath) {
|
|
136
|
+
const normalizedPath = filePath.replace(/\\/g, '/').toLowerCase();
|
|
137
|
+
const educationalPatterns = [
|
|
138
|
+
/ai-examples\//,
|
|
139
|
+
/examples\//,
|
|
140
|
+
/\/security-fixer\.ts$/,
|
|
141
|
+
/\/pattern-fixer\.ts$/,
|
|
142
|
+
/docs\/.*\.md$/,
|
|
143
|
+
];
|
|
144
|
+
return educationalPatterns.some(pattern => pattern.test(normalizedPath));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if file is a test file
|
|
148
|
+
*/
|
|
149
|
+
function isTestFile(filePath) {
|
|
150
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
151
|
+
const testPatterns = [
|
|
152
|
+
/\.test\.(ts|js|tsx|jsx|py|go|java)$/,
|
|
153
|
+
/\/__tests__\//,
|
|
154
|
+
/\/__mocks__\//,
|
|
155
|
+
/\/tests?\//,
|
|
156
|
+
/^test-.*\.(tf|ts|js|py|go|java)$/,
|
|
157
|
+
];
|
|
158
|
+
return testPatterns.some(pattern => pattern.test(normalizedPath));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if a URL is a known legitimate API endpoint
|
|
162
|
+
* (whitelisted APIs that should not be flagged as SSRF)
|
|
163
|
+
*/
|
|
164
|
+
function isLegitimateAPI(url) {
|
|
165
|
+
const legitimateAPIs = [
|
|
166
|
+
'openrouter.ai',
|
|
167
|
+
'api.openai.com',
|
|
168
|
+
'anthropic.com',
|
|
169
|
+
'api.anthropic.com',
|
|
170
|
+
'api.deepseek.com',
|
|
171
|
+
'together.xyz',
|
|
172
|
+
'api.together.xyz',
|
|
173
|
+
'replicate.com',
|
|
174
|
+
'vercel.com',
|
|
175
|
+
'github.com',
|
|
176
|
+
'api.github.com',
|
|
177
|
+
];
|
|
178
|
+
try {
|
|
179
|
+
const parsedUrl = new URL(url);
|
|
180
|
+
return legitimateAPIs.some(domain => parsedUrl.hostname.includes(domain));
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// If URL parsing fails, don't whitelist
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Filter vulnerabilities based on inline suppression comments
|
|
189
|
+
* Removes vulnerabilities that have a codeslick-ignore-next-line comment above them
|
|
190
|
+
*/
|
|
191
|
+
function filterSuppressedVulnerabilities(code, vulnerabilities) {
|
|
192
|
+
return vulnerabilities.filter(vuln => {
|
|
193
|
+
// Skip if no line number (can't check suppression)
|
|
194
|
+
if (!vuln.line) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
const { suppressed, reason } = hasInlineSuppression(code, vuln.line);
|
|
198
|
+
if (suppressed) {
|
|
199
|
+
// Log suppression for debugging (can be removed in production)
|
|
200
|
+
console.log(`[CodeSlick] Suppressed vulnerability at line ${vuln.line}: ${reason}`);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Clear cached patterns (useful for testing)
|
|
208
|
+
*/
|
|
209
|
+
function clearCache() {
|
|
210
|
+
cachedPatterns = null;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=ignore-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore-patterns.js","sourceRoot":"","sources":["../../../../../../src/lib/utils/ignore-patterns.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeslick-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CodeSlick CLI tool for pre-commit security scanning",
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "CodeSlick CLI tool for pre-commit security scanning with Terraform IaC support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"codeslick": "./bin/codeslick.cjs",
|
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
"static-analysis",
|
|
20
20
|
"pre-commit",
|
|
21
21
|
"git-hook",
|
|
22
|
-
"vulnerability-scanner"
|
|
22
|
+
"vulnerability-scanner",
|
|
23
|
+
"terraform",
|
|
24
|
+
"iac",
|
|
25
|
+
"infrastructure-as-code"
|
|
23
26
|
],
|
|
24
27
|
"author": "CodeSlick <support@codeslick.dev>",
|
|
25
28
|
"license": "MIT",
|
|
@@ -36,12 +39,12 @@
|
|
|
36
39
|
"node": ">=18.0.0"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"yargs": "^17.7.2",
|
|
40
42
|
"chalk": "^4.1.2",
|
|
41
|
-
"ora": "^5.4.1",
|
|
42
43
|
"cli-table3": "^0.6.3",
|
|
43
|
-
"glob": "^
|
|
44
|
-
"
|
|
44
|
+
"glob": "^13.0.1",
|
|
45
|
+
"ora": "^5.4.1",
|
|
46
|
+
"typescript": "^5.3.3",
|
|
47
|
+
"yargs": "^17.7.2"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
47
50
|
"@types/node": "^20.10.0",
|
package/src/commands/scan.ts
CHANGED
|
@@ -291,8 +291,10 @@ export async function scanCommand(args: ScanArgs): Promise<void> {
|
|
|
291
291
|
// Collect unique languages scanned
|
|
292
292
|
const languages = [...new Set(results.map(r => r.language))];
|
|
293
293
|
|
|
294
|
-
// Send telemetry
|
|
295
|
-
|
|
294
|
+
// Send telemetry — store the promise so we can await it before process.exit.
|
|
295
|
+
// process.exit() terminates all pending async ops, so fire-and-forget never
|
|
296
|
+
// completes. The telemetry function has a 3-second timeout built in.
|
|
297
|
+
const telemetryDone = trackScan({
|
|
296
298
|
filesScanned: results.length,
|
|
297
299
|
languages,
|
|
298
300
|
vulnerabilities: {
|
|
@@ -302,7 +304,7 @@ export async function scanCommand(args: ScanArgs): Promise<void> {
|
|
|
302
304
|
low: totalLow,
|
|
303
305
|
},
|
|
304
306
|
scanDuration: duration,
|
|
305
|
-
}).catch(() => {}); //
|
|
307
|
+
}).catch(() => {}); // Never let telemetry errors surface
|
|
306
308
|
|
|
307
309
|
// WR2: Build threshold configuration from CLI args or use defaults
|
|
308
310
|
// NOTE: For CLI, thresholds are ENABLED by default (unlike dashboard which is opt-in)
|
|
@@ -382,6 +384,7 @@ export async function scanCommand(args: ScanArgs): Promise<void> {
|
|
|
382
384
|
}
|
|
383
385
|
}
|
|
384
386
|
|
|
387
|
+
await telemetryDone; // Wait for telemetry before terminating process
|
|
385
388
|
process.exit(1); // Exit with failure
|
|
386
389
|
} else {
|
|
387
390
|
if (!args.json) {
|
|
@@ -391,6 +394,7 @@ export async function scanCommand(args: ScanArgs): Promise<void> {
|
|
|
391
394
|
}
|
|
392
395
|
}
|
|
393
396
|
|
|
397
|
+
await telemetryDone; // Wait for telemetry before terminating process
|
|
394
398
|
process.exit(0); // Exit with success
|
|
395
399
|
}
|
|
396
400
|
} catch (error) {
|
|
@@ -59,18 +59,38 @@ export function getSeveritySymbol(severity: string): string {
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Print scan results summary table
|
|
62
|
+
*
|
|
63
|
+
* IMPORTANT: Recalculates counts from actual filtered vulnerabilities
|
|
64
|
+
* to ensure summary reflects false-positive filtering.
|
|
62
65
|
*/
|
|
63
66
|
export function printSummaryTable(results: FileScanResult[]): void {
|
|
64
67
|
const totalFiles = results.length;
|
|
65
|
-
const totalCritical = results.reduce((sum, r) => sum + r.critical, 0);
|
|
66
|
-
const totalHigh = results.reduce((sum, r) => sum + r.high, 0);
|
|
67
|
-
const totalMedium = results.reduce((sum, r) => sum + r.medium, 0);
|
|
68
|
-
const totalLow = results.reduce((sum, r) => sum + r.low, 0);
|
|
69
|
-
const totalVulns = totalCritical + totalHigh + totalMedium + totalLow;
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
// Recalculate from actual vulnerabilities (post-filtering)
|
|
70
|
+
let totalCritical = 0;
|
|
71
|
+
let totalHigh = 0;
|
|
72
|
+
let totalMedium = 0;
|
|
73
|
+
let totalLow = 0;
|
|
74
|
+
let filesWithIssues = 0;
|
|
75
|
+
|
|
76
|
+
results.forEach((r) => {
|
|
77
|
+
const vulns = r.result.security?.vulnerabilities || [];
|
|
78
|
+
const crit = vulns.filter((v: any) => v.severity?.toLowerCase() === 'critical').length;
|
|
79
|
+
const high = vulns.filter((v: any) => v.severity?.toLowerCase() === 'high').length;
|
|
80
|
+
const med = vulns.filter((v: any) => v.severity?.toLowerCase() === 'medium').length;
|
|
81
|
+
const low = vulns.filter((v: any) => v.severity?.toLowerCase() === 'low').length;
|
|
82
|
+
|
|
83
|
+
totalCritical += crit;
|
|
84
|
+
totalHigh += high;
|
|
85
|
+
totalMedium += med;
|
|
86
|
+
totalLow += low;
|
|
87
|
+
|
|
88
|
+
if (crit + high + med + low > 0) {
|
|
89
|
+
filesWithIssues++;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const totalVulns = totalCritical + totalHigh + totalMedium + totalLow;
|
|
74
94
|
|
|
75
95
|
console.log('');
|
|
76
96
|
console.log(chalk.bold('Scan Summary'));
|
|
@@ -281,6 +301,8 @@ export function printCommitAllowed(): void {
|
|
|
281
301
|
/**
|
|
282
302
|
* Print summary table grouped by language
|
|
283
303
|
* Shows files scanned, issues found, and critical count per language
|
|
304
|
+
*
|
|
305
|
+
* IMPORTANT: Recalculates counts from actual filtered vulnerabilities.
|
|
284
306
|
*/
|
|
285
307
|
export function printLanguageSummary(results: FileScanResult[]): void {
|
|
286
308
|
// Group results by language
|
|
@@ -289,12 +311,20 @@ export function printLanguageSummary(results: FileScanResult[]): void {
|
|
|
289
311
|
for (const result of results) {
|
|
290
312
|
const lang = result.language;
|
|
291
313
|
const existing = byLanguage.get(lang) || { files: 0, issues: 0, critical: 0, high: 0, medium: 0, low: 0 };
|
|
314
|
+
|
|
315
|
+
// Recalculate from actual vulnerabilities
|
|
316
|
+
const vulns = result.result.security?.vulnerabilities || [];
|
|
317
|
+
const crit = vulns.filter((v: any) => v.severity?.toLowerCase() === 'critical').length;
|
|
318
|
+
const high = vulns.filter((v: any) => v.severity?.toLowerCase() === 'high').length;
|
|
319
|
+
const med = vulns.filter((v: any) => v.severity?.toLowerCase() === 'medium').length;
|
|
320
|
+
const low = vulns.filter((v: any) => v.severity?.toLowerCase() === 'low').length;
|
|
321
|
+
|
|
292
322
|
existing.files++;
|
|
293
|
-
existing.critical +=
|
|
294
|
-
existing.high +=
|
|
295
|
-
existing.medium +=
|
|
296
|
-
existing.low +=
|
|
297
|
-
existing.issues +=
|
|
323
|
+
existing.critical += crit;
|
|
324
|
+
existing.high += high;
|
|
325
|
+
existing.medium += med;
|
|
326
|
+
existing.low += low;
|
|
327
|
+
existing.issues += crit + high + med + low;
|
|
298
328
|
byLanguage.set(lang, existing);
|
|
299
329
|
}
|
|
300
330
|
|
|
@@ -478,29 +508,76 @@ function truncateMessage(message: string, maxLength: number): string {
|
|
|
478
508
|
|
|
479
509
|
/**
|
|
480
510
|
* Output results as JSON
|
|
511
|
+
*
|
|
512
|
+
* IMPORTANT: Summary counts are calculated from ACTUAL filtered vulnerabilities,
|
|
513
|
+
* not from pre-calculated counts. This ensures the summary reflects
|
|
514
|
+
* false-positive filtering applied during analysis.
|
|
481
515
|
*/
|
|
482
516
|
export function printJSONResults(results: FileScanResult[]): void {
|
|
517
|
+
// Recalculate counts from actual filtered vulnerabilities
|
|
518
|
+
// This ensures summary matches the vulnerabilities array after false-positive filtering
|
|
519
|
+
const recalculateCountsFromVulnerabilities = (r: FileScanResult) => {
|
|
520
|
+
const vulns = r.result.security?.vulnerabilities || [];
|
|
521
|
+
const counts = {
|
|
522
|
+
critical: vulns.filter((v: any) => v.severity?.toLowerCase() === 'critical').length,
|
|
523
|
+
high: vulns.filter((v: any) => v.severity?.toLowerCase() === 'high').length,
|
|
524
|
+
medium: vulns.filter((v: any) => v.severity?.toLowerCase() === 'medium').length,
|
|
525
|
+
low: vulns.filter((v: any) => v.severity?.toLowerCase() === 'low').length,
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// Debug first file
|
|
529
|
+
if (process.env.DEBUG_COUNTS && r.relativePath === 'scripts/reset-quota.ts') {
|
|
530
|
+
console.error(`[DEBUG] ${r.relativePath}: old_high=${r.high}, new_high=${counts.high}, total_vulns=${vulns.length}`);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return counts;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// Calculate accurate summary from filtered vulnerabilities
|
|
537
|
+
let totalCritical = 0;
|
|
538
|
+
let totalHigh = 0;
|
|
539
|
+
let totalMedium = 0;
|
|
540
|
+
let totalLow = 0;
|
|
541
|
+
let filesWithIssues = 0;
|
|
542
|
+
|
|
543
|
+
const filesOutput = results.map((r) => {
|
|
544
|
+
const counts = recalculateCountsFromVulnerabilities(r);
|
|
545
|
+
|
|
546
|
+
// Debug: Log first mismatch
|
|
547
|
+
if (counts.high !== r.high && process.env.DEBUG_COUNTS) {
|
|
548
|
+
console.error(`[DEBUG] Mismatch in ${r.relativePath}: old=${r.high}, new=${counts.high}, vulns=${r.result.security?.vulnerabilities?.length || 0}`);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Accumulate totals
|
|
552
|
+
totalCritical += counts.critical;
|
|
553
|
+
totalHigh += counts.high;
|
|
554
|
+
totalMedium += counts.medium;
|
|
555
|
+
totalLow += counts.low;
|
|
556
|
+
|
|
557
|
+
// Count files with issues
|
|
558
|
+
if (counts.critical + counts.high + counts.medium + counts.low > 0) {
|
|
559
|
+
filesWithIssues++;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
path: r.relativePath,
|
|
564
|
+
language: r.language,
|
|
565
|
+
vulnerabilities: r.result.security?.vulnerabilities || [],
|
|
566
|
+
counts,
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
|
|
483
570
|
const output = {
|
|
484
571
|
summary: {
|
|
485
572
|
filesScanned: results.length,
|
|
486
|
-
filesWithIssues
|
|
487
|
-
totalVulnerabilities:
|
|
488
|
-
critical:
|
|
489
|
-
high:
|
|
490
|
-
medium:
|
|
491
|
-
low:
|
|
573
|
+
filesWithIssues,
|
|
574
|
+
totalVulnerabilities: totalCritical + totalHigh + totalMedium + totalLow,
|
|
575
|
+
critical: totalCritical,
|
|
576
|
+
high: totalHigh,
|
|
577
|
+
medium: totalMedium,
|
|
578
|
+
low: totalLow,
|
|
492
579
|
},
|
|
493
|
-
files:
|
|
494
|
-
path: r.relativePath,
|
|
495
|
-
language: r.language,
|
|
496
|
-
vulnerabilities: r.result.security?.vulnerabilities || [],
|
|
497
|
-
counts: {
|
|
498
|
-
critical: r.critical,
|
|
499
|
-
high: r.high,
|
|
500
|
-
medium: r.medium,
|
|
501
|
-
low: r.low,
|
|
502
|
-
},
|
|
503
|
-
})),
|
|
580
|
+
files: filesOutput,
|
|
504
581
|
};
|
|
505
582
|
|
|
506
583
|
console.log(JSON.stringify(output, null, 2));
|
|
@@ -520,28 +597,48 @@ export function generateMarkdownReport(
|
|
|
520
597
|
const filename = `codeslick-report-${timestamp}.md`;
|
|
521
598
|
const filepath = join(process.cwd(), filename);
|
|
522
599
|
|
|
523
|
-
// Calculate totals
|
|
600
|
+
// Calculate totals from actual vulnerabilities (post-filtering)
|
|
524
601
|
const totalFiles = results.length;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const filesWithIssues = results.filter(r => r.critical + r.high + r.medium + r.low > 0).length;
|
|
602
|
+
let totalCritical = 0;
|
|
603
|
+
let totalHigh = 0;
|
|
604
|
+
let totalMedium = 0;
|
|
605
|
+
let totalLow = 0;
|
|
606
|
+
let filesWithIssues = 0;
|
|
531
607
|
|
|
532
|
-
// Group by language
|
|
608
|
+
// Group by language and recalculate counts
|
|
533
609
|
const byLanguage = new Map<string, { files: number; critical: number; high: number; medium: number; low: number }>();
|
|
534
610
|
for (const result of results) {
|
|
535
611
|
const lang = result.language;
|
|
536
612
|
const existing = byLanguage.get(lang) || { files: 0, critical: 0, high: 0, medium: 0, low: 0 };
|
|
613
|
+
|
|
614
|
+
// Recalculate from actual vulnerabilities
|
|
615
|
+
const vulns = result.result.security?.vulnerabilities || [];
|
|
616
|
+
const crit = vulns.filter((v: any) => v.severity?.toLowerCase() === 'critical').length;
|
|
617
|
+
const high = vulns.filter((v: any) => v.severity?.toLowerCase() === 'high').length;
|
|
618
|
+
const med = vulns.filter((v: any) => v.severity?.toLowerCase() === 'medium').length;
|
|
619
|
+
const low = vulns.filter((v: any) => v.severity?.toLowerCase() === 'low').length;
|
|
620
|
+
|
|
537
621
|
existing.files++;
|
|
538
|
-
existing.critical +=
|
|
539
|
-
existing.high +=
|
|
540
|
-
existing.medium +=
|
|
541
|
-
existing.low +=
|
|
622
|
+
existing.critical += crit;
|
|
623
|
+
existing.high += high;
|
|
624
|
+
existing.medium += med;
|
|
625
|
+
existing.low += low;
|
|
542
626
|
byLanguage.set(lang, existing);
|
|
627
|
+
|
|
628
|
+
// Accumulate totals
|
|
629
|
+
totalCritical += crit;
|
|
630
|
+
totalHigh += high;
|
|
631
|
+
totalMedium += med;
|
|
632
|
+
totalLow += low;
|
|
633
|
+
|
|
634
|
+
// Count files with issues
|
|
635
|
+
if (crit + high + med + low > 0) {
|
|
636
|
+
filesWithIssues++;
|
|
637
|
+
}
|
|
543
638
|
}
|
|
544
639
|
|
|
640
|
+
const totalVulns = totalCritical + totalHigh + totalMedium + totalLow;
|
|
641
|
+
|
|
545
642
|
// Collect all vulnerabilities for Top 10
|
|
546
643
|
const allVulns: Array<{ file: string; line: number; vuln: SecurityVulnerability }> = [];
|
|
547
644
|
for (const result of results) {
|
|
@@ -656,8 +753,25 @@ export function generateMarkdownReport(
|
|
|
656
753
|
md += `\n*Supported: .js, .jsx, .ts, .tsx, .py, .java*\n`;
|
|
657
754
|
}
|
|
658
755
|
|
|
659
|
-
// Files with issues (detailed)
|
|
660
|
-
const filesWithProblems = results
|
|
756
|
+
// Files with issues (detailed) - recalculate counts from actual vulnerabilities
|
|
757
|
+
const filesWithProblems = results
|
|
758
|
+
.map((r) => {
|
|
759
|
+
const vulns = r.result.security?.vulnerabilities || [];
|
|
760
|
+
const crit = vulns.filter((v: any) => v.severity?.toLowerCase() === 'critical').length;
|
|
761
|
+
const high = vulns.filter((v: any) => v.severity?.toLowerCase() === 'high').length;
|
|
762
|
+
const med = vulns.filter((v: any) => v.severity?.toLowerCase() === 'medium').length;
|
|
763
|
+
const low = vulns.filter((v: any) => v.severity?.toLowerCase() === 'low').length;
|
|
764
|
+
return {
|
|
765
|
+
relativePath: r.relativePath,
|
|
766
|
+
critical: crit,
|
|
767
|
+
high,
|
|
768
|
+
medium: med,
|
|
769
|
+
low,
|
|
770
|
+
total: crit + high + med + low,
|
|
771
|
+
};
|
|
772
|
+
})
|
|
773
|
+
.filter((f) => f.total > 0);
|
|
774
|
+
|
|
661
775
|
if (filesWithProblems.length > 0) {
|
|
662
776
|
md += `
|
|
663
777
|
---
|
|
@@ -693,16 +807,28 @@ export function generateMarkdownReport(
|
|
|
693
807
|
|
|
694
808
|
/**
|
|
695
809
|
* Print brief summary for screen (when report is generated)
|
|
810
|
+
*
|
|
811
|
+
* IMPORTANT: Recalculates counts from actual filtered vulnerabilities.
|
|
696
812
|
*/
|
|
697
813
|
export function printBriefSummary(
|
|
698
814
|
results: FileScanResult[],
|
|
699
815
|
reportPath: string,
|
|
700
816
|
duration: number
|
|
701
817
|
): void {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
818
|
+
// Recalculate from actual vulnerabilities (post-filtering)
|
|
819
|
+
let totalCritical = 0;
|
|
820
|
+
let totalHigh = 0;
|
|
821
|
+
let totalMedium = 0;
|
|
822
|
+
let totalLow = 0;
|
|
823
|
+
|
|
824
|
+
results.forEach((r) => {
|
|
825
|
+
const vulns = r.result.security?.vulnerabilities || [];
|
|
826
|
+
totalCritical += vulns.filter((v: any) => v.severity?.toLowerCase() === 'critical').length;
|
|
827
|
+
totalHigh += vulns.filter((v: any) => v.severity?.toLowerCase() === 'high').length;
|
|
828
|
+
totalMedium += vulns.filter((v: any) => v.severity?.toLowerCase() === 'medium').length;
|
|
829
|
+
totalLow += vulns.filter((v: any) => v.severity?.toLowerCase() === 'low').length;
|
|
830
|
+
});
|
|
831
|
+
|
|
706
832
|
const totalVulns = totalCritical + totalHigh + totalMedium + totalLow;
|
|
707
833
|
|
|
708
834
|
console.log('');
|