@maverick006/security-engine 1.0.2 → 1.0.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/dist/deduplication.d.ts +16 -0
- package/dist/deduplication.d.ts.map +1 -0
- package/dist/deduplication.js +101 -0
- package/dist/deduplication.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +13 -8
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +153 -38
- package/dist/orchestrator.js.map +1 -1
- package/dist/scanner.d.ts +9 -0
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scoring.d.ts +19 -13
- package/dist/scoring.d.ts.map +1 -1
- package/dist/scoring.js +96 -26
- package/dist/scoring.js.map +1 -1
- package/package.json +5 -4
- package/src/deduplication.ts +118 -0
- package/src/index.ts +1 -0
- package/src/orchestrator.ts +183 -46
- package/src/scanner.ts +11 -0
- package/src/scoring.ts +107 -30
- package/tests/deduplication.test.ts +122 -0
- package/tests/orchestrator.test.ts +120 -96
- package/tests/scoring.test.ts +33 -25
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NormalizedFinding } from '@maverick006/types';
|
|
2
|
+
/**
|
|
3
|
+
* Options for finding deduplication.
|
|
4
|
+
*/
|
|
5
|
+
export interface DeduplicationOptions {
|
|
6
|
+
lineTolerance?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deduplicates findings across multiple scanners:
|
|
10
|
+
* 1. Exact duplicates (same scanner, file, rule, line).
|
|
11
|
+
* 2. Cross-scanner duplicates (e.g. Trivy & npm-audit reporting the same CVE or package vuln).
|
|
12
|
+
* 3. Line shift tolerance (+/- 3 lines) to handle minor offset differences.
|
|
13
|
+
* 4. Preserves distinct vulnerabilities on the same file/line.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deduplicateFindings(findings: NormalizedFinding[], options?: DeduplicationOptions): NormalizedFinding[];
|
|
16
|
+
//# sourceMappingURL=deduplication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplication.d.ts","sourceRoot":"","sources":["../src/deduplication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAY,MAAM,oBAAoB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAuDD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,OAAO,GAAE,oBAAyB,GACjC,iBAAiB,EAAE,CA6CrB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deduplicateFindings = deduplicateFindings;
|
|
4
|
+
/**
|
|
5
|
+
* Normalizes a rule or CVE identifier to detect cross-scanner duplicates.
|
|
6
|
+
*/
|
|
7
|
+
function normalizeIdentifier(finding) {
|
|
8
|
+
// If CVE or CWE is present, prioritize that as universal identifier
|
|
9
|
+
const cveMatch = (finding.ruleId || finding.title || finding.description || '').match(/CVE-\d{4}-\d+/i);
|
|
10
|
+
if (cveMatch) {
|
|
11
|
+
return cveMatch[0].toUpperCase();
|
|
12
|
+
}
|
|
13
|
+
// If package-based vulnerability, match package + cwe/rule
|
|
14
|
+
if (finding.package) {
|
|
15
|
+
const pkg = finding.package.toLowerCase();
|
|
16
|
+
const cwe = finding.cwe ? finding.cwe.toLowerCase() : '';
|
|
17
|
+
return `pkg:${pkg}:${cwe || finding.ruleId || 'vuln'}`;
|
|
18
|
+
}
|
|
19
|
+
// Generic rule ID fallback
|
|
20
|
+
return (finding.ruleId || finding.title || 'unknown').toLowerCase().trim();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes file paths for reliable cross-platform comparison.
|
|
24
|
+
*/
|
|
25
|
+
function normalizeFilePath(filePath) {
|
|
26
|
+
if (!filePath)
|
|
27
|
+
return '';
|
|
28
|
+
return filePath.replace(/\\/g, '/').toLowerCase().trim();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Merges two findings for the same vulnerability, retaining the highest confidence and most complete metadata.
|
|
32
|
+
*/
|
|
33
|
+
function mergeFindings(primary, secondary) {
|
|
34
|
+
return {
|
|
35
|
+
...primary,
|
|
36
|
+
// Keep the more severe or primary severity
|
|
37
|
+
severity: primary.severity || secondary.severity,
|
|
38
|
+
confidence: primary.confidence || secondary.confidence,
|
|
39
|
+
cwe: primary.cwe || secondary.cwe,
|
|
40
|
+
owasp: primary.owasp || secondary.owasp,
|
|
41
|
+
remediation: primary.remediation || secondary.remediation,
|
|
42
|
+
codeSnippet: primary.codeSnippet || secondary.codeSnippet,
|
|
43
|
+
package: primary.package || secondary.package,
|
|
44
|
+
packageVersion: primary.packageVersion || secondary.packageVersion,
|
|
45
|
+
fixedVersion: primary.fixedVersion || secondary.fixedVersion,
|
|
46
|
+
references: Array.from(new Set([...(primary.references || []), ...(secondary.references || [])])),
|
|
47
|
+
// Note in scanner if multiple scanners detected it
|
|
48
|
+
scanner: primary.scanner.includes(secondary.scanner)
|
|
49
|
+
? primary.scanner
|
|
50
|
+
: `${primary.scanner}, ${secondary.scanner}`
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Deduplicates findings across multiple scanners:
|
|
55
|
+
* 1. Exact duplicates (same scanner, file, rule, line).
|
|
56
|
+
* 2. Cross-scanner duplicates (e.g. Trivy & npm-audit reporting the same CVE or package vuln).
|
|
57
|
+
* 3. Line shift tolerance (+/- 3 lines) to handle minor offset differences.
|
|
58
|
+
* 4. Preserves distinct vulnerabilities on the same file/line.
|
|
59
|
+
*/
|
|
60
|
+
function deduplicateFindings(findings, options = {}) {
|
|
61
|
+
const lineTolerance = options.lineTolerance ?? 3;
|
|
62
|
+
const deduplicated = [];
|
|
63
|
+
for (const candidate of findings) {
|
|
64
|
+
const candidateFile = normalizeFilePath(candidate.file);
|
|
65
|
+
const candidateId = normalizeIdentifier(candidate);
|
|
66
|
+
const candidateLine = candidate.line || 0;
|
|
67
|
+
let matchedIndex = -1;
|
|
68
|
+
for (let i = 0; i < deduplicated.length; i++) {
|
|
69
|
+
const existing = deduplicated[i];
|
|
70
|
+
const existingFile = normalizeFilePath(existing.file);
|
|
71
|
+
const existingId = normalizeIdentifier(existing);
|
|
72
|
+
const existingLine = existing.line || 0;
|
|
73
|
+
// Check for same file & same vulnerability identity
|
|
74
|
+
const isSameFile = candidateFile && existingFile ? candidateFile === existingFile : true;
|
|
75
|
+
const isSameVuln = candidateId === existingId;
|
|
76
|
+
if (isSameFile && isSameVuln) {
|
|
77
|
+
// If line numbers exist, check line tolerance
|
|
78
|
+
if (candidateLine > 0 && existingLine > 0) {
|
|
79
|
+
if (Math.abs(candidateLine - existingLine) <= lineTolerance) {
|
|
80
|
+
matchedIndex = i;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// If either lacks line numbers (e.g. package/repo level), treat as match
|
|
86
|
+
matchedIndex = i;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (matchedIndex >= 0) {
|
|
92
|
+
// Merge with existing finding
|
|
93
|
+
deduplicated[matchedIndex] = mergeFindings(deduplicated[matchedIndex], candidate);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
deduplicated.push({ ...candidate });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return deduplicated;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=deduplication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplication.js","sourceRoot":"","sources":["../src/deduplication.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,4 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./scanner"), exports);
|
|
18
18
|
__exportStar(require("./orchestrator"), exports);
|
|
19
19
|
__exportStar(require("./scoring"), exports);
|
|
20
|
+
__exportStar(require("./deduplication"), exports);
|
|
20
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,iDAA+B;AAC/B,4CAA0B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,iDAA+B;AAC/B,4CAA0B;AAC1B,kDAAgC"}
|
package/dist/orchestrator.d.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import { ScanInput, ScannerResult } from '@maverick006/types';
|
|
1
|
+
import { ScanInput, ScannerResult, ScannerCoverage } from '@maverick006/types';
|
|
2
2
|
import { SecurityScanner } from './scanner';
|
|
3
|
+
export interface OrchestratorOptions {
|
|
4
|
+
concurrencyLimit?: number;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface OrchestratedScanResult extends ScannerResult {
|
|
8
|
+
scannerResults: ScannerResult[];
|
|
9
|
+
coverage: ScannerCoverage;
|
|
10
|
+
}
|
|
3
11
|
/**
|
|
4
12
|
* Orchestrates the execution of multiple security scanners and aggregates/deduplicates their findings.
|
|
5
13
|
*/
|
|
6
14
|
export declare class Orchestrator {
|
|
7
15
|
private scanners;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Executes all registered scanners against the given input.
|
|
11
|
-
*/
|
|
12
|
-
runScan(input: ScanInput): Promise<ScannerResult>;
|
|
16
|
+
private options;
|
|
17
|
+
constructor(scanners: SecurityScanner[], options?: OrchestratorOptions);
|
|
13
18
|
/**
|
|
14
|
-
*
|
|
19
|
+
* Executes registered scanners using a bounded concurrency pool.
|
|
15
20
|
*/
|
|
16
|
-
|
|
21
|
+
runScan(input: ScanInput): Promise<OrchestratedScanResult>;
|
|
17
22
|
}
|
|
18
23
|
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,aAAa,EAAgB,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAChH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AA+CD;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,OAAO,CAAsB;gBAEzB,QAAQ,EAAE,eAAe,EAAE,EAAE,OAAO,GAAE,mBAAwB;IAQ1E;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,sBAAsB,CAAC;CAsIjE"}
|
package/dist/orchestrator.js
CHANGED
|
@@ -1,67 +1,182 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Orchestrator = void 0;
|
|
4
|
+
const types_1 = require("@maverick006/types");
|
|
5
|
+
const deduplication_1 = require("./deduplication");
|
|
6
|
+
/**
|
|
7
|
+
* Runs tasks with a bounded concurrency pool (no unlimited Promise.all).
|
|
8
|
+
*/
|
|
9
|
+
async function runWithConcurrencyLimit(items, limit, fn) {
|
|
10
|
+
const results = new Array(items.length);
|
|
11
|
+
let currentIndex = 0;
|
|
12
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
13
|
+
while (currentIndex < items.length) {
|
|
14
|
+
const index = currentIndex++;
|
|
15
|
+
results[index] = await fn(items[index]);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
await Promise.all(workers);
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Wraps a promise with a timeout.
|
|
23
|
+
*/
|
|
24
|
+
function withTimeout(promise, timeoutMs, scannerName) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const timer = setTimeout(() => {
|
|
27
|
+
const err = new Error(`Scanner ${scannerName} timed out after ${timeoutMs}ms`);
|
|
28
|
+
err.code = 'ETIMEDOUT';
|
|
29
|
+
reject(err);
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
promise
|
|
32
|
+
.then(val => {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
resolve(val);
|
|
35
|
+
})
|
|
36
|
+
.catch(err => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
reject(err);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
4
42
|
/**
|
|
5
43
|
* Orchestrates the execution of multiple security scanners and aggregates/deduplicates their findings.
|
|
6
44
|
*/
|
|
7
45
|
class Orchestrator {
|
|
8
46
|
scanners = [];
|
|
9
|
-
|
|
47
|
+
options;
|
|
48
|
+
constructor(scanners, options = {}) {
|
|
10
49
|
this.scanners = scanners;
|
|
50
|
+
this.options = {
|
|
51
|
+
concurrencyLimit: options.concurrencyLimit ?? 3,
|
|
52
|
+
timeoutMs: options.timeoutMs ?? 120_000
|
|
53
|
+
};
|
|
11
54
|
}
|
|
12
55
|
/**
|
|
13
|
-
* Executes
|
|
56
|
+
* Executes registered scanners using a bounded concurrency pool.
|
|
14
57
|
*/
|
|
15
58
|
async runScan(input) {
|
|
16
59
|
const startTime = new Date();
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
60
|
+
const concurrency = this.options.concurrencyLimit ?? 3;
|
|
61
|
+
const timeout = this.options.timeoutMs ?? 120_000;
|
|
62
|
+
const executeScanner = async (scanner) => {
|
|
63
|
+
const scanStart = new Date();
|
|
64
|
+
// Check applicability if scanner provides capability detection
|
|
65
|
+
if (scanner.capabilities?.detectApplicability) {
|
|
66
|
+
try {
|
|
67
|
+
const isApplicable = await scanner.capabilities.detectApplicability(input.repositoryPath, input);
|
|
68
|
+
if (!isApplicable) {
|
|
69
|
+
const scanEnd = new Date();
|
|
70
|
+
return {
|
|
71
|
+
scanner: scanner.name,
|
|
72
|
+
success: true,
|
|
73
|
+
state: types_1.ScannerState.SKIPPED,
|
|
74
|
+
reason: `Skipped: Not applicable to repository or target`,
|
|
75
|
+
findings: [],
|
|
76
|
+
startTime: scanStart,
|
|
77
|
+
endTime: scanEnd,
|
|
78
|
+
durationMs: scanEnd.getTime() - scanStart.getTime()
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.warn(`Applicability check error for ${scanner.name}:`, err.message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const scanPromise = scanner.scan(input);
|
|
88
|
+
const result = await withTimeout(scanPromise, timeout, scanner.name);
|
|
89
|
+
const scanEnd = new Date();
|
|
90
|
+
return {
|
|
91
|
+
...result,
|
|
92
|
+
state: result.state || (result.success ? types_1.ScannerState.SUCCESS : types_1.ScannerState.FAILED),
|
|
93
|
+
durationMs: scanEnd.getTime() - scanStart.getTime(),
|
|
94
|
+
endTime: scanEnd
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
const scanEnd = new Date();
|
|
99
|
+
const durationMs = scanEnd.getTime() - scanStart.getTime();
|
|
100
|
+
if (err.code === 'ETIMEDOUT') {
|
|
101
|
+
return {
|
|
102
|
+
scanner: scanner.name,
|
|
103
|
+
success: false,
|
|
104
|
+
state: types_1.ScannerState.TIMEOUT,
|
|
105
|
+
error: err.message,
|
|
106
|
+
reason: `Execution exceeded timeout of ${timeout}ms`,
|
|
107
|
+
findings: [],
|
|
108
|
+
startTime: scanStart,
|
|
109
|
+
endTime: scanEnd,
|
|
110
|
+
durationMs
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (err.code === 'ENOENT') {
|
|
114
|
+
return {
|
|
115
|
+
scanner: scanner.name,
|
|
116
|
+
success: false,
|
|
117
|
+
state: types_1.ScannerState.NOT_INSTALLED,
|
|
118
|
+
error: err.message,
|
|
119
|
+
reason: `Scanner executable was not found on PATH`,
|
|
120
|
+
findings: [],
|
|
121
|
+
startTime: scanStart,
|
|
122
|
+
endTime: scanEnd,
|
|
123
|
+
durationMs
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
scanner: scanner.name,
|
|
128
|
+
success: false,
|
|
129
|
+
state: types_1.ScannerState.FAILED,
|
|
130
|
+
error: err.message || String(err),
|
|
131
|
+
findings: [],
|
|
132
|
+
startTime: scanStart,
|
|
133
|
+
endTime: scanEnd,
|
|
134
|
+
durationMs
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
// Execute through bounded concurrency worker pool
|
|
139
|
+
const scannerResults = await runWithConcurrencyLimit(this.scanners, concurrency, executeScanner);
|
|
20
140
|
let allFindings = [];
|
|
21
141
|
const rawOutputs = {};
|
|
22
|
-
for (
|
|
23
|
-
|
|
24
|
-
const scannerName = this.scanners[i].name;
|
|
25
|
-
if (result.status === 'fulfilled') {
|
|
26
|
-
const data = result.value;
|
|
142
|
+
for (const data of scannerResults) {
|
|
143
|
+
if (data.findings && data.findings.length > 0) {
|
|
27
144
|
allFindings = allFindings.concat(data.findings);
|
|
28
|
-
if (data.rawOutput) {
|
|
29
|
-
rawOutputs[scannerName] = data.rawOutput;
|
|
30
|
-
}
|
|
31
145
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.error(`Scanner ${scannerName} failed:`, result.reason);
|
|
146
|
+
if (data.rawOutput) {
|
|
147
|
+
rawOutputs[data.scanner] = data.rawOutput;
|
|
35
148
|
}
|
|
36
149
|
}
|
|
37
|
-
|
|
150
|
+
// High-precision deduplication across scanners with line tolerance
|
|
151
|
+
const deduplicatedFindings = (0, deduplication_1.deduplicateFindings)(allFindings);
|
|
152
|
+
// Standardize finding IDs to VG-FIND-001, VG-FIND-002, etc.
|
|
153
|
+
deduplicatedFindings.forEach((finding, index) => {
|
|
154
|
+
finding.id = `VG-FIND-${String(index + 1).padStart(3, '0')}`;
|
|
155
|
+
});
|
|
156
|
+
// Compute coverage matrix based on successful scanner executions
|
|
157
|
+
const coverage = {
|
|
158
|
+
code: scannerResults.some(r => r.scanner.toLowerCase().includes('semgrep') && r.state === types_1.ScannerState.SUCCESS),
|
|
159
|
+
dependencies: scannerResults.some(r => (r.scanner.toLowerCase().includes('npm') || r.scanner.toLowerCase().includes('audit') || r.scanner.toLowerCase().includes('trivy')) && r.state === types_1.ScannerState.SUCCESS),
|
|
160
|
+
secrets: scannerResults.some(r => r.scanner.toLowerCase().includes('gitleaks') && r.state === types_1.ScannerState.SUCCESS),
|
|
161
|
+
containers: scannerResults.some(r => r.scanner.toLowerCase().includes('trivy') && r.state === types_1.ScannerState.SUCCESS),
|
|
162
|
+
iac: scannerResults.some(r => r.scanner.toLowerCase().includes('checkov') && r.state === types_1.ScannerState.SUCCESS),
|
|
163
|
+
web: scannerResults.some(r => r.scanner.toLowerCase().includes('zap') && r.state === types_1.ScannerState.SUCCESS),
|
|
164
|
+
cloud: scannerResults.some(r => (r.scanner.toLowerCase().includes('prowler') || r.scanner.toLowerCase().includes('cspm')) && r.state === types_1.ScannerState.SUCCESS)
|
|
165
|
+
};
|
|
166
|
+
const endTime = new Date();
|
|
38
167
|
return {
|
|
39
168
|
scanner: 'VibeGuard_Orchestrator',
|
|
40
|
-
success:
|
|
169
|
+
success: scannerResults.some(r => r.state === types_1.ScannerState.SUCCESS),
|
|
170
|
+
state: types_1.ScannerState.SUCCESS,
|
|
171
|
+
durationMs: endTime.getTime() - startTime.getTime(),
|
|
41
172
|
findings: deduplicatedFindings,
|
|
42
|
-
|
|
173
|
+
scannerResults,
|
|
174
|
+
coverage,
|
|
175
|
+
rawOutput: JSON.stringify(rawOutputs),
|
|
43
176
|
startTime,
|
|
44
|
-
endTime
|
|
177
|
+
endTime
|
|
45
178
|
};
|
|
46
179
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Deduplicates findings based on a fingerprint generated from ruleId, file, line, and column.
|
|
49
|
-
*/
|
|
50
|
-
deduplicateFindings(findings) {
|
|
51
|
-
const unique = new Map();
|
|
52
|
-
for (const finding of findings) {
|
|
53
|
-
// Create a unique fingerprint for the finding
|
|
54
|
-
const fingerprint = `${finding.ruleId}-${finding.file}-${finding.line}-${finding.column}`;
|
|
55
|
-
if (!unique.has(fingerprint)) {
|
|
56
|
-
unique.set(fingerprint, finding);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
// If it already exists, we might want to merge information, but for now we keep the first one
|
|
60
|
-
// Optionally, if the new one has higher confidence, we could replace it.
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return Array.from(unique.values());
|
|
64
|
-
}
|
|
65
180
|
}
|
|
66
181
|
exports.Orchestrator = Orchestrator;
|
|
67
182
|
//# sourceMappingURL=orchestrator.js.map
|
package/dist/orchestrator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"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"}
|
package/dist/scanner.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { ScanInput, ScannerResult } from '@maverick006/types';
|
|
2
|
+
export interface ScannerCapability {
|
|
3
|
+
category: 'code' | 'dependencies' | 'secrets' | 'containers' | 'iac' | 'web' | 'cloud';
|
|
4
|
+
requiresCredentials?: boolean;
|
|
5
|
+
detectApplicability?: (repoPath: string, input?: ScanInput) => Promise<boolean> | boolean;
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* Base interface that all VibeGuard scanners must implement.
|
|
4
9
|
* This provides the core abstraction allowing new scanners to be added
|
|
@@ -13,6 +18,10 @@ export interface SecurityScanner {
|
|
|
13
18
|
* The version of the scanner, if known/applicable.
|
|
14
19
|
*/
|
|
15
20
|
version?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional capability metadata and applicability detection.
|
|
23
|
+
*/
|
|
24
|
+
capabilities?: ScannerCapability;
|
|
16
25
|
/**
|
|
17
26
|
* Executes the scanner against the given input.
|
|
18
27
|
*
|
package/dist/scanner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAChD"}
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;IACvF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC3F;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,iBAAiB,CAAC;IAEjC;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAChD"}
|
package/dist/scoring.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { NormalizedFinding } from '@maverick006/types';
|
|
2
|
-
export interface ScoreResult {
|
|
3
|
-
score: number;
|
|
4
|
-
grade: 'A' | 'B' | 'C' | 'D' | 'F';
|
|
1
|
+
import { NormalizedFinding, ScannerCoverage, DeterministicScore } from '@maverick006/types';
|
|
2
|
+
export interface ScoreResult extends DeterministicScore {
|
|
5
3
|
metrics: {
|
|
6
4
|
critical: number;
|
|
7
5
|
high: number;
|
|
@@ -10,15 +8,23 @@ export interface ScoreResult {
|
|
|
10
8
|
};
|
|
11
9
|
}
|
|
12
10
|
/**
|
|
13
|
-
* Calculates a deterministic security score
|
|
14
|
-
*
|
|
11
|
+
* Calculates a deterministic, reproducible security score (0 to 100) and grade
|
|
12
|
+
* based on the volume and severity of unique findings.
|
|
15
13
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
14
|
+
* Rules:
|
|
15
|
+
* - Base score: 100
|
|
16
|
+
* - Critical: -30 points per finding (any critical forces Grade F and caps score at <= 49)
|
|
17
|
+
* - High: -10 points per finding
|
|
18
|
+
* - Medium: -3 points per finding
|
|
19
|
+
* - Low: -1 point per finding
|
|
20
|
+
* - Info: 0 points
|
|
21
|
+
*
|
|
22
|
+
* Grades:
|
|
23
|
+
* A: 90 - 100
|
|
24
|
+
* B: 80 - 89
|
|
25
|
+
* C: 70 - 79
|
|
26
|
+
* D: 50 - 69
|
|
27
|
+
* F: < 50 OR any CRITICAL finding
|
|
22
28
|
*/
|
|
23
|
-
export declare function calculateScore(findings: NormalizedFinding[]): ScoreResult;
|
|
29
|
+
export declare function calculateScore(findings: NormalizedFinding[], coverage?: Partial<ScannerCoverage>): ScoreResult;
|
|
24
30
|
//# sourceMappingURL=scoring.d.ts.map
|
package/dist/scoring.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAY,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAY,eAAe,EAAE,kBAAkB,EAAmB,MAAM,oBAAoB,CAAC;AAEvH,MAAM,WAAW,WAAY,SAAQ,kBAAkB;IACrD,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAClC,WAAW,CA0Hb"}
|