codeslick-cli 1.2.2 → 1.2.4
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/__tests__/threshold-handler.test.ts +175 -0
- package/dist/packages/cli/src/commands/scan.d.ts +11 -0
- package/dist/packages/cli/src/commands/scan.d.ts.map +1 -1
- package/dist/packages/cli/src/commands/scan.js +74 -5
- package/dist/packages/cli/src/commands/scan.js.map +1 -1
- package/dist/packages/cli/src/config/config-loader.d.ts +11 -0
- package/dist/packages/cli/src/config/config-loader.d.ts.map +1 -1
- package/dist/packages/cli/src/config/config-loader.js.map +1 -1
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts +18 -0
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts.map +1 -1
- package/dist/packages/cli/src/reporters/cli-reporter.js +115 -0
- package/dist/packages/cli/src/reporters/cli-reporter.js.map +1 -1
- package/dist/packages/cli/src/utils/test-runner.d.ts +84 -0
- package/dist/packages/cli/src/utils/test-runner.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/test-runner.js +209 -0
- package/dist/packages/cli/src/utils/test-runner.js.map +1 -0
- package/dist/packages/cli/src/utils/threshold-handler.d.ts +40 -0
- package/dist/packages/cli/src/utils/threshold-handler.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/threshold-handler.js +85 -0
- package/dist/packages/cli/src/utils/threshold-handler.js.map +1 -0
- package/dist/src/lib/analyzers/go-analyzer.d.ts +5 -0
- package/dist/src/lib/analyzers/go-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/go-analyzer.js +47 -0
- package/dist/src/lib/analyzers/go-analyzer.js.map +1 -1
- 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 +48 -0
- package/dist/src/lib/analyzers/java-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts +5 -0
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.js +48 -0
- package/dist/src/lib/analyzers/javascript-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.d.ts +5 -0
- package/dist/src/lib/analyzers/python-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.js +55 -0
- package/dist/src/lib/analyzers/python-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/types.d.ts +4 -0
- package/dist/src/lib/analyzers/types.d.ts.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 +48 -0
- package/dist/src/lib/analyzers/typescript-analyzer.js.map +1 -1
- package/dist/src/lib/github/types.d.ts +112 -0
- package/dist/src/lib/github/types.d.ts.map +1 -0
- package/dist/src/lib/github/types.js +34 -0
- package/dist/src/lib/github/types.js.map +1 -0
- package/dist/src/lib/security/epss-service.d.ts +63 -0
- package/dist/src/lib/security/epss-service.d.ts.map +1 -0
- package/dist/src/lib/security/epss-service.js +256 -0
- package/dist/src/lib/security/epss-service.js.map +1 -0
- package/dist/src/lib/security/threshold-evaluator.d.ts +73 -0
- package/dist/src/lib/security/threshold-evaluator.d.ts.map +1 -0
- package/dist/src/lib/security/threshold-evaluator.js +234 -0
- package/dist/src/lib/security/threshold-evaluator.js.map +1 -0
- package/dist/src/lib/security/triage-service.d.ts +76 -0
- package/dist/src/lib/security/triage-service.d.ts.map +1 -0
- package/dist/src/lib/security/triage-service.js +318 -0
- package/dist/src/lib/security/triage-service.js.map +1 -0
- package/dist/src/lib/types/index.d.ts +4 -0
- package/dist/src/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/scan.ts +100 -7
- package/src/config/config-loader.ts +15 -0
- package/src/reporters/cli-reporter.ts +132 -0
- package/src/utils/test-runner.ts +249 -0
- package/src/utils/threshold-handler.ts +99 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* EPSS (Exploit Prediction Scoring System) Service
|
|
4
|
+
*
|
|
5
|
+
* Fetches exploit prediction scores from FIRST.org EPSS API
|
|
6
|
+
* Used for smart triage and vulnerability prioritization
|
|
7
|
+
*
|
|
8
|
+
* Feature 1 Phase 1 (Q1 2026): Alert Deduplication & AutoTriage
|
|
9
|
+
*
|
|
10
|
+
* API: https://api.first.org/data/v1/epss
|
|
11
|
+
* Docs: https://www.first.org/epss/api
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.getEPSSScores = getEPSSScores;
|
|
15
|
+
exports.getEPSSScore = getEPSSScore;
|
|
16
|
+
exports.clearEPSSCache = clearEPSSCache;
|
|
17
|
+
exports.getEPSSCacheStats = getEPSSCacheStats;
|
|
18
|
+
exports.interpretEPSSScore = interpretEPSSScore;
|
|
19
|
+
exports.getEPSSScoresBatch = getEPSSScoresBatch;
|
|
20
|
+
/**
|
|
21
|
+
* In-memory cache for EPSS scores
|
|
22
|
+
* TTL: 24 hours (EPSS data updates daily)
|
|
23
|
+
*/
|
|
24
|
+
const epssCache = new Map();
|
|
25
|
+
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
|
26
|
+
/**
|
|
27
|
+
* Rate limiting: Max 1 request per second to FIRST.org API
|
|
28
|
+
*/
|
|
29
|
+
let lastRequestTime = 0;
|
|
30
|
+
const MIN_REQUEST_INTERVAL = 1000; // 1 second
|
|
31
|
+
/**
|
|
32
|
+
* Fetch EPSS scores for given CVE IDs
|
|
33
|
+
*
|
|
34
|
+
* @param cveIds - Array of CVE IDs (e.g., ["CVE-2021-44228", "CVE-2022-12345"])
|
|
35
|
+
* @returns Array of EPSS scores (0 if not found or error)
|
|
36
|
+
*/
|
|
37
|
+
async function getEPSSScores(cveIds) {
|
|
38
|
+
console.log('[EPSS] getEPSSScores called with', cveIds.length, 'CVE IDs:', cveIds);
|
|
39
|
+
if (!cveIds || cveIds.length === 0) {
|
|
40
|
+
console.log('[EPSS] No CVE IDs provided, returning empty array');
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
// Filter out invalid CVE IDs and deduplicate
|
|
44
|
+
const validCveIds = [...new Set(cveIds.filter(isValidCveId))];
|
|
45
|
+
console.log('[EPSS] Valid CVE IDs after filtering:', validCveIds);
|
|
46
|
+
if (validCveIds.length === 0) {
|
|
47
|
+
console.log('[EPSS] No valid CVE IDs found, returning empty array');
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const results = [];
|
|
51
|
+
const cveIdsToFetch = [];
|
|
52
|
+
// Check cache first
|
|
53
|
+
for (const cveId of validCveIds) {
|
|
54
|
+
const cached = epssCache.get(cveId);
|
|
55
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
56
|
+
results.push({ ...cached.score, cached: true });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
cveIdsToFetch.push(cveId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// If all results are cached, return immediately
|
|
63
|
+
if (cveIdsToFetch.length === 0) {
|
|
64
|
+
return results;
|
|
65
|
+
}
|
|
66
|
+
// Fetch from API
|
|
67
|
+
try {
|
|
68
|
+
// Rate limiting: wait if needed
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const timeSinceLastRequest = now - lastRequestTime;
|
|
71
|
+
if (timeSinceLastRequest < MIN_REQUEST_INTERVAL) {
|
|
72
|
+
await new Promise(resolve => setTimeout(resolve, MIN_REQUEST_INTERVAL - timeSinceLastRequest));
|
|
73
|
+
}
|
|
74
|
+
lastRequestTime = Date.now();
|
|
75
|
+
// FIRST.org EPSS API supports bulk queries
|
|
76
|
+
// Format: ?cve=CVE-2021-44228,CVE-2022-12345
|
|
77
|
+
const cveParam = cveIdsToFetch.join(',');
|
|
78
|
+
const url = `https://api.first.org/data/v1/epss?cve=${encodeURIComponent(cveParam)}`;
|
|
79
|
+
const response = await fetch(url, {
|
|
80
|
+
method: 'GET',
|
|
81
|
+
headers: {
|
|
82
|
+
'Accept': 'application/json',
|
|
83
|
+
'User-Agent': 'CodeSlick/1.0 (Security Analysis Tool)',
|
|
84
|
+
},
|
|
85
|
+
// 10 second timeout
|
|
86
|
+
signal: AbortSignal.timeout(10000),
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
console.warn(`[EPSS] API returned ${response.status}: ${response.statusText}`);
|
|
90
|
+
// Return default scores for unfetched CVEs
|
|
91
|
+
return [
|
|
92
|
+
...results,
|
|
93
|
+
...cveIdsToFetch.map(cveId => createDefaultScore(cveId)),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
if (data.status !== 'OK') {
|
|
98
|
+
console.warn(`[EPSS] API status: ${data.status}`);
|
|
99
|
+
return [
|
|
100
|
+
...results,
|
|
101
|
+
...cveIdsToFetch.map(cveId => createDefaultScore(cveId)),
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
// Parse and cache results
|
|
105
|
+
for (const item of data.data) {
|
|
106
|
+
const score = {
|
|
107
|
+
cve: item.cve,
|
|
108
|
+
epssScore: parseFloat(item.epss),
|
|
109
|
+
percentile: parseFloat(item.percentile),
|
|
110
|
+
date: item.date,
|
|
111
|
+
cached: false,
|
|
112
|
+
};
|
|
113
|
+
// Cache the result
|
|
114
|
+
epssCache.set(item.cve, {
|
|
115
|
+
score,
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
});
|
|
118
|
+
results.push(score);
|
|
119
|
+
}
|
|
120
|
+
// Add default scores for CVEs not found in API response
|
|
121
|
+
const foundCves = new Set(data.data.map(item => item.cve.toUpperCase()));
|
|
122
|
+
for (const cveId of cveIdsToFetch) {
|
|
123
|
+
if (!foundCves.has(cveId.toUpperCase())) {
|
|
124
|
+
const defaultScore = createDefaultScore(cveId);
|
|
125
|
+
// Cache default score (shorter TTL)
|
|
126
|
+
epssCache.set(cveId, {
|
|
127
|
+
score: defaultScore,
|
|
128
|
+
timestamp: Date.now(),
|
|
129
|
+
});
|
|
130
|
+
results.push(defaultScore);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return results;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
console.error('[EPSS] Error fetching scores:', error);
|
|
137
|
+
// Return default scores on error
|
|
138
|
+
return [
|
|
139
|
+
...results,
|
|
140
|
+
...cveIdsToFetch.map(cveId => createDefaultScore(cveId)),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Fetch EPSS score for a single CVE
|
|
146
|
+
*
|
|
147
|
+
* @param cveId - CVE ID (e.g., "CVE-2021-44228")
|
|
148
|
+
* @returns EPSS score or null if not found
|
|
149
|
+
*/
|
|
150
|
+
async function getEPSSScore(cveId) {
|
|
151
|
+
const scores = await getEPSSScores([cveId]);
|
|
152
|
+
return scores.length > 0 ? scores[0] : null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Validate CVE ID format
|
|
156
|
+
*
|
|
157
|
+
* @param cveId - CVE ID to validate
|
|
158
|
+
* @returns true if valid format
|
|
159
|
+
*/
|
|
160
|
+
function isValidCveId(cveId) {
|
|
161
|
+
if (!cveId || typeof cveId !== 'string') {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
// CVE format: CVE-YYYY-NNNNN (year can be 1999-2999, number can be 1-99999)
|
|
165
|
+
return /^CVE-\d{4}-\d{1,7}$/i.test(cveId.trim());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create default EPSS score for CVEs not found in API
|
|
169
|
+
*
|
|
170
|
+
* @param cveId - CVE ID
|
|
171
|
+
* @returns Default EPSS score (0.0)
|
|
172
|
+
*/
|
|
173
|
+
function createDefaultScore(cveId) {
|
|
174
|
+
return {
|
|
175
|
+
cve: cveId,
|
|
176
|
+
epssScore: 0.0, // Assume low exploit probability if not in EPSS database
|
|
177
|
+
percentile: 0.0,
|
|
178
|
+
date: new Date().toISOString().split('T')[0],
|
|
179
|
+
cached: false,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear EPSS cache (useful for testing or manual refresh)
|
|
184
|
+
*/
|
|
185
|
+
function clearEPSSCache() {
|
|
186
|
+
epssCache.clear();
|
|
187
|
+
console.log('[EPSS] Cache cleared');
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get cache statistics
|
|
191
|
+
*/
|
|
192
|
+
function getEPSSCacheStats() {
|
|
193
|
+
let oldestTimestamp = null;
|
|
194
|
+
for (const entry of epssCache.values()) {
|
|
195
|
+
if (oldestTimestamp === null || entry.timestamp < oldestTimestamp) {
|
|
196
|
+
oldestTimestamp = entry.timestamp;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
size: epssCache.size,
|
|
201
|
+
oldestEntry: oldestTimestamp,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Interpret EPSS score for human-readable risk level
|
|
206
|
+
*
|
|
207
|
+
* @param epssScore - EPSS score (0.0-1.0)
|
|
208
|
+
* @returns Human-readable risk level
|
|
209
|
+
*/
|
|
210
|
+
function interpretEPSSScore(epssScore) {
|
|
211
|
+
if (epssScore >= 0.7) {
|
|
212
|
+
return {
|
|
213
|
+
risk: 'critical',
|
|
214
|
+
description: 'Very high probability of exploitation (top 30% of all CVEs)',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
else if (epssScore >= 0.3) {
|
|
218
|
+
return {
|
|
219
|
+
risk: 'high',
|
|
220
|
+
description: 'High probability of exploitation',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
else if (epssScore >= 0.1) {
|
|
224
|
+
return {
|
|
225
|
+
risk: 'medium',
|
|
226
|
+
description: 'Moderate probability of exploitation',
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
return {
|
|
231
|
+
risk: 'low',
|
|
232
|
+
description: 'Low probability of exploitation',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Batch fetch EPSS scores with chunking for large CVE lists
|
|
238
|
+
* FIRST.org API supports up to 100 CVEs per request
|
|
239
|
+
*
|
|
240
|
+
* @param cveIds - Array of CVE IDs
|
|
241
|
+
* @param chunkSize - Number of CVEs per API call (default: 50)
|
|
242
|
+
* @returns Array of EPSS scores
|
|
243
|
+
*/
|
|
244
|
+
async function getEPSSScoresBatch(cveIds, chunkSize = 50) {
|
|
245
|
+
const chunks = [];
|
|
246
|
+
for (let i = 0; i < cveIds.length; i += chunkSize) {
|
|
247
|
+
chunks.push(cveIds.slice(i, i + chunkSize));
|
|
248
|
+
}
|
|
249
|
+
const allScores = [];
|
|
250
|
+
for (const chunk of chunks) {
|
|
251
|
+
const scores = await getEPSSScores(chunk);
|
|
252
|
+
allScores.push(...scores);
|
|
253
|
+
}
|
|
254
|
+
return allScores;
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=epss-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epss-service.js","sourceRoot":"","sources":["../../../../../../src/lib/security/epss-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold Evaluator - Pass/Fail Security Gates
|
|
3
|
+
*
|
|
4
|
+
* Evaluates whether analysis results meet configured security thresholds.
|
|
5
|
+
* Used by:
|
|
6
|
+
* - GitHub PR checks (block merges)
|
|
7
|
+
* - CLI scans (exit codes)
|
|
8
|
+
* - Team dashboard (preview feature)
|
|
9
|
+
*
|
|
10
|
+
* Winter Roadmap WR2: Pass/Fail Thresholds
|
|
11
|
+
*/
|
|
12
|
+
import type { AggregatedResults } from '../github/types';
|
|
13
|
+
/**
|
|
14
|
+
* Threshold configuration (from team settings)
|
|
15
|
+
*/
|
|
16
|
+
export interface ThresholdConfig {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
blockOnCritical: boolean;
|
|
19
|
+
blockOnHigh: boolean;
|
|
20
|
+
maxVulnerabilities: number;
|
|
21
|
+
maxEpss: number;
|
|
22
|
+
exemptPaths: string[];
|
|
23
|
+
failureMessage?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Threshold evaluation result
|
|
27
|
+
*/
|
|
28
|
+
export interface ThresholdResult {
|
|
29
|
+
passed: boolean;
|
|
30
|
+
reason: string;
|
|
31
|
+
blockedBy: 'critical' | 'high' | 'count' | 'epss' | null;
|
|
32
|
+
vulnerabilityCount: number;
|
|
33
|
+
criticalCount: number;
|
|
34
|
+
highCount: number;
|
|
35
|
+
maxEpssFound: number;
|
|
36
|
+
exemptedCount: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Default threshold configuration
|
|
40
|
+
*/
|
|
41
|
+
export declare const DEFAULT_THRESHOLD_CONFIG: ThresholdConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Evaluate security thresholds against analysis results
|
|
44
|
+
*
|
|
45
|
+
* @param results - Aggregated analysis results
|
|
46
|
+
* @param config - Threshold configuration
|
|
47
|
+
* @returns Threshold evaluation result
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const result = evaluateThresholds(analysisResults, {
|
|
51
|
+
* enabled: true,
|
|
52
|
+
* blockOnCritical: true,
|
|
53
|
+
* blockOnHigh: false,
|
|
54
|
+
* maxVulnerabilities: 10,
|
|
55
|
+
* maxEpss: 70,
|
|
56
|
+
* exemptPaths: ['/test/ **', '** /*.test.ts'] // (remove spaces)
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* if (!result.passed) {
|
|
60
|
+
* console.log('Threshold failed:', result.reason);
|
|
61
|
+
* process.exit(1);
|
|
62
|
+
* }
|
|
63
|
+
*/
|
|
64
|
+
export declare function evaluateThresholds(results: AggregatedResults, config: ThresholdConfig): ThresholdResult;
|
|
65
|
+
/**
|
|
66
|
+
* Format threshold result as GitHub status message (140 char limit)
|
|
67
|
+
*/
|
|
68
|
+
export declare function formatGitHubStatusMessage(result: ThresholdResult, customMessage?: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Format threshold result for CLI output
|
|
71
|
+
*/
|
|
72
|
+
export declare function formatCLIOutput(result: ThresholdResult): string;
|
|
73
|
+
//# sourceMappingURL=threshold-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-evaluator.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/security/threshold-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACzD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAOtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,eAAe,GACtB,eAAe,CAqEjB;AA0GD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CASjG;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAqB/D"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Threshold Evaluator - Pass/Fail Security Gates
|
|
4
|
+
*
|
|
5
|
+
* Evaluates whether analysis results meet configured security thresholds.
|
|
6
|
+
* Used by:
|
|
7
|
+
* - GitHub PR checks (block merges)
|
|
8
|
+
* - CLI scans (exit codes)
|
|
9
|
+
* - Team dashboard (preview feature)
|
|
10
|
+
*
|
|
11
|
+
* Winter Roadmap WR2: Pass/Fail Thresholds
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.DEFAULT_THRESHOLD_CONFIG = void 0;
|
|
15
|
+
exports.evaluateThresholds = evaluateThresholds;
|
|
16
|
+
exports.formatGitHubStatusMessage = formatGitHubStatusMessage;
|
|
17
|
+
exports.formatCLIOutput = formatCLIOutput;
|
|
18
|
+
const minimatch_1 = require("minimatch");
|
|
19
|
+
/**
|
|
20
|
+
* Default threshold configuration
|
|
21
|
+
*/
|
|
22
|
+
exports.DEFAULT_THRESHOLD_CONFIG = {
|
|
23
|
+
enabled: false,
|
|
24
|
+
blockOnCritical: true,
|
|
25
|
+
blockOnHigh: false,
|
|
26
|
+
maxVulnerabilities: 50,
|
|
27
|
+
maxEpss: 70, // 70%
|
|
28
|
+
exemptPaths: [],
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Evaluate security thresholds against analysis results
|
|
32
|
+
*
|
|
33
|
+
* @param results - Aggregated analysis results
|
|
34
|
+
* @param config - Threshold configuration
|
|
35
|
+
* @returns Threshold evaluation result
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const result = evaluateThresholds(analysisResults, {
|
|
39
|
+
* enabled: true,
|
|
40
|
+
* blockOnCritical: true,
|
|
41
|
+
* blockOnHigh: false,
|
|
42
|
+
* maxVulnerabilities: 10,
|
|
43
|
+
* maxEpss: 70,
|
|
44
|
+
* exemptPaths: ['/test/ **', '** /*.test.ts'] // (remove spaces)
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* if (!result.passed) {
|
|
48
|
+
* console.log('Threshold failed:', result.reason);
|
|
49
|
+
* process.exit(1);
|
|
50
|
+
* }
|
|
51
|
+
*/
|
|
52
|
+
function evaluateThresholds(results, config) {
|
|
53
|
+
// If thresholds disabled, always pass
|
|
54
|
+
if (!config.enabled) {
|
|
55
|
+
return {
|
|
56
|
+
passed: true,
|
|
57
|
+
reason: 'Thresholds disabled',
|
|
58
|
+
blockedBy: null,
|
|
59
|
+
vulnerabilityCount: results.totalVulnerabilities,
|
|
60
|
+
criticalCount: results.criticalCount,
|
|
61
|
+
highCount: results.highCount,
|
|
62
|
+
maxEpssFound: 0,
|
|
63
|
+
exemptedCount: 0,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Filter vulnerabilities by exempt paths
|
|
67
|
+
const { filteredResults, exemptedCount } = filterExemptPaths(results, config.exemptPaths);
|
|
68
|
+
// Calculate max EPSS score found
|
|
69
|
+
const maxEpssFound = calculateMaxEpss(filteredResults);
|
|
70
|
+
// Build result object
|
|
71
|
+
const result = {
|
|
72
|
+
passed: true,
|
|
73
|
+
reason: '',
|
|
74
|
+
blockedBy: null,
|
|
75
|
+
vulnerabilityCount: filteredResults.totalVulnerabilities,
|
|
76
|
+
criticalCount: filteredResults.criticalCount,
|
|
77
|
+
highCount: filteredResults.highCount,
|
|
78
|
+
maxEpssFound,
|
|
79
|
+
exemptedCount,
|
|
80
|
+
};
|
|
81
|
+
// Check 1: Block on critical
|
|
82
|
+
if (config.blockOnCritical && filteredResults.criticalCount > 0) {
|
|
83
|
+
result.passed = false;
|
|
84
|
+
result.blockedBy = 'critical';
|
|
85
|
+
result.reason = `${filteredResults.criticalCount} critical vulnerabilit${filteredResults.criticalCount !== 1 ? 'ies' : 'y'} found`;
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
// Check 2: Block on high
|
|
89
|
+
if (config.blockOnHigh && filteredResults.highCount > 0) {
|
|
90
|
+
result.passed = false;
|
|
91
|
+
result.blockedBy = 'high';
|
|
92
|
+
result.reason = `${filteredResults.highCount} high priority vulnerabilit${filteredResults.highCount !== 1 ? 'ies' : 'y'} found`;
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
// Check 3: Max vulnerability count
|
|
96
|
+
if (config.maxVulnerabilities > 0 && filteredResults.totalVulnerabilities > config.maxVulnerabilities) {
|
|
97
|
+
result.passed = false;
|
|
98
|
+
result.blockedBy = 'count';
|
|
99
|
+
result.reason = `${filteredResults.totalVulnerabilities} vulnerabilities exceed limit of ${config.maxVulnerabilities}`;
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
// Check 4: EPSS threshold
|
|
103
|
+
if (config.maxEpss > 0 && maxEpssFound > config.maxEpss / 100) {
|
|
104
|
+
result.passed = false;
|
|
105
|
+
result.blockedBy = 'epss';
|
|
106
|
+
result.reason = `EPSS ${(maxEpssFound * 100).toFixed(1)}% exceeds threshold of ${config.maxEpss}%`;
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
// All checks passed
|
|
110
|
+
result.passed = true;
|
|
111
|
+
result.reason = formatPassReason(filteredResults);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Filter vulnerabilities by exempt paths (glob patterns)
|
|
116
|
+
*/
|
|
117
|
+
function filterExemptPaths(results, exemptPaths) {
|
|
118
|
+
// If no exempt paths, return original results
|
|
119
|
+
if (!exemptPaths || exemptPaths.length === 0) {
|
|
120
|
+
return { filteredResults: results, exemptedCount: 0 };
|
|
121
|
+
}
|
|
122
|
+
let exemptedCount = 0;
|
|
123
|
+
let criticalCount = 0;
|
|
124
|
+
let highCount = 0;
|
|
125
|
+
let mediumCount = 0;
|
|
126
|
+
let lowCount = 0;
|
|
127
|
+
// Filter file results
|
|
128
|
+
const filteredFileResults = results.fileResults.map(fileResult => {
|
|
129
|
+
const filteredVulnerabilities = fileResult.vulnerabilities.filter(vuln => {
|
|
130
|
+
// Check if file matches any exempt pattern
|
|
131
|
+
const isExempt = exemptPaths.some(pattern => (0, minimatch_1.minimatch)(fileResult.filename, pattern, { matchBase: true }));
|
|
132
|
+
if (isExempt) {
|
|
133
|
+
exemptedCount++;
|
|
134
|
+
return false; // Skip this vulnerability
|
|
135
|
+
}
|
|
136
|
+
// Count by severity
|
|
137
|
+
switch (vuln.severity) {
|
|
138
|
+
case 'critical':
|
|
139
|
+
criticalCount++;
|
|
140
|
+
break;
|
|
141
|
+
case 'high':
|
|
142
|
+
highCount++;
|
|
143
|
+
break;
|
|
144
|
+
case 'medium':
|
|
145
|
+
mediumCount++;
|
|
146
|
+
break;
|
|
147
|
+
case 'low':
|
|
148
|
+
lowCount++;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
return true; // Include this vulnerability
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
...fileResult,
|
|
155
|
+
vulnerabilities: filteredVulnerabilities,
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
// Build filtered results
|
|
159
|
+
const filteredResults = {
|
|
160
|
+
...results,
|
|
161
|
+
fileResults: filteredFileResults,
|
|
162
|
+
totalVulnerabilities: criticalCount + highCount + mediumCount + lowCount,
|
|
163
|
+
criticalCount,
|
|
164
|
+
highCount,
|
|
165
|
+
mediumCount,
|
|
166
|
+
lowCount,
|
|
167
|
+
};
|
|
168
|
+
return { filteredResults, exemptedCount };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Calculate maximum EPSS score from all vulnerabilities
|
|
172
|
+
*/
|
|
173
|
+
function calculateMaxEpss(results) {
|
|
174
|
+
let maxEpss = 0;
|
|
175
|
+
for (const fileResult of results.fileResults) {
|
|
176
|
+
for (const vuln of fileResult.vulnerabilities) {
|
|
177
|
+
if (vuln.epssScore !== undefined && vuln.epssScore > maxEpss) {
|
|
178
|
+
maxEpss = vuln.epssScore;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return maxEpss;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Format pass reason (summary of results)
|
|
186
|
+
*/
|
|
187
|
+
function formatPassReason(results) {
|
|
188
|
+
if (results.totalVulnerabilities === 0) {
|
|
189
|
+
return 'No security vulnerabilities found';
|
|
190
|
+
}
|
|
191
|
+
const parts = [];
|
|
192
|
+
if (results.criticalCount > 0)
|
|
193
|
+
parts.push(`${results.criticalCount} critical`);
|
|
194
|
+
if (results.highCount > 0)
|
|
195
|
+
parts.push(`${results.highCount} high`);
|
|
196
|
+
if (results.mediumCount > 0)
|
|
197
|
+
parts.push(`${results.mediumCount} medium`);
|
|
198
|
+
if (results.lowCount > 0)
|
|
199
|
+
parts.push(`${results.lowCount} low`);
|
|
200
|
+
return `${results.totalVulnerabilities} vulnerabilities found (${parts.join(', ')})`;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Format threshold result as GitHub status message (140 char limit)
|
|
204
|
+
*/
|
|
205
|
+
function formatGitHubStatusMessage(result, customMessage) {
|
|
206
|
+
if (!result.passed && customMessage) {
|
|
207
|
+
return customMessage.substring(0, 140);
|
|
208
|
+
}
|
|
209
|
+
const prefix = result.passed ? '✅' : '❌';
|
|
210
|
+
const message = `${prefix} ${result.reason}`;
|
|
211
|
+
return message.substring(0, 140);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Format threshold result for CLI output
|
|
215
|
+
*/
|
|
216
|
+
function formatCLIOutput(result) {
|
|
217
|
+
if (result.passed) {
|
|
218
|
+
return `\n✅ THRESHOLD PASSED\n${result.reason}\n`;
|
|
219
|
+
}
|
|
220
|
+
let output = `\n❌ THRESHOLD FAILED\n`;
|
|
221
|
+
output += `Reason: ${result.reason}\n`;
|
|
222
|
+
output += `\nVulnerabilities (after exemptions):\n`;
|
|
223
|
+
output += ` Critical: ${result.criticalCount}\n`;
|
|
224
|
+
output += ` High: ${result.highCount}\n`;
|
|
225
|
+
output += ` Total: ${result.vulnerabilityCount}\n`;
|
|
226
|
+
if (result.exemptedCount > 0) {
|
|
227
|
+
output += `\nExempted: ${result.exemptedCount} vulnerabilities in exempt paths\n`;
|
|
228
|
+
}
|
|
229
|
+
if (result.maxEpssFound > 0) {
|
|
230
|
+
output += `Max EPSS: ${(result.maxEpssFound * 100).toFixed(1)}%\n`;
|
|
231
|
+
}
|
|
232
|
+
return output;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=threshold-evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-evaluator.js","sourceRoot":"","sources":["../../../../../../src/lib/security/threshold-evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Triage Service
|
|
3
|
+
*
|
|
4
|
+
* Computes priority scores for security issues using multiple signals:
|
|
5
|
+
* - CVSS Score (base severity)
|
|
6
|
+
* - EPSS Score (exploit prediction)
|
|
7
|
+
* - OWASP Category (criticality weight)
|
|
8
|
+
* - Environment Context (production vs dev)
|
|
9
|
+
*
|
|
10
|
+
* Feature 1 Phase 1 (Q1 2026): Alert Deduplication & AutoTriage
|
|
11
|
+
*
|
|
12
|
+
* Priority Formula:
|
|
13
|
+
* priority = cvss * 0.5 + epss * 0.3 + owasp_weight * 0.2
|
|
14
|
+
*/
|
|
15
|
+
import { SecurityVulnerability } from '../analyzers/types';
|
|
16
|
+
/**
|
|
17
|
+
* Environment context for triage
|
|
18
|
+
* Production issues get higher priority
|
|
19
|
+
*/
|
|
20
|
+
export interface EnvironmentContext {
|
|
21
|
+
isProduction?: boolean;
|
|
22
|
+
hasPublicExposure?: boolean;
|
|
23
|
+
hasSensitiveData?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Triage configuration
|
|
27
|
+
*/
|
|
28
|
+
export interface TriageConfig {
|
|
29
|
+
cvssWeight?: number;
|
|
30
|
+
epssWeight?: number;
|
|
31
|
+
owaspWeight?: number;
|
|
32
|
+
environmentContext?: EnvironmentContext;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Triage result with priority and reasoning
|
|
36
|
+
*/
|
|
37
|
+
export interface TriageResult {
|
|
38
|
+
issue: SecurityVulnerability;
|
|
39
|
+
priorityScore: number;
|
|
40
|
+
priority: 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
41
|
+
triageReason: string;
|
|
42
|
+
epssScore?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Triage multiple security issues with smart prioritization
|
|
46
|
+
*
|
|
47
|
+
* @param issues - Array of security issues to triage
|
|
48
|
+
* @param config - Triage configuration
|
|
49
|
+
* @returns Array of triage results sorted by priority (highest first)
|
|
50
|
+
*/
|
|
51
|
+
export declare function triageSecurityIssues(issues: SecurityVulnerability[], config?: TriageConfig): Promise<TriageResult[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Get triage statistics for a set of results
|
|
54
|
+
*
|
|
55
|
+
* @param results - Triage results
|
|
56
|
+
* @returns Triage statistics
|
|
57
|
+
*/
|
|
58
|
+
export declare function getTriageStats(results: TriageResult[]): {
|
|
59
|
+
total: number;
|
|
60
|
+
critical: number;
|
|
61
|
+
high: number;
|
|
62
|
+
medium: number;
|
|
63
|
+
low: number;
|
|
64
|
+
info: number;
|
|
65
|
+
withEPSS: number;
|
|
66
|
+
averagePriority: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Filter triage results by minimum priority level
|
|
70
|
+
*
|
|
71
|
+
* @param results - Triage results
|
|
72
|
+
* @param minPriority - Minimum priority level ('critical' | 'high' | 'medium' | 'low')
|
|
73
|
+
* @returns Filtered results
|
|
74
|
+
*/
|
|
75
|
+
export declare function filterByPriority(results: TriageResult[], minPriority: 'critical' | 'high' | 'medium' | 'low'): TriageResult[];
|
|
76
|
+
//# sourceMappingURL=triage-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-service.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/security/triage-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AA4B3D;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,qBAAqB,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkOD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,qBAAqB,EAAE,EAC/B,MAAM,GAAE,YAAiB,GACxB,OAAO,CAAC,YAAY,EAAE,CAAC,CA6CzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB,CAyBA;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EAAE,EACvB,WAAW,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAClD,YAAY,EAAE,CAKhB"}
|