guardrail-security 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/dist/attack-surface/analyzer.d.ts +50 -0
- package/dist/attack-surface/analyzer.d.ts.map +1 -0
- package/dist/attack-surface/analyzer.js +83 -0
- package/dist/attack-surface/index.d.ts +5 -0
- package/dist/attack-surface/index.d.ts.map +1 -0
- package/dist/attack-surface/index.js +20 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/languages/index.d.ts +21 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +78 -0
- package/dist/languages/java-analyzer.d.ts +72 -0
- package/dist/languages/java-analyzer.d.ts.map +1 -0
- package/dist/languages/java-analyzer.js +417 -0
- package/dist/languages/python-analyzer.d.ts +70 -0
- package/dist/languages/python-analyzer.d.ts.map +1 -0
- package/dist/languages/python-analyzer.js +425 -0
- package/dist/license/compatibility-matrix.d.ts +28 -0
- package/dist/license/compatibility-matrix.d.ts.map +1 -0
- package/dist/license/compatibility-matrix.js +323 -0
- package/dist/license/engine.d.ts +77 -0
- package/dist/license/engine.d.ts.map +1 -0
- package/dist/license/engine.js +264 -0
- package/dist/license/index.d.ts +6 -0
- package/dist/license/index.d.ts.map +1 -0
- package/dist/license/index.js +21 -0
- package/dist/sbom/generator.d.ts +108 -0
- package/dist/sbom/generator.d.ts.map +1 -0
- package/dist/sbom/generator.js +271 -0
- package/dist/sbom/index.d.ts +5 -0
- package/dist/sbom/index.d.ts.map +1 -0
- package/dist/sbom/index.js +20 -0
- package/dist/secrets/guardian.d.ts +113 -0
- package/dist/secrets/guardian.d.ts.map +1 -0
- package/dist/secrets/guardian.js +334 -0
- package/dist/secrets/index.d.ts +10 -0
- package/dist/secrets/index.d.ts.map +1 -0
- package/dist/secrets/index.js +30 -0
- package/dist/secrets/patterns.d.ts +42 -0
- package/dist/secrets/patterns.d.ts.map +1 -0
- package/dist/secrets/patterns.js +165 -0
- package/dist/secrets/pre-commit.d.ts +39 -0
- package/dist/secrets/pre-commit.d.ts.map +1 -0
- package/dist/secrets/pre-commit.js +127 -0
- package/dist/secrets/vault-integration.d.ts +83 -0
- package/dist/secrets/vault-integration.d.ts.map +1 -0
- package/dist/secrets/vault-integration.js +295 -0
- package/dist/secrets/vault-providers.d.ts +110 -0
- package/dist/secrets/vault-providers.d.ts.map +1 -0
- package/dist/secrets/vault-providers.js +417 -0
- package/dist/supply-chain/detector.d.ts +80 -0
- package/dist/supply-chain/detector.d.ts.map +1 -0
- package/dist/supply-chain/detector.js +168 -0
- package/dist/supply-chain/index.d.ts +11 -0
- package/dist/supply-chain/index.d.ts.map +1 -0
- package/dist/supply-chain/index.js +26 -0
- package/dist/supply-chain/malicious-db.d.ts +41 -0
- package/dist/supply-chain/malicious-db.d.ts.map +1 -0
- package/dist/supply-chain/malicious-db.js +82 -0
- package/dist/supply-chain/script-analyzer.d.ts +54 -0
- package/dist/supply-chain/script-analyzer.d.ts.map +1 -0
- package/dist/supply-chain/script-analyzer.js +160 -0
- package/dist/supply-chain/typosquat.d.ts +58 -0
- package/dist/supply-chain/typosquat.d.ts.map +1 -0
- package/dist/supply-chain/typosquat.js +257 -0
- package/dist/supply-chain/vulnerability-db.d.ts +114 -0
- package/dist/supply-chain/vulnerability-db.d.ts.map +1 -0
- package/dist/supply-chain/vulnerability-db.js +310 -0
- package/package.json +34 -0
- package/src/__tests__/license/engine.test.ts +250 -0
- package/src/__tests__/supply-chain/typosquat.test.ts +191 -0
- package/src/attack-surface/analyzer.ts +152 -0
- package/src/attack-surface/index.ts +5 -0
- package/src/index.ts +21 -0
- package/src/languages/index.ts +91 -0
- package/src/languages/java-analyzer.ts +490 -0
- package/src/languages/python-analyzer.ts +498 -0
- package/src/license/compatibility-matrix.ts +366 -0
- package/src/license/engine.ts +345 -0
- package/src/license/index.ts +6 -0
- package/src/sbom/generator.ts +355 -0
- package/src/sbom/index.ts +5 -0
- package/src/secrets/guardian.ts +448 -0
- package/src/secrets/index.ts +10 -0
- package/src/secrets/patterns.ts +186 -0
- package/src/secrets/pre-commit.ts +158 -0
- package/src/secrets/vault-integration.ts +360 -0
- package/src/secrets/vault-providers.ts +446 -0
- package/src/supply-chain/detector.ts +252 -0
- package/src/supply-chain/index.ts +11 -0
- package/src/supply-chain/malicious-db.ts +103 -0
- package/src/supply-chain/script-analyzer.ts +194 -0
- package/src/supply-chain/typosquat.ts +302 -0
- package/src/supply-chain/vulnerability-db.ts +386 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vulnerability Database Integration
|
|
3
|
+
*
|
|
4
|
+
* Integrates with multiple vulnerability databases:
|
|
5
|
+
* - OSV (Open Source Vulnerabilities)
|
|
6
|
+
* - GitHub Security Advisories
|
|
7
|
+
* - NVD (National Vulnerability Database)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface Vulnerability {
|
|
11
|
+
id: string;
|
|
12
|
+
source: 'osv' | 'github' | 'nvd' | 'npm';
|
|
13
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
14
|
+
cvssScore?: number;
|
|
15
|
+
title: string;
|
|
16
|
+
description: string;
|
|
17
|
+
affectedVersions: string[];
|
|
18
|
+
patchedVersions: string[];
|
|
19
|
+
references: string[];
|
|
20
|
+
publishedAt: Date;
|
|
21
|
+
updatedAt: Date;
|
|
22
|
+
cwe?: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface VulnerabilityCheckResult {
|
|
26
|
+
package: string;
|
|
27
|
+
version: string;
|
|
28
|
+
vulnerabilities: Vulnerability[];
|
|
29
|
+
isVulnerable: boolean;
|
|
30
|
+
highestSeverity: 'none' | 'low' | 'medium' | 'high' | 'critical';
|
|
31
|
+
recommendedVersion?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface VulnerabilityReport {
|
|
35
|
+
projectPath: string;
|
|
36
|
+
scanDate: Date;
|
|
37
|
+
totalPackages: number;
|
|
38
|
+
vulnerablePackages: number;
|
|
39
|
+
results: VulnerabilityCheckResult[];
|
|
40
|
+
summary: {
|
|
41
|
+
critical: number;
|
|
42
|
+
high: number;
|
|
43
|
+
medium: number;
|
|
44
|
+
low: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const vulnerabilityCache = new Map<string, { data: Vulnerability[]; fetchedAt: Date }>();
|
|
49
|
+
const CACHE_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
50
|
+
|
|
51
|
+
export class VulnerabilityDatabase {
|
|
52
|
+
private osvApiUrl = 'https://api.osv.dev/v1';
|
|
53
|
+
private npmAuditUrl = 'https://registry.npmjs.org/-/npm/v1/security/advisories/bulk';
|
|
54
|
+
// GitHub API available for future enhancement with authentication
|
|
55
|
+
// private githubApiUrl = 'https://api.github.com/advisories';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check a single package for vulnerabilities
|
|
59
|
+
*/
|
|
60
|
+
async checkPackage(name: string, version: string): Promise<VulnerabilityCheckResult> {
|
|
61
|
+
const cacheKey = `${name}@${version}`;
|
|
62
|
+
const cached = vulnerabilityCache.get(cacheKey);
|
|
63
|
+
|
|
64
|
+
if (cached && (Date.now() - cached.fetchedAt.getTime()) < CACHE_TTL_MS) {
|
|
65
|
+
return this.buildResult(name, version, cached.data);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
69
|
+
|
|
70
|
+
// Query multiple sources in parallel
|
|
71
|
+
const [osvVulns, npmVulns] = await Promise.allSettled([
|
|
72
|
+
this.queryOSV(name, version),
|
|
73
|
+
this.queryNpmAudit(name, version),
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
if (osvVulns.status === 'fulfilled') {
|
|
77
|
+
vulnerabilities.push(...osvVulns.value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (npmVulns.status === 'fulfilled') {
|
|
81
|
+
vulnerabilities.push(...npmVulns.value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Deduplicate by ID
|
|
85
|
+
const uniqueVulns = this.deduplicateVulnerabilities(vulnerabilities);
|
|
86
|
+
|
|
87
|
+
// Cache the result
|
|
88
|
+
vulnerabilityCache.set(cacheKey, {
|
|
89
|
+
data: uniqueVulns,
|
|
90
|
+
fetchedAt: new Date(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return this.buildResult(name, version, uniqueVulns);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check multiple packages in bulk
|
|
98
|
+
*/
|
|
99
|
+
async checkPackages(packages: { name: string; version: string }[]): Promise<VulnerabilityCheckResult[]> {
|
|
100
|
+
const batchSize = 20;
|
|
101
|
+
const results: VulnerabilityCheckResult[] = [];
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < packages.length; i += batchSize) {
|
|
104
|
+
const batch = packages.slice(i, i + batchSize);
|
|
105
|
+
const batchResults = await Promise.all(
|
|
106
|
+
batch.map(pkg => this.checkPackage(pkg.name, pkg.version))
|
|
107
|
+
);
|
|
108
|
+
results.push(...batchResults);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Query OSV (Open Source Vulnerabilities) API
|
|
116
|
+
*/
|
|
117
|
+
private async queryOSV(packageName: string, version: string): Promise<Vulnerability[]> {
|
|
118
|
+
try {
|
|
119
|
+
const response = await fetch(`${this.osvApiUrl}/query`, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json',
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
package: {
|
|
126
|
+
name: packageName,
|
|
127
|
+
ecosystem: 'npm',
|
|
128
|
+
},
|
|
129
|
+
version: version,
|
|
130
|
+
}),
|
|
131
|
+
signal: AbortSignal.timeout(10000),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const data = await response.json();
|
|
139
|
+
return this.parseOSVResponse(data);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`OSV query failed for ${packageName}@${version}:`, error);
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Parse OSV API response
|
|
148
|
+
*/
|
|
149
|
+
private parseOSVResponse(data: any): Vulnerability[] {
|
|
150
|
+
if (!data.vulns || !Array.isArray(data.vulns)) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return data.vulns.map((vuln: any) => ({
|
|
155
|
+
id: vuln.id,
|
|
156
|
+
source: 'osv' as const,
|
|
157
|
+
severity: this.mapOSVSeverity(vuln.severity),
|
|
158
|
+
cvssScore: vuln.severity?.[0]?.score,
|
|
159
|
+
title: vuln.summary || vuln.id,
|
|
160
|
+
description: vuln.details || '',
|
|
161
|
+
affectedVersions: this.extractAffectedVersions(vuln.affected),
|
|
162
|
+
patchedVersions: this.extractPatchedVersions(vuln.affected),
|
|
163
|
+
references: (vuln.references || []).map((r: any) => r.url),
|
|
164
|
+
publishedAt: new Date(vuln.published || Date.now()),
|
|
165
|
+
updatedAt: new Date(vuln.modified || Date.now()),
|
|
166
|
+
cwe: vuln.database_specific?.cwe_ids || [],
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Query npm audit API
|
|
172
|
+
*/
|
|
173
|
+
private async queryNpmAudit(packageName: string, version: string): Promise<Vulnerability[]> {
|
|
174
|
+
try {
|
|
175
|
+
const response = await fetch(this.npmAuditUrl, {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: {
|
|
178
|
+
'Content-Type': 'application/json',
|
|
179
|
+
},
|
|
180
|
+
body: JSON.stringify({
|
|
181
|
+
[packageName]: [version],
|
|
182
|
+
}),
|
|
183
|
+
signal: AbortSignal.timeout(10000),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const data = await response.json();
|
|
191
|
+
return this.parseNpmAuditResponse(data, packageName);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error(`npm audit query failed for ${packageName}@${version}:`, error);
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Parse npm audit response
|
|
200
|
+
*/
|
|
201
|
+
private parseNpmAuditResponse(data: any, packageName: string): Vulnerability[] {
|
|
202
|
+
const advisories = data[packageName] || [];
|
|
203
|
+
|
|
204
|
+
return advisories.map((advisory: any) => ({
|
|
205
|
+
id: `npm-${advisory.id || advisory.github_advisory_id}`,
|
|
206
|
+
source: 'npm' as const,
|
|
207
|
+
severity: advisory.severity || 'medium',
|
|
208
|
+
cvssScore: advisory.cvss?.score,
|
|
209
|
+
title: advisory.title || 'Security Advisory',
|
|
210
|
+
description: advisory.overview || advisory.recommendation || '',
|
|
211
|
+
affectedVersions: [advisory.vulnerable_versions || '*'],
|
|
212
|
+
patchedVersions: [advisory.patched_versions || 'No patch available'],
|
|
213
|
+
references: [advisory.url].filter(Boolean),
|
|
214
|
+
publishedAt: new Date(advisory.created || Date.now()),
|
|
215
|
+
updatedAt: new Date(advisory.updated || Date.now()),
|
|
216
|
+
cwe: advisory.cwe ? [advisory.cwe] : [],
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Map OSV severity to standard levels
|
|
222
|
+
*/
|
|
223
|
+
private mapOSVSeverity(severity: any): 'low' | 'medium' | 'high' | 'critical' {
|
|
224
|
+
if (!severity || !Array.isArray(severity) || severity.length === 0) {
|
|
225
|
+
return 'medium';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const score = severity[0]?.score || 0;
|
|
229
|
+
if (score >= 9.0) return 'critical';
|
|
230
|
+
if (score >= 7.0) return 'high';
|
|
231
|
+
if (score >= 4.0) return 'medium';
|
|
232
|
+
return 'low';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Extract affected versions from OSV format
|
|
237
|
+
*/
|
|
238
|
+
private extractAffectedVersions(affected: any[]): string[] {
|
|
239
|
+
if (!affected || !Array.isArray(affected)) {
|
|
240
|
+
return ['*'];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const versions: string[] = [];
|
|
244
|
+
for (const aff of affected) {
|
|
245
|
+
if (aff.versions) {
|
|
246
|
+
versions.push(...aff.versions);
|
|
247
|
+
}
|
|
248
|
+
if (aff.ranges) {
|
|
249
|
+
for (const range of aff.ranges) {
|
|
250
|
+
if (range.events) {
|
|
251
|
+
const introduced = range.events.find((e: any) => e.introduced);
|
|
252
|
+
const fixed = range.events.find((e: any) => e.fixed);
|
|
253
|
+
if (introduced) {
|
|
254
|
+
versions.push(`>=${introduced.introduced}${fixed ? ` <${fixed.fixed}` : ''}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return versions.length > 0 ? versions : ['*'];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Extract patched versions from OSV format
|
|
266
|
+
*/
|
|
267
|
+
private extractPatchedVersions(affected: any[]): string[] {
|
|
268
|
+
if (!affected || !Array.isArray(affected)) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const versions: string[] = [];
|
|
273
|
+
for (const aff of affected) {
|
|
274
|
+
if (aff.ranges) {
|
|
275
|
+
for (const range of aff.ranges) {
|
|
276
|
+
if (range.events) {
|
|
277
|
+
const fixed = range.events.find((e: any) => e.fixed);
|
|
278
|
+
if (fixed) {
|
|
279
|
+
versions.push(fixed.fixed);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return versions;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Deduplicate vulnerabilities by ID
|
|
291
|
+
*/
|
|
292
|
+
private deduplicateVulnerabilities(vulns: Vulnerability[]): Vulnerability[] {
|
|
293
|
+
const seen = new Set<string>();
|
|
294
|
+
return vulns.filter(vuln => {
|
|
295
|
+
if (seen.has(vuln.id)) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
seen.add(vuln.id);
|
|
299
|
+
return true;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Build result object
|
|
305
|
+
*/
|
|
306
|
+
private buildResult(name: string, version: string, vulnerabilities: Vulnerability[]): VulnerabilityCheckResult {
|
|
307
|
+
const severityOrder = { critical: 4, high: 3, medium: 2, low: 1, none: 0 };
|
|
308
|
+
|
|
309
|
+
let highestSeverity: 'none' | 'low' | 'medium' | 'high' | 'critical' = 'none';
|
|
310
|
+
for (const vuln of vulnerabilities) {
|
|
311
|
+
if (severityOrder[vuln.severity] > severityOrder[highestSeverity]) {
|
|
312
|
+
highestSeverity = vuln.severity;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Find recommended version from patches
|
|
317
|
+
const patchedVersions = vulnerabilities.flatMap(v => v.patchedVersions).filter(Boolean);
|
|
318
|
+
const recommendedVersion = patchedVersions.length > 0 ? patchedVersions[0] : undefined;
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
package: name,
|
|
322
|
+
version,
|
|
323
|
+
vulnerabilities,
|
|
324
|
+
isVulnerable: vulnerabilities.length > 0,
|
|
325
|
+
highestSeverity,
|
|
326
|
+
recommendedVersion,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Generate a full vulnerability report for a project
|
|
332
|
+
*/
|
|
333
|
+
async generateReport(projectPath: string, packages: { name: string; version: string }[]): Promise<VulnerabilityReport> {
|
|
334
|
+
const results = await this.checkPackages(packages);
|
|
335
|
+
|
|
336
|
+
const summary = {
|
|
337
|
+
critical: 0,
|
|
338
|
+
high: 0,
|
|
339
|
+
medium: 0,
|
|
340
|
+
low: 0,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
for (const result of results) {
|
|
344
|
+
for (const vuln of result.vulnerabilities) {
|
|
345
|
+
summary[vuln.severity]++;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const vulnerablePackages = results.filter(r => r.isVulnerable).length;
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
projectPath,
|
|
353
|
+
scanDate: new Date(),
|
|
354
|
+
totalPackages: packages.length,
|
|
355
|
+
vulnerablePackages,
|
|
356
|
+
results,
|
|
357
|
+
summary,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Clear vulnerability cache
|
|
363
|
+
*/
|
|
364
|
+
clearCache(): void {
|
|
365
|
+
vulnerabilityCache.clear();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Get cache statistics
|
|
370
|
+
*/
|
|
371
|
+
getCacheStats(): { size: number; oldestEntry: Date | null } {
|
|
372
|
+
let oldest: Date | null = null;
|
|
373
|
+
for (const entry of vulnerabilityCache.values()) {
|
|
374
|
+
if (!oldest || entry.fetchedAt < oldest) {
|
|
375
|
+
oldest = entry.fetchedAt;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
size: vulnerabilityCache.size,
|
|
380
|
+
oldestEntry: oldest,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Export singleton
|
|
386
|
+
export const vulnerabilityDatabase = new VulnerabilityDatabase();
|