api-tests-coverage 1.0.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 +703 -0
- package/config.yaml.example +227 -0
- package/dist/action/src/index.d.ts +2 -0
- package/dist/action/src/index.d.ts.map +1 -0
- package/dist/action/src/index.js +349 -0
- package/dist/action/src/prComment.d.ts +34 -0
- package/dist/action/src/prComment.d.ts.map +1 -0
- package/dist/action/src/prComment.js +146 -0
- package/dist/src/ast/astAnalysisOrchestrator.d.ts +36 -0
- package/dist/src/ast/astAnalysisOrchestrator.d.ts.map +1 -0
- package/dist/src/ast/astAnalysisOrchestrator.js +123 -0
- package/dist/src/ast/astTypes.d.ts +105 -0
- package/dist/src/ast/astTypes.d.ts.map +1 -0
- package/dist/src/ast/astTypes.js +9 -0
- package/dist/src/ast/languageAnalyzer.d.ts +46 -0
- package/dist/src/ast/languageAnalyzer.d.ts.map +1 -0
- package/dist/src/ast/languageAnalyzer.js +9 -0
- package/dist/src/ast/languageCapabilities.d.ts +24 -0
- package/dist/src/ast/languageCapabilities.d.ts.map +1 -0
- package/dist/src/ast/languageCapabilities.js +92 -0
- package/dist/src/ast/parseFile.d.ts +16 -0
- package/dist/src/ast/parseFile.d.ts.map +1 -0
- package/dist/src/ast/parseFile.js +65 -0
- package/dist/src/ast/parserRegistry.d.ts +39 -0
- package/dist/src/ast/parserRegistry.d.ts.map +1 -0
- package/dist/src/ast/parserRegistry.js +66 -0
- package/dist/src/buildSummary.d.ts +26 -0
- package/dist/src/buildSummary.d.ts.map +1 -0
- package/dist/src/buildSummary.js +193 -0
- package/dist/src/businessCoverage.d.ts +68 -0
- package/dist/src/businessCoverage.d.ts.map +1 -0
- package/dist/src/businessCoverage.js +290 -0
- package/dist/src/compatibilityCoverage.d.ts +83 -0
- package/dist/src/compatibilityCoverage.d.ts.map +1 -0
- package/dist/src/compatibilityCoverage.js +501 -0
- package/dist/src/config/defaultConfig.d.ts +9 -0
- package/dist/src/config/defaultConfig.d.ts.map +1 -0
- package/dist/src/config/defaultConfig.js +97 -0
- package/dist/src/config/index.d.ts +12 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +37 -0
- package/dist/src/config/loadConfig.d.ts +29 -0
- package/dist/src/config/loadConfig.d.ts.map +1 -0
- package/dist/src/config/loadConfig.js +135 -0
- package/dist/src/config/mergeConfig.d.ts +15 -0
- package/dist/src/config/mergeConfig.d.ts.map +1 -0
- package/dist/src/config/mergeConfig.js +57 -0
- package/dist/src/config/schema.d.ts +15 -0
- package/dist/src/config/schema.d.ts.map +1 -0
- package/dist/src/config/schema.js +30 -0
- package/dist/src/config/types.d.ts +175 -0
- package/dist/src/config/types.d.ts.map +1 -0
- package/dist/src/config/types.js +9 -0
- package/dist/src/config/validateConfig.d.ts +22 -0
- package/dist/src/config/validateConfig.d.ts.map +1 -0
- package/dist/src/config/validateConfig.js +171 -0
- package/dist/src/config.d.ts +168 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +204 -0
- package/dist/src/coverage/deep-analysis/callGraph.d.ts +67 -0
- package/dist/src/coverage/deep-analysis/callGraph.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/callGraph.js +275 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.d.ts +23 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/deepEndpointResolver.js +394 -0
- package/dist/src/coverage/deep-analysis/index.d.ts +17 -0
- package/dist/src/coverage/deep-analysis/index.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/index.js +63 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.d.ts +60 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveAssertions.js +121 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.d.ts +36 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveConstants.js +92 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.d.ts +55 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveEnums.js +152 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.d.ts +70 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveMethodChains.js +152 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.d.ts +80 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolvePaths.js +216 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.d.ts +71 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/resolveRequestWrappers.js +226 -0
- package/dist/src/coverage/deep-analysis/symbolTable.d.ts +58 -0
- package/dist/src/coverage/deep-analysis/symbolTable.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/symbolTable.js +230 -0
- package/dist/src/coverage/deep-analysis/types.d.ts +122 -0
- package/dist/src/coverage/deep-analysis/types.d.ts.map +1 -0
- package/dist/src/coverage/deep-analysis/types.js +21 -0
- package/dist/src/discovery/fileClassifier.d.ts +50 -0
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -0
- package/dist/src/discovery/fileClassifier.js +238 -0
- package/dist/src/discovery/projectDiscovery.d.ts +66 -0
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -0
- package/dist/src/discovery/projectDiscovery.js +287 -0
- package/dist/src/endpointCoverage.d.ts +70 -0
- package/dist/src/endpointCoverage.d.ts.map +1 -0
- package/dist/src/endpointCoverage.js +381 -0
- package/dist/src/errorCoverage.d.ts +93 -0
- package/dist/src/errorCoverage.d.ts.map +1 -0
- package/dist/src/errorCoverage.js +698 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +1441 -0
- package/dist/src/inference/businessRuleInference.d.ts +63 -0
- package/dist/src/inference/businessRuleInference.d.ts.map +1 -0
- package/dist/src/inference/businessRuleInference.js +268 -0
- package/dist/src/inference/integrationFlowInference.d.ts +56 -0
- package/dist/src/inference/integrationFlowInference.d.ts.map +1 -0
- package/dist/src/inference/integrationFlowInference.js +266 -0
- package/dist/src/integrationCoverage.d.ts +72 -0
- package/dist/src/integrationCoverage.d.ts.map +1 -0
- package/dist/src/integrationCoverage.js +317 -0
- package/dist/src/intelligence/index.d.ts +20 -0
- package/dist/src/intelligence/index.d.ts.map +1 -0
- package/dist/src/intelligence/index.js +105 -0
- package/dist/src/intelligence/linkageEngine.d.ts +20 -0
- package/dist/src/intelligence/linkageEngine.d.ts.map +1 -0
- package/dist/src/intelligence/linkageEngine.js +522 -0
- package/dist/src/intelligence/markdownReporter.d.ts +12 -0
- package/dist/src/intelligence/markdownReporter.d.ts.map +1 -0
- package/dist/src/intelligence/markdownReporter.js +265 -0
- package/dist/src/intelligence/riskScoring.d.ts +53 -0
- package/dist/src/intelligence/riskScoring.d.ts.map +1 -0
- package/dist/src/intelligence/riskScoring.js +181 -0
- package/dist/src/intelligence/types.d.ts +121 -0
- package/dist/src/intelligence/types.d.ts.map +1 -0
- package/dist/src/intelligence/types.js +8 -0
- package/dist/src/languageDetection.d.ts +100 -0
- package/dist/src/languageDetection.d.ts.map +1 -0
- package/dist/src/languageDetection.js +349 -0
- package/dist/src/languages/java/index.d.ts +16 -0
- package/dist/src/languages/java/index.d.ts.map +1 -0
- package/dist/src/languages/java/index.js +103 -0
- package/dist/src/languages/java/parser.d.ts +7 -0
- package/dist/src/languages/java/parser.d.ts.map +1 -0
- package/dist/src/languages/java/parser.js +50 -0
- package/dist/src/languages/java/semanticBuilder.d.ts +21 -0
- package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -0
- package/dist/src/languages/java/semanticBuilder.js +358 -0
- package/dist/src/languages/javascript/annotationExtractor.d.ts +20 -0
- package/dist/src/languages/javascript/annotationExtractor.d.ts.map +1 -0
- package/dist/src/languages/javascript/annotationExtractor.js +94 -0
- package/dist/src/languages/javascript/assertionResolver.d.ts +18 -0
- package/dist/src/languages/javascript/assertionResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/assertionResolver.js +150 -0
- package/dist/src/languages/javascript/callResolver.d.ts +23 -0
- package/dist/src/languages/javascript/callResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/callResolver.js +236 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.d.ts +23 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.d.ts.map +1 -0
- package/dist/src/languages/javascript/httpInteractionExtractor.js +205 -0
- package/dist/src/languages/javascript/index.d.ts +20 -0
- package/dist/src/languages/javascript/index.d.ts.map +1 -0
- package/dist/src/languages/javascript/index.js +136 -0
- package/dist/src/languages/javascript/parser.d.ts +14 -0
- package/dist/src/languages/javascript/parser.d.ts.map +1 -0
- package/dist/src/languages/javascript/parser.js +38 -0
- package/dist/src/languages/javascript/symbolResolver.d.ts +31 -0
- package/dist/src/languages/javascript/symbolResolver.d.ts.map +1 -0
- package/dist/src/languages/javascript/symbolResolver.js +183 -0
- package/dist/src/languages/kotlin/index.d.ts +16 -0
- package/dist/src/languages/kotlin/index.d.ts.map +1 -0
- package/dist/src/languages/kotlin/index.js +151 -0
- package/dist/src/languages/kotlin/parser.d.ts +11 -0
- package/dist/src/languages/kotlin/parser.d.ts.map +1 -0
- package/dist/src/languages/kotlin/parser.js +74 -0
- package/dist/src/languages/python/index.d.ts +15 -0
- package/dist/src/languages/python/index.d.ts.map +1 -0
- package/dist/src/languages/python/index.js +293 -0
- package/dist/src/languages/ruby/index.d.ts +15 -0
- package/dist/src/languages/ruby/index.d.ts.map +1 -0
- package/dist/src/languages/ruby/index.js +274 -0
- package/dist/src/languages/shared/treeSitterUtils.d.ts +43 -0
- package/dist/src/languages/shared/treeSitterUtils.d.ts.map +1 -0
- package/dist/src/languages/shared/treeSitterUtils.js +100 -0
- package/dist/src/languages/typescript/index.d.ts +14 -0
- package/dist/src/languages/typescript/index.d.ts.map +1 -0
- package/dist/src/languages/typescript/index.js +25 -0
- package/dist/src/lib/index.d.ts +228 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +486 -0
- package/dist/src/mcp/client/index.d.ts +37 -0
- package/dist/src/mcp/client/index.d.ts.map +1 -0
- package/dist/src/mcp/client/index.js +235 -0
- package/dist/src/mcp/config.d.ts +50 -0
- package/dist/src/mcp/config.d.ts.map +1 -0
- package/dist/src/mcp/config.js +125 -0
- package/dist/src/mcp/events.d.ts +24 -0
- package/dist/src/mcp/events.d.ts.map +1 -0
- package/dist/src/mcp/events.js +48 -0
- package/dist/src/mcp/fallback/index.d.ts +50 -0
- package/dist/src/mcp/fallback/index.d.ts.map +1 -0
- package/dist/src/mcp/fallback/index.js +216 -0
- package/dist/src/mcp/index.d.ts +67 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +212 -0
- package/dist/src/mcp/normalizer.d.ts +21 -0
- package/dist/src/mcp/normalizer.d.ts.map +1 -0
- package/dist/src/mcp/normalizer.js +99 -0
- package/dist/src/mcp/prompts/index.d.ts +86 -0
- package/dist/src/mcp/prompts/index.d.ts.map +1 -0
- package/dist/src/mcp/prompts/index.js +304 -0
- package/dist/src/mcp/templates/index.d.ts +35 -0
- package/dist/src/mcp/templates/index.d.ts.map +1 -0
- package/dist/src/mcp/templates/index.js +143 -0
- package/dist/src/mcp/testing/mock-server/index.d.ts +47 -0
- package/dist/src/mcp/testing/mock-server/index.d.ts.map +1 -0
- package/dist/src/mcp/testing/mock-server/index.js +157 -0
- package/dist/src/mcp/types.d.ts +127 -0
- package/dist/src/mcp/types.d.ts.map +1 -0
- package/dist/src/mcp/types.js +8 -0
- package/dist/src/observability.d.ts +138 -0
- package/dist/src/observability.d.ts.map +1 -0
- package/dist/src/observability.js +519 -0
- package/dist/src/parameterCoverage.d.ts +75 -0
- package/dist/src/parameterCoverage.d.ts.map +1 -0
- package/dist/src/parameterCoverage.js +629 -0
- package/dist/src/perfResilienceCoverage.d.ts +155 -0
- package/dist/src/perfResilienceCoverage.d.ts.map +1 -0
- package/dist/src/perfResilienceCoverage.js +670 -0
- package/dist/src/pluginLoader.d.ts +51 -0
- package/dist/src/pluginLoader.d.ts.map +1 -0
- package/dist/src/pluginLoader.js +72 -0
- package/dist/src/publishing.d.ts +63 -0
- package/dist/src/publishing.d.ts.map +1 -0
- package/dist/src/publishing.js +379 -0
- package/dist/src/qualityGate.d.ts +58 -0
- package/dist/src/qualityGate.d.ts.map +1 -0
- package/dist/src/qualityGate.js +118 -0
- package/dist/src/reporting.d.ts +41 -0
- package/dist/src/reporting.d.ts.map +1 -0
- package/dist/src/reporting.js +278 -0
- package/dist/src/screenshots.d.ts +71 -0
- package/dist/src/screenshots.d.ts.map +1 -0
- package/dist/src/screenshots.js +141 -0
- package/dist/src/security/gate/index.d.ts +11 -0
- package/dist/src/security/gate/index.d.ts.map +1 -0
- package/dist/src/security/gate/index.js +65 -0
- package/dist/src/security/index.d.ts +30 -0
- package/dist/src/security/index.d.ts.map +1 -0
- package/dist/src/security/index.js +342 -0
- package/dist/src/security/normalizers/semgrep.d.ts +10 -0
- package/dist/src/security/normalizers/semgrep.d.ts.map +1 -0
- package/dist/src/security/normalizers/semgrep.js +104 -0
- package/dist/src/security/normalizers/trivy.d.ts +10 -0
- package/dist/src/security/normalizers/trivy.d.ts.map +1 -0
- package/dist/src/security/normalizers/trivy.js +78 -0
- package/dist/src/security/normalizers/zap.d.ts +10 -0
- package/dist/src/security/normalizers/zap.d.ts.map +1 -0
- package/dist/src/security/normalizers/zap.js +104 -0
- package/dist/src/security/scanners/semgrep.d.ts +6 -0
- package/dist/src/security/scanners/semgrep.d.ts.map +1 -0
- package/dist/src/security/scanners/semgrep.js +125 -0
- package/dist/src/security/scanners/trivy.d.ts +6 -0
- package/dist/src/security/scanners/trivy.d.ts.map +1 -0
- package/dist/src/security/scanners/trivy.js +115 -0
- package/dist/src/security/scanners/zap.d.ts +6 -0
- package/dist/src/security/scanners/zap.d.ts.map +1 -0
- package/dist/src/security/scanners/zap.js +135 -0
- package/dist/src/security/types.d.ts +146 -0
- package/dist/src/security/types.d.ts.map +1 -0
- package/dist/src/security/types.js +6 -0
- package/dist/src/securityCoverage.d.ts +116 -0
- package/dist/src/securityCoverage.d.ts.map +1 -0
- package/dist/src/securityCoverage.js +725 -0
- package/dist/src/summary/buildSummary.d.ts +28 -0
- package/dist/src/summary/buildSummary.d.ts.map +1 -0
- package/dist/src/summary/buildSummary.js +257 -0
- package/dist/src/summary/evaluateMetrics.d.ts +31 -0
- package/dist/src/summary/evaluateMetrics.d.ts.map +1 -0
- package/dist/src/summary/evaluateMetrics.js +118 -0
- package/dist/src/summary/index.d.ts +10 -0
- package/dist/src/summary/index.d.ts.map +1 -0
- package/dist/src/summary/index.js +22 -0
- package/dist/src/summary/markdownRenderer.d.ts +139 -0
- package/dist/src/summary/markdownRenderer.d.ts.map +1 -0
- package/dist/src/summary/markdownRenderer.js +459 -0
- package/dist/src/summary/prSummary.d.ts +24 -0
- package/dist/src/summary/prSummary.d.ts.map +1 -0
- package/dist/src/summary/prSummary.js +233 -0
- package/dist/src/summary/summaryTypes.d.ts +35 -0
- package/dist/src/summary/summaryTypes.d.ts.map +1 -0
- package/dist/src/summary/summaryTypes.js +27 -0
- package/package.json +84 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evaluateSecurityGate = evaluateSecurityGate;
|
|
4
|
+
// ─── Gate evaluation ──────────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Count findings matching specific criteria.
|
|
7
|
+
*/
|
|
8
|
+
function countFindings(findings, predicate) {
|
|
9
|
+
return findings.filter(predicate).length;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Evaluate the security gate against normalized findings.
|
|
13
|
+
* Returns a SecurityGateResult with pass/fail status and detailed reasons.
|
|
14
|
+
*/
|
|
15
|
+
function evaluateSecurityGate(findings, config) {
|
|
16
|
+
const reasons = [];
|
|
17
|
+
const counts = {
|
|
18
|
+
critical: countFindings(findings, (f) => f.severity === 'CRITICAL'),
|
|
19
|
+
high: countFindings(findings, (f) => f.severity === 'HIGH'),
|
|
20
|
+
medium: countFindings(findings, (f) => f.severity === 'MEDIUM'),
|
|
21
|
+
low: countFindings(findings, (f) => f.severity === 'LOW'),
|
|
22
|
+
secrets: countFindings(findings, (f) => f.category === 'secret'),
|
|
23
|
+
misconfigHigh: countFindings(findings, (f) => f.category === 'misconfig' && (f.severity === 'HIGH' || f.severity === 'CRITICAL')),
|
|
24
|
+
criticalVulns: countFindings(findings, (f) => f.category === 'sca' && f.severity === 'CRITICAL'),
|
|
25
|
+
highVulns: countFindings(findings, (f) => f.category === 'sca' && f.severity === 'HIGH'),
|
|
26
|
+
};
|
|
27
|
+
// ── failOnCritical ────────────────────────────────────────────────────────
|
|
28
|
+
if (config.failOnCritical && counts.critical > 0) {
|
|
29
|
+
reasons.push(`${counts.critical} CRITICAL finding(s) found (failOnCritical=true)`);
|
|
30
|
+
}
|
|
31
|
+
// ── failOnHigh ────────────────────────────────────────────────────────────
|
|
32
|
+
if (config.failOnHigh && counts.high > 0) {
|
|
33
|
+
reasons.push(`${counts.high} HIGH finding(s) found (failOnHigh=true)`);
|
|
34
|
+
}
|
|
35
|
+
// ── maxMedium ─────────────────────────────────────────────────────────────
|
|
36
|
+
if (config.maxMedium !== undefined && counts.medium > config.maxMedium) {
|
|
37
|
+
reasons.push(`${counts.medium} MEDIUM finding(s) found, exceeds maxMedium=${config.maxMedium}`);
|
|
38
|
+
}
|
|
39
|
+
// ── maxLow ────────────────────────────────────────────────────────────────
|
|
40
|
+
if (config.maxLow !== undefined && counts.low > config.maxLow) {
|
|
41
|
+
reasons.push(`${counts.low} LOW finding(s) found, exceeds maxLow=${config.maxLow}`);
|
|
42
|
+
}
|
|
43
|
+
// ── maxSecrets ────────────────────────────────────────────────────────────
|
|
44
|
+
if (config.maxSecrets !== undefined && counts.secrets > config.maxSecrets) {
|
|
45
|
+
reasons.push(`${counts.secrets} secret(s) found, exceeds maxSecrets=${config.maxSecrets}`);
|
|
46
|
+
}
|
|
47
|
+
// ── maxMisconfigHigh ─────────────────────────────────────────────────────
|
|
48
|
+
if (config.maxMisconfigHigh !== undefined && counts.misconfigHigh > config.maxMisconfigHigh) {
|
|
49
|
+
reasons.push(`${counts.misconfigHigh} HIGH/CRITICAL misconfiguration(s) found, exceeds maxMisconfigHigh=${config.maxMisconfigHigh}`);
|
|
50
|
+
}
|
|
51
|
+
// ── maxCriticalVulns ─────────────────────────────────────────────────────
|
|
52
|
+
if (config.maxCriticalVulns !== undefined && counts.criticalVulns > config.maxCriticalVulns) {
|
|
53
|
+
reasons.push(`${counts.criticalVulns} CRITICAL vulnerability(ies) found, exceeds maxCriticalVulns=${config.maxCriticalVulns}`);
|
|
54
|
+
}
|
|
55
|
+
// ── maxHighVulns ─────────────────────────────────────────────────────────
|
|
56
|
+
if (config.maxHighVulns !== undefined && counts.highVulns > config.maxHighVulns) {
|
|
57
|
+
reasons.push(`${counts.highVulns} HIGH vulnerability(ies) found, exceeds maxHighVulns=${config.maxHighVulns}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
passed: reasons.length === 0,
|
|
61
|
+
reasons,
|
|
62
|
+
thresholds: config,
|
|
63
|
+
counts,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SecurityScanConfig, SecurityScanSummary, ScannerResult } from './types';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export { normalizeSemgrepOutput } from './normalizers/semgrep';
|
|
4
|
+
export { normalizeTrivyOutput } from './normalizers/trivy';
|
|
5
|
+
export { normalizeZapOutput } from './normalizers/zap';
|
|
6
|
+
export { evaluateSecurityGate } from './gate/index';
|
|
7
|
+
/**
|
|
8
|
+
* Run all configured security scanners and collect findings.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runSecurityScanners(config: SecurityScanConfig): Promise<ScannerResult[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Build a SecurityScanSummary from scanner results.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildSecurityScanSummary(results: ScannerResult[], config: SecurityScanConfig): SecurityScanSummary;
|
|
15
|
+
/**
|
|
16
|
+
* Generate all security scan reports in the given directory.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateSecurityScanReports(summary: SecurityScanSummary, reportsDir: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Run the complete security scan workflow:
|
|
21
|
+
* 1. Execute configured scanners
|
|
22
|
+
* 2. Normalize findings
|
|
23
|
+
* 3. Build summary
|
|
24
|
+
* 4. Evaluate gate
|
|
25
|
+
* 5. Generate reports
|
|
26
|
+
*
|
|
27
|
+
* Returns the SecurityScanSummary for further use.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runSecurityScan(config: SecurityScanConfig, reportsDir?: string): Promise<SecurityScanSummary>;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/security/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EAKnB,aAAa,EACd,MAAM,SAAS,CAAC;AAQjB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAIpD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,EAAE,CAAC,CAkB1B;AAID;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,aAAa,EAAE,EACxB,MAAM,EAAE,kBAAkB,GACzB,mBAAmB,CAoDrB;AAID;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,mBAAmB,EAC5B,UAAU,EAAE,MAAM,GACjB,IAAI,CA2EN;AAuKD;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,kBAAkB,EAC1B,UAAU,GAAE,MAAkB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CAM9B"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
36
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.evaluateSecurityGate = exports.normalizeZapOutput = exports.normalizeTrivyOutput = exports.normalizeSemgrepOutput = void 0;
|
|
40
|
+
exports.runSecurityScanners = runSecurityScanners;
|
|
41
|
+
exports.buildSecurityScanSummary = buildSecurityScanSummary;
|
|
42
|
+
exports.generateSecurityScanReports = generateSecurityScanReports;
|
|
43
|
+
exports.runSecurityScan = runSecurityScan;
|
|
44
|
+
/**
|
|
45
|
+
* Security scanning orchestrator.
|
|
46
|
+
* Coordinates scanner execution, normalization, report generation,
|
|
47
|
+
* and security gate evaluation.
|
|
48
|
+
*/
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const semgrep_1 = require("./scanners/semgrep");
|
|
52
|
+
const trivy_1 = require("./scanners/trivy");
|
|
53
|
+
const zap_1 = require("./scanners/zap");
|
|
54
|
+
const index_1 = require("./gate/index");
|
|
55
|
+
// ─── Re-exports ───────────────────────────────────────────────────────────────
|
|
56
|
+
__exportStar(require("./types"), exports);
|
|
57
|
+
var semgrep_2 = require("./normalizers/semgrep");
|
|
58
|
+
Object.defineProperty(exports, "normalizeSemgrepOutput", { enumerable: true, get: function () { return semgrep_2.normalizeSemgrepOutput; } });
|
|
59
|
+
var trivy_2 = require("./normalizers/trivy");
|
|
60
|
+
Object.defineProperty(exports, "normalizeTrivyOutput", { enumerable: true, get: function () { return trivy_2.normalizeTrivyOutput; } });
|
|
61
|
+
var zap_2 = require("./normalizers/zap");
|
|
62
|
+
Object.defineProperty(exports, "normalizeZapOutput", { enumerable: true, get: function () { return zap_2.normalizeZapOutput; } });
|
|
63
|
+
var index_2 = require("./gate/index");
|
|
64
|
+
Object.defineProperty(exports, "evaluateSecurityGate", { enumerable: true, get: function () { return index_2.evaluateSecurityGate; } });
|
|
65
|
+
// ─── Scanner orchestration ────────────────────────────────────────────────────
|
|
66
|
+
/**
|
|
67
|
+
* Run all configured security scanners and collect findings.
|
|
68
|
+
*/
|
|
69
|
+
async function runSecurityScanners(config) {
|
|
70
|
+
var _a, _b, _c, _d, _e;
|
|
71
|
+
const workspace = path.resolve((_a = config.workspace) !== null && _a !== void 0 ? _a : '.');
|
|
72
|
+
const results = [];
|
|
73
|
+
const scanners = (_b = config.scanners) !== null && _b !== void 0 ? _b : {};
|
|
74
|
+
if ((_c = scanners.semgrep) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
75
|
+
results.push(await (0, semgrep_1.runSemgrep)(scanners.semgrep, workspace));
|
|
76
|
+
}
|
|
77
|
+
if ((_d = scanners.trivy) === null || _d === void 0 ? void 0 : _d.enabled) {
|
|
78
|
+
results.push(await (0, trivy_1.runTrivy)(scanners.trivy, workspace));
|
|
79
|
+
}
|
|
80
|
+
if ((_e = scanners.zap) === null || _e === void 0 ? void 0 : _e.enabled) {
|
|
81
|
+
results.push(await (0, zap_1.runZap)(scanners.zap));
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
// ─── Summary builder ──────────────────────────────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Build a SecurityScanSummary from scanner results.
|
|
88
|
+
*/
|
|
89
|
+
function buildSecurityScanSummary(results, config) {
|
|
90
|
+
var _a, _b, _c;
|
|
91
|
+
const allFindings = results.flatMap((r) => r.findings);
|
|
92
|
+
const scannersRun = results
|
|
93
|
+
.filter((r) => r.success || r.findings.length > 0)
|
|
94
|
+
.map((r) => r.scanner);
|
|
95
|
+
const bySeverity = {
|
|
96
|
+
LOW: 0,
|
|
97
|
+
MEDIUM: 0,
|
|
98
|
+
HIGH: 0,
|
|
99
|
+
CRITICAL: 0,
|
|
100
|
+
};
|
|
101
|
+
const byCategory = {
|
|
102
|
+
sast: 0,
|
|
103
|
+
sca: 0,
|
|
104
|
+
secret: 0,
|
|
105
|
+
misconfig: 0,
|
|
106
|
+
dast: 0,
|
|
107
|
+
auth: 0,
|
|
108
|
+
injection: 0,
|
|
109
|
+
'data-exposure': 0,
|
|
110
|
+
crypto: 0,
|
|
111
|
+
unknown: 0,
|
|
112
|
+
};
|
|
113
|
+
const byScanner = {
|
|
114
|
+
semgrep: 0,
|
|
115
|
+
trivy: 0,
|
|
116
|
+
zap: 0,
|
|
117
|
+
gitleaks: 0,
|
|
118
|
+
other: 0,
|
|
119
|
+
};
|
|
120
|
+
for (const finding of allFindings) {
|
|
121
|
+
bySeverity[finding.severity] = ((_a = bySeverity[finding.severity]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
122
|
+
byCategory[finding.category] = ((_b = byCategory[finding.category]) !== null && _b !== void 0 ? _b : 0) + 1;
|
|
123
|
+
byScanner[finding.scanner] = ((_c = byScanner[finding.scanner]) !== null && _c !== void 0 ? _c : 0) + 1;
|
|
124
|
+
}
|
|
125
|
+
const gateResult = config.gate
|
|
126
|
+
? (0, index_1.evaluateSecurityGate)(allFindings, config.gate)
|
|
127
|
+
: undefined;
|
|
128
|
+
return {
|
|
129
|
+
scannersRun,
|
|
130
|
+
totalFindings: allFindings.length,
|
|
131
|
+
bySeverity,
|
|
132
|
+
byCategory,
|
|
133
|
+
byScanner,
|
|
134
|
+
findings: allFindings,
|
|
135
|
+
gateResult,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// ─── Report generation ────────────────────────────────────────────────────────
|
|
139
|
+
/**
|
|
140
|
+
* Generate all security scan reports in the given directory.
|
|
141
|
+
*/
|
|
142
|
+
function generateSecurityScanReports(summary, reportsDir) {
|
|
143
|
+
if (!fs.existsSync(reportsDir)) {
|
|
144
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
const { findings } = summary;
|
|
147
|
+
// ── security-scan-summary.json ────────────────────────────────────────────
|
|
148
|
+
const summaryJson = {
|
|
149
|
+
scannersRun: summary.scannersRun,
|
|
150
|
+
totalFindings: summary.totalFindings,
|
|
151
|
+
bySeverity: summary.bySeverity,
|
|
152
|
+
byCategory: summary.byCategory,
|
|
153
|
+
byScanner: summary.byScanner,
|
|
154
|
+
gateResult: summary.gateResult,
|
|
155
|
+
};
|
|
156
|
+
fs.writeFileSync(path.join(reportsDir, 'security-scan-summary.json'), JSON.stringify(summaryJson, null, 2), 'utf-8');
|
|
157
|
+
// ── security-sast.json ────────────────────────────────────────────────────
|
|
158
|
+
const sast = findings.filter((f) => f.scanner === 'semgrep' || f.category === 'sast' || f.category === 'injection');
|
|
159
|
+
fs.writeFileSync(path.join(reportsDir, 'security-sast.json'), JSON.stringify(sast, null, 2), 'utf-8');
|
|
160
|
+
// ── security-dependencies.json ────────────────────────────────────────────
|
|
161
|
+
const deps = findings.filter((f) => f.category === 'sca');
|
|
162
|
+
fs.writeFileSync(path.join(reportsDir, 'security-dependencies.json'), JSON.stringify(deps, null, 2), 'utf-8');
|
|
163
|
+
// ── security-secrets.json ─────────────────────────────────────────────────
|
|
164
|
+
const secrets = findings.filter((f) => f.category === 'secret');
|
|
165
|
+
fs.writeFileSync(path.join(reportsDir, 'security-secrets.json'), JSON.stringify(secrets, null, 2), 'utf-8');
|
|
166
|
+
// ── security-misconfig.json ───────────────────────────────────────────────
|
|
167
|
+
const misconfig = findings.filter((f) => f.category === 'misconfig');
|
|
168
|
+
fs.writeFileSync(path.join(reportsDir, 'security-misconfig.json'), JSON.stringify(misconfig, null, 2), 'utf-8');
|
|
169
|
+
// ── security-dast.json ────────────────────────────────────────────────────
|
|
170
|
+
const dast = findings.filter((f) => f.scanner === 'zap' || f.category === 'dast');
|
|
171
|
+
fs.writeFileSync(path.join(reportsDir, 'security-dast.json'), JSON.stringify(dast, null, 2), 'utf-8');
|
|
172
|
+
// ── security-ai-summary.md ────────────────────────────────────────────────
|
|
173
|
+
const md = buildAiSummaryMarkdown(summary);
|
|
174
|
+
fs.writeFileSync(path.join(reportsDir, 'security-ai-summary.md'), md, 'utf-8');
|
|
175
|
+
// ── security-scan-summary.html ────────────────────────────────────────────
|
|
176
|
+
const html = buildSummaryHtml(summary);
|
|
177
|
+
fs.writeFileSync(path.join(reportsDir, 'security-scan-summary.html'), html, 'utf-8');
|
|
178
|
+
}
|
|
179
|
+
// ─── AI-friendly Markdown report ──────────────────────────────────────────────
|
|
180
|
+
function buildAiSummaryMarkdown(summary) {
|
|
181
|
+
const { findings, gateResult } = summary;
|
|
182
|
+
const gate = gateResult ? (gateResult.passed ? '✅ PASSED' : '❌ FAILED') : 'Not configured';
|
|
183
|
+
const topSecrets = findings.filter((f) => f.category === 'secret').slice(0, 5);
|
|
184
|
+
const topCritical = findings
|
|
185
|
+
.filter((f) => f.severity === 'CRITICAL' || f.severity === 'HIGH')
|
|
186
|
+
.slice(0, 10);
|
|
187
|
+
const listFindings = (items) => items.length === 0
|
|
188
|
+
? '- None\n'
|
|
189
|
+
: items
|
|
190
|
+
.map((f) => { var _a; return `- **[${f.severity}]** ${f.title}${f.filePath ? ` — \`${f.filePath}\`` : ''}${f.packageName ? ` (${f.packageName} ${(_a = f.installedVersion) !== null && _a !== void 0 ? _a : ''})` : ''}`; })
|
|
191
|
+
.join('\n') + '\n';
|
|
192
|
+
return `# Security Scan AI Summary
|
|
193
|
+
|
|
194
|
+
## Scanners Executed
|
|
195
|
+
${summary.scannersRun.length > 0 ? summary.scannersRun.map((s) => `- ${s}`).join('\n') : '- None'}
|
|
196
|
+
|
|
197
|
+
## Security Gate
|
|
198
|
+
**Status:** ${gate}
|
|
199
|
+
${gateResult && !gateResult.passed ? `\n**Failure reasons:**\n${gateResult.reasons.map((r) => `- ${r}`).join('\n')}` : ''}
|
|
200
|
+
|
|
201
|
+
## Findings Summary
|
|
202
|
+
|
|
203
|
+
| Severity | Count |
|
|
204
|
+
|----------|-------|
|
|
205
|
+
| CRITICAL | ${summary.bySeverity.CRITICAL} |
|
|
206
|
+
| HIGH | ${summary.bySeverity.HIGH} |
|
|
207
|
+
| MEDIUM | ${summary.bySeverity.MEDIUM} |
|
|
208
|
+
| LOW | ${summary.bySeverity.LOW} |
|
|
209
|
+
|
|
210
|
+
| Category | Count |
|
|
211
|
+
|----------|-------|
|
|
212
|
+
${Object.entries(summary.byCategory)
|
|
213
|
+
.filter(([, v]) => v > 0)
|
|
214
|
+
.map(([k, v]) => `| ${k} | ${v} |`)
|
|
215
|
+
.join('\n')}
|
|
216
|
+
|
|
217
|
+
## Top Risks
|
|
218
|
+
|
|
219
|
+
### Secrets Found (${findings.filter((f) => f.category === 'secret').length})
|
|
220
|
+
${listFindings(topSecrets)}
|
|
221
|
+
|
|
222
|
+
### Critical / High Findings (${topCritical.length} shown)
|
|
223
|
+
${listFindings(topCritical)}
|
|
224
|
+
|
|
225
|
+
## Recommended Remediation Order
|
|
226
|
+
|
|
227
|
+
1. **Secrets** — revoke and rotate immediately (${findings.filter((f) => f.category === 'secret').length} found)
|
|
228
|
+
2. **Critical Vulnerabilities** — patch or mitigate (${summary.bySeverity.CRITICAL} found)
|
|
229
|
+
3. **Auth Flaws** — fix authentication/authorization issues (${summary.byCategory.auth} found)
|
|
230
|
+
4. **Injection Risks** — fix injection vulnerabilities (${summary.byCategory.injection} found)
|
|
231
|
+
5. **Misconfigurations** — resolve HIGH/CRITICAL misconfigurations (${summary.byCategory.misconfig} found)
|
|
232
|
+
6. **Medium Findings** — address remaining MEDIUM issues (${summary.bySeverity.MEDIUM} found)
|
|
233
|
+
`;
|
|
234
|
+
}
|
|
235
|
+
// ─── HTML summary report ──────────────────────────────────────────────────────
|
|
236
|
+
function buildSummaryHtml(summary) {
|
|
237
|
+
const gateStatus = summary.gateResult
|
|
238
|
+
? summary.gateResult.passed
|
|
239
|
+
? '<span style="color:green">✅ PASSED</span>'
|
|
240
|
+
: `<span style="color:red">❌ FAILED</span>`
|
|
241
|
+
: '<span style="color:gray">Not configured</span>';
|
|
242
|
+
const gateReasons = summary.gateResult && !summary.gateResult.passed
|
|
243
|
+
? `<ul>${summary.gateResult.reasons.map((r) => `<li>${r}</li>`).join('')}</ul>`
|
|
244
|
+
: '';
|
|
245
|
+
const severityRows = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
|
246
|
+
.map((s) => {
|
|
247
|
+
const count = summary.bySeverity[s];
|
|
248
|
+
const cls = s === 'CRITICAL' ? 'bad' : s === 'HIGH' ? 'bad' : s === 'MEDIUM' ? 'warn' : '';
|
|
249
|
+
return `<tr class="${cls}"><td>${s}</td><td>${count}</td></tr>`;
|
|
250
|
+
})
|
|
251
|
+
.join('\n');
|
|
252
|
+
const categoryRows = Object.entries(summary.byCategory)
|
|
253
|
+
.filter(([, v]) => v > 0)
|
|
254
|
+
.map(([k, v]) => `<tr><td>${k}</td><td>${v}</td></tr>`)
|
|
255
|
+
.join('\n');
|
|
256
|
+
const findingRows = summary.findings
|
|
257
|
+
.slice(0, 200)
|
|
258
|
+
.map((f) => {
|
|
259
|
+
var _a;
|
|
260
|
+
const cls = f.severity === 'CRITICAL' || f.severity === 'HIGH'
|
|
261
|
+
? 'bad'
|
|
262
|
+
: f.severity === 'MEDIUM'
|
|
263
|
+
? 'warn'
|
|
264
|
+
: '';
|
|
265
|
+
const file = f.filePath ? `<code>${f.filePath}${f.lineStart ? `:${f.lineStart}` : ''}</code>` : '—';
|
|
266
|
+
const pkg = f.packageName ? `${f.packageName} ${(_a = f.installedVersion) !== null && _a !== void 0 ? _a : ''}` : '—';
|
|
267
|
+
return `<tr class="${cls}">
|
|
268
|
+
<td>${f.scanner}</td>
|
|
269
|
+
<td>${f.severity}</td>
|
|
270
|
+
<td>${f.category}</td>
|
|
271
|
+
<td>${f.title}</td>
|
|
272
|
+
<td>${file}</td>
|
|
273
|
+
<td>${pkg}</td>
|
|
274
|
+
</tr>`;
|
|
275
|
+
})
|
|
276
|
+
.join('\n');
|
|
277
|
+
return `<!DOCTYPE html>
|
|
278
|
+
<html lang="en">
|
|
279
|
+
<head>
|
|
280
|
+
<meta charset="UTF-8">
|
|
281
|
+
<title>Security Scan Summary</title>
|
|
282
|
+
<style>
|
|
283
|
+
body { font-family: sans-serif; padding: 2rem; }
|
|
284
|
+
h1, h2 { margin-bottom: 0.5rem; }
|
|
285
|
+
.gate { font-size: 1.2rem; margin-bottom: 1rem; }
|
|
286
|
+
table { border-collapse: collapse; width: 100%; margin-bottom: 2rem; }
|
|
287
|
+
th, td { border: 1px solid #ccc; padding: 0.5rem 1rem; text-align: left; }
|
|
288
|
+
th { background: #f0f0f0; }
|
|
289
|
+
tr.bad { background: #ffe6e6; }
|
|
290
|
+
tr.warn { background: #fff9e6; }
|
|
291
|
+
</style>
|
|
292
|
+
</head>
|
|
293
|
+
<body>
|
|
294
|
+
<h1>Security Scan Summary</h1>
|
|
295
|
+
<div class="gate">
|
|
296
|
+
Security Gate: ${gateStatus}
|
|
297
|
+
${gateReasons}
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<h2>Scanners Run</h2>
|
|
301
|
+
<p>${summary.scannersRun.length > 0 ? summary.scannersRun.join(', ') : 'None'}</p>
|
|
302
|
+
|
|
303
|
+
<h2>Findings by Severity</h2>
|
|
304
|
+
<table>
|
|
305
|
+
<thead><tr><th>Severity</th><th>Count</th></tr></thead>
|
|
306
|
+
<tbody>${severityRows}</tbody>
|
|
307
|
+
</table>
|
|
308
|
+
|
|
309
|
+
<h2>Findings by Category</h2>
|
|
310
|
+
<table>
|
|
311
|
+
<thead><tr><th>Category</th><th>Count</th></tr></thead>
|
|
312
|
+
<tbody>${categoryRows || '<tr><td colspan="2">No findings</td></tr>'}</tbody>
|
|
313
|
+
</table>
|
|
314
|
+
|
|
315
|
+
<h2>Findings Detail (up to 200)</h2>
|
|
316
|
+
<table>
|
|
317
|
+
<thead>
|
|
318
|
+
<tr><th>Scanner</th><th>Severity</th><th>Category</th><th>Title</th><th>File</th><th>Package</th></tr>
|
|
319
|
+
</thead>
|
|
320
|
+
<tbody>${findingRows || '<tr><td colspan="6">No findings</td></tr>'}</tbody>
|
|
321
|
+
</table>
|
|
322
|
+
</body>
|
|
323
|
+
</html>`;
|
|
324
|
+
}
|
|
325
|
+
// ─── Full security scan workflow ───────────────────────────────────────────────
|
|
326
|
+
/**
|
|
327
|
+
* Run the complete security scan workflow:
|
|
328
|
+
* 1. Execute configured scanners
|
|
329
|
+
* 2. Normalize findings
|
|
330
|
+
* 3. Build summary
|
|
331
|
+
* 4. Evaluate gate
|
|
332
|
+
* 5. Generate reports
|
|
333
|
+
*
|
|
334
|
+
* Returns the SecurityScanSummary for further use.
|
|
335
|
+
*/
|
|
336
|
+
async function runSecurityScan(config, reportsDir = 'reports') {
|
|
337
|
+
const results = await runSecurityScanners(config);
|
|
338
|
+
const summary = buildSecurityScanSummary(results, config);
|
|
339
|
+
const resolvedDir = path.resolve(reportsDir);
|
|
340
|
+
generateSecurityScanReports(summary, resolvedDir);
|
|
341
|
+
return summary;
|
|
342
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semgrep output normalizer.
|
|
3
|
+
* Converts Semgrep JSON findings to the common SecurityFinding model.
|
|
4
|
+
*/
|
|
5
|
+
import { SecurityFinding } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Parse a Semgrep JSON output buffer and return normalized SecurityFindings.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeSemgrepOutput(raw: unknown): SecurityFinding[];
|
|
10
|
+
//# sourceMappingURL=semgrep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semgrep.d.ts","sourceRoot":"","sources":["../../../../src/security/normalizers/semgrep.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,eAAe,EAGhB,MAAM,UAAU,CAAC;AAqHlB;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,CAuBtE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeSemgrepOutput = normalizeSemgrepOutput;
|
|
4
|
+
// ─── Severity mapping ─────────────────────────────────────────────────────────
|
|
5
|
+
function mapSemgrepSeverity(severity) {
|
|
6
|
+
switch ((severity !== null && severity !== void 0 ? severity : '').toUpperCase()) {
|
|
7
|
+
case 'CRITICAL':
|
|
8
|
+
return 'CRITICAL';
|
|
9
|
+
case 'ERROR':
|
|
10
|
+
return 'HIGH';
|
|
11
|
+
case 'WARNING':
|
|
12
|
+
case 'WARN':
|
|
13
|
+
return 'MEDIUM';
|
|
14
|
+
case 'INFO':
|
|
15
|
+
case 'NOTE':
|
|
16
|
+
return 'LOW';
|
|
17
|
+
default:
|
|
18
|
+
return 'MEDIUM';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// ─── Category mapping ─────────────────────────────────────────────────────────
|
|
22
|
+
function mapSemgrepCategory(checkId, metadata) {
|
|
23
|
+
var _a;
|
|
24
|
+
const cat = ((_a = metadata === null || metadata === void 0 ? void 0 : metadata.category) !== null && _a !== void 0 ? _a : '').toLowerCase();
|
|
25
|
+
if (cat === 'security') {
|
|
26
|
+
// Derive more specific category from the rule ID and CWE/OWASP tags
|
|
27
|
+
const idLower = checkId.toLowerCase();
|
|
28
|
+
const cwes = normaliseTags(metadata === null || metadata === void 0 ? void 0 : metadata.cwe);
|
|
29
|
+
const owasp = normaliseTags(metadata === null || metadata === void 0 ? void 0 : metadata.owasp);
|
|
30
|
+
if (cwes.some((c) => c.includes('89') || c.includes('943')) ||
|
|
31
|
+
idLower.includes('injection') ||
|
|
32
|
+
idLower.includes('sqli') ||
|
|
33
|
+
idLower.includes('xss')) {
|
|
34
|
+
return 'injection';
|
|
35
|
+
}
|
|
36
|
+
if (cwes.some((c) => c.includes('798') || c.includes('259') || c.includes('321')) ||
|
|
37
|
+
idLower.includes('hardcoded') ||
|
|
38
|
+
idLower.includes('secret') ||
|
|
39
|
+
idLower.includes('password')) {
|
|
40
|
+
return 'secret';
|
|
41
|
+
}
|
|
42
|
+
if (cwes.some((c) => c.includes('287') || c.includes('306') || c.includes('384')) ||
|
|
43
|
+
idLower.includes('auth')) {
|
|
44
|
+
return 'auth';
|
|
45
|
+
}
|
|
46
|
+
if (cwes.some((c) => c.includes('311') || c.includes('319') || c.includes('327')) ||
|
|
47
|
+
idLower.includes('crypto') ||
|
|
48
|
+
idLower.includes('cipher') ||
|
|
49
|
+
idLower.includes('tls') ||
|
|
50
|
+
idLower.includes('ssl')) {
|
|
51
|
+
return 'crypto';
|
|
52
|
+
}
|
|
53
|
+
if (owasp.some((o) => o.includes('A3') || o.includes('sensitive'))) {
|
|
54
|
+
return 'data-exposure';
|
|
55
|
+
}
|
|
56
|
+
return 'sast';
|
|
57
|
+
}
|
|
58
|
+
if (cat.includes('secret') || cat.includes('credential'))
|
|
59
|
+
return 'secret';
|
|
60
|
+
if (cat.includes('inject'))
|
|
61
|
+
return 'injection';
|
|
62
|
+
if (cat.includes('auth'))
|
|
63
|
+
return 'auth';
|
|
64
|
+
if (cat.includes('crypto') || cat.includes('cipher'))
|
|
65
|
+
return 'crypto';
|
|
66
|
+
if (cat.includes('exposure') || cat.includes('disclosure'))
|
|
67
|
+
return 'data-exposure';
|
|
68
|
+
if (cat.includes('config') || cat.includes('misconfiguration'))
|
|
69
|
+
return 'misconfig';
|
|
70
|
+
return 'sast';
|
|
71
|
+
}
|
|
72
|
+
function normaliseTags(value) {
|
|
73
|
+
if (!value)
|
|
74
|
+
return [];
|
|
75
|
+
return Array.isArray(value) ? value : [value];
|
|
76
|
+
}
|
|
77
|
+
// ─── Normalizer ───────────────────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Parse a Semgrep JSON output buffer and return normalized SecurityFindings.
|
|
80
|
+
*/
|
|
81
|
+
function normalizeSemgrepOutput(raw) {
|
|
82
|
+
var _a;
|
|
83
|
+
const data = raw;
|
|
84
|
+
const results = (_a = data.results) !== null && _a !== void 0 ? _a : [];
|
|
85
|
+
return results.map((r) => {
|
|
86
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
87
|
+
const cwe = normaliseTags((_b = (_a = r.extra) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.cwe);
|
|
88
|
+
const owasp = normaliseTags((_d = (_c = r.extra) === null || _c === void 0 ? void 0 : _c.metadata) === null || _d === void 0 ? void 0 : _d.owasp);
|
|
89
|
+
return {
|
|
90
|
+
scanner: 'semgrep',
|
|
91
|
+
category: mapSemgrepCategory(r.checkId, (_e = r.extra) === null || _e === void 0 ? void 0 : _e.metadata),
|
|
92
|
+
severity: mapSemgrepSeverity((_f = r.extra) === null || _f === void 0 ? void 0 : _f.severity),
|
|
93
|
+
title: (_h = (_g = r.extra) === null || _g === void 0 ? void 0 : _g.message) !== null && _h !== void 0 ? _h : r.checkId,
|
|
94
|
+
description: (_j = r.extra) === null || _j === void 0 ? void 0 : _j.lines,
|
|
95
|
+
ruleId: r.checkId,
|
|
96
|
+
cwe: cwe.length > 0 ? cwe : undefined,
|
|
97
|
+
owasp: owasp.length > 0 ? owasp : undefined,
|
|
98
|
+
filePath: r.path,
|
|
99
|
+
lineStart: (_k = r.start) === null || _k === void 0 ? void 0 : _k.line,
|
|
100
|
+
lineEnd: (_l = r.end) === null || _l === void 0 ? void 0 : _l.line,
|
|
101
|
+
scannerNativePayload: r,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivy output normalizer.
|
|
3
|
+
* Converts Trivy JSON findings to the common SecurityFinding model.
|
|
4
|
+
*/
|
|
5
|
+
import { SecurityFinding } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Parse Trivy JSON output and return normalized SecurityFindings.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeTrivyOutput(raw: unknown): SecurityFinding[];
|
|
10
|
+
//# sourceMappingURL=trivy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trivy.d.ts","sourceRoot":"","sources":["../../../../src/security/normalizers/trivy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,eAAe,EAGhB,MAAM,UAAU,CAAC;AAqFlB;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,CAgEpE"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeTrivyOutput = normalizeTrivyOutput;
|
|
4
|
+
// ─── Severity mapping ─────────────────────────────────────────────────────────
|
|
5
|
+
function mapTrivySeverity(severity) {
|
|
6
|
+
switch ((severity !== null && severity !== void 0 ? severity : '').toUpperCase()) {
|
|
7
|
+
case 'CRITICAL': return 'CRITICAL';
|
|
8
|
+
case 'HIGH': return 'HIGH';
|
|
9
|
+
case 'MEDIUM': return 'MEDIUM';
|
|
10
|
+
case 'LOW':
|
|
11
|
+
case 'UNKNOWN':
|
|
12
|
+
default: return 'LOW';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// ─── Normalizer ───────────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Parse Trivy JSON output and return normalized SecurityFindings.
|
|
18
|
+
*/
|
|
19
|
+
function normalizeTrivyOutput(raw) {
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
21
|
+
const data = raw;
|
|
22
|
+
const results = (_a = data.Results) !== null && _a !== void 0 ? _a : [];
|
|
23
|
+
const findings = [];
|
|
24
|
+
for (const result of results) {
|
|
25
|
+
const filePath = result.Target;
|
|
26
|
+
// ── Vulnerabilities ──────────────────────────────────────────────────────
|
|
27
|
+
for (const vuln of (_b = result.Vulnerabilities) !== null && _b !== void 0 ? _b : []) {
|
|
28
|
+
const category = 'sca';
|
|
29
|
+
findings.push({
|
|
30
|
+
scanner: 'trivy',
|
|
31
|
+
category,
|
|
32
|
+
severity: mapTrivySeverity(vuln.Severity),
|
|
33
|
+
title: (_c = vuln.Title) !== null && _c !== void 0 ? _c : vuln.VulnerabilityID,
|
|
34
|
+
description: vuln.Description,
|
|
35
|
+
ruleId: vuln.VulnerabilityID,
|
|
36
|
+
cve: [vuln.VulnerabilityID],
|
|
37
|
+
cwe: vuln.CweIDs,
|
|
38
|
+
filePath,
|
|
39
|
+
packageName: vuln.PkgName,
|
|
40
|
+
installedVersion: vuln.InstalledVersion,
|
|
41
|
+
fixedVersion: vuln.FixedVersion,
|
|
42
|
+
scannerNativePayload: vuln,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// ── Secrets ──────────────────────────────────────────────────────────────
|
|
46
|
+
for (const secret of (_d = result.Secrets) !== null && _d !== void 0 ? _d : []) {
|
|
47
|
+
findings.push({
|
|
48
|
+
scanner: 'trivy',
|
|
49
|
+
category: 'secret',
|
|
50
|
+
severity: mapTrivySeverity(secret.Severity),
|
|
51
|
+
title: secret.Title,
|
|
52
|
+
description: secret.Match,
|
|
53
|
+
ruleId: secret.RuleID,
|
|
54
|
+
filePath,
|
|
55
|
+
lineStart: secret.StartLine,
|
|
56
|
+
lineEnd: secret.EndLine,
|
|
57
|
+
secretType: secret.Category,
|
|
58
|
+
scannerNativePayload: secret,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// ── Misconfigurations ────────────────────────────────────────────────────
|
|
62
|
+
for (const mis of (_e = result.Misconfigurations) !== null && _e !== void 0 ? _e : []) {
|
|
63
|
+
findings.push({
|
|
64
|
+
scanner: 'trivy',
|
|
65
|
+
category: 'misconfig',
|
|
66
|
+
severity: mapTrivySeverity(mis.Severity),
|
|
67
|
+
title: mis.Title,
|
|
68
|
+
description: mis.Description,
|
|
69
|
+
ruleId: mis.ID,
|
|
70
|
+
filePath,
|
|
71
|
+
lineStart: (_f = mis.CauseMetadata) === null || _f === void 0 ? void 0 : _f.StartLine,
|
|
72
|
+
lineEnd: (_g = mis.CauseMetadata) === null || _g === void 0 ? void 0 : _g.EndLine,
|
|
73
|
+
scannerNativePayload: mis,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return findings;
|
|
78
|
+
}
|