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.
Files changed (95) hide show
  1. package/dist/attack-surface/analyzer.d.ts +50 -0
  2. package/dist/attack-surface/analyzer.d.ts.map +1 -0
  3. package/dist/attack-surface/analyzer.js +83 -0
  4. package/dist/attack-surface/index.d.ts +5 -0
  5. package/dist/attack-surface/index.d.ts.map +1 -0
  6. package/dist/attack-surface/index.js +20 -0
  7. package/dist/index.d.ts +15 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +33 -0
  10. package/dist/languages/index.d.ts +21 -0
  11. package/dist/languages/index.d.ts.map +1 -0
  12. package/dist/languages/index.js +78 -0
  13. package/dist/languages/java-analyzer.d.ts +72 -0
  14. package/dist/languages/java-analyzer.d.ts.map +1 -0
  15. package/dist/languages/java-analyzer.js +417 -0
  16. package/dist/languages/python-analyzer.d.ts +70 -0
  17. package/dist/languages/python-analyzer.d.ts.map +1 -0
  18. package/dist/languages/python-analyzer.js +425 -0
  19. package/dist/license/compatibility-matrix.d.ts +28 -0
  20. package/dist/license/compatibility-matrix.d.ts.map +1 -0
  21. package/dist/license/compatibility-matrix.js +323 -0
  22. package/dist/license/engine.d.ts +77 -0
  23. package/dist/license/engine.d.ts.map +1 -0
  24. package/dist/license/engine.js +264 -0
  25. package/dist/license/index.d.ts +6 -0
  26. package/dist/license/index.d.ts.map +1 -0
  27. package/dist/license/index.js +21 -0
  28. package/dist/sbom/generator.d.ts +108 -0
  29. package/dist/sbom/generator.d.ts.map +1 -0
  30. package/dist/sbom/generator.js +271 -0
  31. package/dist/sbom/index.d.ts +5 -0
  32. package/dist/sbom/index.d.ts.map +1 -0
  33. package/dist/sbom/index.js +20 -0
  34. package/dist/secrets/guardian.d.ts +113 -0
  35. package/dist/secrets/guardian.d.ts.map +1 -0
  36. package/dist/secrets/guardian.js +334 -0
  37. package/dist/secrets/index.d.ts +10 -0
  38. package/dist/secrets/index.d.ts.map +1 -0
  39. package/dist/secrets/index.js +30 -0
  40. package/dist/secrets/patterns.d.ts +42 -0
  41. package/dist/secrets/patterns.d.ts.map +1 -0
  42. package/dist/secrets/patterns.js +165 -0
  43. package/dist/secrets/pre-commit.d.ts +39 -0
  44. package/dist/secrets/pre-commit.d.ts.map +1 -0
  45. package/dist/secrets/pre-commit.js +127 -0
  46. package/dist/secrets/vault-integration.d.ts +83 -0
  47. package/dist/secrets/vault-integration.d.ts.map +1 -0
  48. package/dist/secrets/vault-integration.js +295 -0
  49. package/dist/secrets/vault-providers.d.ts +110 -0
  50. package/dist/secrets/vault-providers.d.ts.map +1 -0
  51. package/dist/secrets/vault-providers.js +417 -0
  52. package/dist/supply-chain/detector.d.ts +80 -0
  53. package/dist/supply-chain/detector.d.ts.map +1 -0
  54. package/dist/supply-chain/detector.js +168 -0
  55. package/dist/supply-chain/index.d.ts +11 -0
  56. package/dist/supply-chain/index.d.ts.map +1 -0
  57. package/dist/supply-chain/index.js +26 -0
  58. package/dist/supply-chain/malicious-db.d.ts +41 -0
  59. package/dist/supply-chain/malicious-db.d.ts.map +1 -0
  60. package/dist/supply-chain/malicious-db.js +82 -0
  61. package/dist/supply-chain/script-analyzer.d.ts +54 -0
  62. package/dist/supply-chain/script-analyzer.d.ts.map +1 -0
  63. package/dist/supply-chain/script-analyzer.js +160 -0
  64. package/dist/supply-chain/typosquat.d.ts +58 -0
  65. package/dist/supply-chain/typosquat.d.ts.map +1 -0
  66. package/dist/supply-chain/typosquat.js +257 -0
  67. package/dist/supply-chain/vulnerability-db.d.ts +114 -0
  68. package/dist/supply-chain/vulnerability-db.d.ts.map +1 -0
  69. package/dist/supply-chain/vulnerability-db.js +310 -0
  70. package/package.json +34 -0
  71. package/src/__tests__/license/engine.test.ts +250 -0
  72. package/src/__tests__/supply-chain/typosquat.test.ts +191 -0
  73. package/src/attack-surface/analyzer.ts +152 -0
  74. package/src/attack-surface/index.ts +5 -0
  75. package/src/index.ts +21 -0
  76. package/src/languages/index.ts +91 -0
  77. package/src/languages/java-analyzer.ts +490 -0
  78. package/src/languages/python-analyzer.ts +498 -0
  79. package/src/license/compatibility-matrix.ts +366 -0
  80. package/src/license/engine.ts +345 -0
  81. package/src/license/index.ts +6 -0
  82. package/src/sbom/generator.ts +355 -0
  83. package/src/sbom/index.ts +5 -0
  84. package/src/secrets/guardian.ts +448 -0
  85. package/src/secrets/index.ts +10 -0
  86. package/src/secrets/patterns.ts +186 -0
  87. package/src/secrets/pre-commit.ts +158 -0
  88. package/src/secrets/vault-integration.ts +360 -0
  89. package/src/secrets/vault-providers.ts +446 -0
  90. package/src/supply-chain/detector.ts +252 -0
  91. package/src/supply-chain/index.ts +11 -0
  92. package/src/supply-chain/malicious-db.ts +103 -0
  93. package/src/supply-chain/script-analyzer.ts +194 -0
  94. package/src/supply-chain/typosquat.ts +302 -0
  95. package/src/supply-chain/vulnerability-db.ts +386 -0
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ /**
3
+ * Typosquatting Detection
4
+ *
5
+ * Detects potential typosquatting attacks against popular packages
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.typosquatDetector = exports.TyposquatDetector = void 0;
9
+ /**
10
+ * Top 100 most popular npm packages (simplified list)
11
+ */
12
+ const POPULAR_PACKAGES = [
13
+ 'react', 'vue', 'angular', 'express', 'next', 'axios', 'lodash', 'webpack',
14
+ 'typescript', 'eslint', 'prettier', 'jest', 'mocha', 'chai', 'babel',
15
+ 'moment', 'dayjs', 'date-fns', 'redux', 'mobx', 'rxjs', 'socket.io',
16
+ 'fastify', 'koa', 'hapi', 'nestjs', 'prisma', 'mongoose', 'sequelize',
17
+ 'typeorm', 'knex', 'pg', 'mysql', 'redis', 'mongodb', 'sqlite3',
18
+ 'passport', 'jsonwebtoken', 'bcrypt', 'crypto-js', 'uuid', 'nanoid',
19
+ 'dotenv', 'config', 'yargs', 'commander', 'inquirer', 'chalk', 'ora',
20
+ 'debug', 'winston', 'pino', 'morgan', 'cors', 'helmet', 'compression',
21
+ 'multer', 'body-parser', 'cookie-parser', 'express-session', 'passport',
22
+ 'nodemailer', 'sendgrid', 'twilio', 'stripe', 'aws-sdk', 'google-cloud',
23
+ 'firebase', 'azure', 'docker', 'kubernetes', 'terraform', 'ansible',
24
+ 'jenkins', 'gitlab', 'github', 'bitbucket', 'jira', 'confluence',
25
+ 'slack', 'discord', 'telegram', 'whatsapp', 'sentry', 'datadog',
26
+ 'newrelic', 'prometheus', 'grafana', 'elasticsearch', 'kibana', 'logstash',
27
+ 'kafka', 'rabbitmq', 'celery', 'bull', 'agenda', 'cron', 'node-schedule',
28
+ ];
29
+ class TyposquatDetector {
30
+ popularPackages;
31
+ constructor() {
32
+ this.popularPackages = new Set(POPULAR_PACKAGES);
33
+ }
34
+ /**
35
+ * Detect typosquatting
36
+ */
37
+ async detectTyposquatting(packageName) {
38
+ const patterns = [];
39
+ let targetPackage;
40
+ let maxSimilarity = 0;
41
+ // Check against popular packages
42
+ for (const popular of this.popularPackages) {
43
+ // Skip if exact match
44
+ if (packageName === popular) {
45
+ continue;
46
+ }
47
+ // Check various typosquatting techniques
48
+ const techniques = [
49
+ this.checkCharacterSwap(packageName, popular),
50
+ this.checkMissingCharacter(packageName, popular),
51
+ this.checkExtraCharacter(packageName, popular),
52
+ this.checkHomoglyph(packageName, popular),
53
+ this.checkCombosquatting(packageName, popular),
54
+ this.checkLevenshtein(packageName, popular),
55
+ ];
56
+ for (const technique of techniques) {
57
+ if (technique.isMatch) {
58
+ patterns.push(technique.pattern);
59
+ if (technique.similarity > maxSimilarity) {
60
+ maxSimilarity = technique.similarity;
61
+ targetPackage = popular;
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return {
67
+ isTyposquat: patterns.length > 0,
68
+ suspiciousPackage: packageName,
69
+ targetPackage,
70
+ similarity: maxSimilarity,
71
+ patterns,
72
+ };
73
+ }
74
+ /**
75
+ * Check for character swap (e.g., raect vs react)
76
+ */
77
+ checkCharacterSwap(pkg, popular) {
78
+ if (Math.abs(pkg.length - popular.length) > 0) {
79
+ return { isMatch: false, pattern: '', similarity: 0 };
80
+ }
81
+ // Try swapping adjacent characters
82
+ for (let i = 0; i < popular.length - 1; i++) {
83
+ const swapped = popular.substring(0, i) +
84
+ popular[i + 1] +
85
+ popular[i] +
86
+ popular.substring(i + 2);
87
+ if (swapped === pkg) {
88
+ return {
89
+ isMatch: true,
90
+ pattern: 'character_swap',
91
+ similarity: 0.95,
92
+ };
93
+ }
94
+ }
95
+ return { isMatch: false, pattern: '', similarity: 0 };
96
+ }
97
+ /**
98
+ * Check for missing character (e.g., reat vs react)
99
+ */
100
+ checkMissingCharacter(pkg, popular) {
101
+ if (pkg.length !== popular.length - 1) {
102
+ return { isMatch: false, pattern: '', similarity: 0 };
103
+ }
104
+ // Try removing each character
105
+ for (let i = 0; i < popular.length; i++) {
106
+ const removed = popular.substring(0, i) + popular.substring(i + 1);
107
+ if (removed === pkg) {
108
+ return {
109
+ isMatch: true,
110
+ pattern: 'missing_character',
111
+ similarity: 0.9,
112
+ };
113
+ }
114
+ }
115
+ return { isMatch: false, pattern: '', similarity: 0 };
116
+ }
117
+ /**
118
+ * Check for extra character (e.g., reactt vs react)
119
+ */
120
+ checkExtraCharacter(pkg, popular) {
121
+ if (pkg.length !== popular.length + 1) {
122
+ return { isMatch: false, pattern: '', similarity: 0 };
123
+ }
124
+ // Try removing each character from pkg
125
+ for (let i = 0; i < pkg.length; i++) {
126
+ const removed = pkg.substring(0, i) + pkg.substring(i + 1);
127
+ if (removed === popular) {
128
+ return {
129
+ isMatch: true,
130
+ pattern: 'extra_character',
131
+ similarity: 0.9,
132
+ };
133
+ }
134
+ }
135
+ return { isMatch: false, pattern: '', similarity: 0 };
136
+ }
137
+ /**
138
+ * Check for homoglyph substitution (e.g., react with Cyrillic 'а')
139
+ */
140
+ checkHomoglyph(pkg, popular) {
141
+ // Common homoglyphs
142
+ const homoglyphs = {
143
+ 'a': ['а', 'ɑ', 'α'], // Cyrillic/Greek a
144
+ 'e': ['е', ' е'], // Cyrillic e
145
+ 'o': ['о', 'ο', '0'], // Cyrillic/Greek o, zero
146
+ 'i': ['і', 'ı', 'l', '1'], // Cyrillic i, Turkish i, l, one
147
+ 'c': ['с', 'ϲ'], // Cyrillic c
148
+ 'p': ['р'], // Cyrillic p
149
+ 'x': ['х', 'χ'], // Cyrillic/Greek x
150
+ };
151
+ // Normalize both strings
152
+ const normalize = (str) => {
153
+ let normalized = str;
154
+ for (const [latin, alternates] of Object.entries(homoglyphs)) {
155
+ for (const alt of alternates) {
156
+ normalized = normalized.replace(new RegExp(alt, 'g'), latin);
157
+ }
158
+ }
159
+ return normalized;
160
+ };
161
+ const normalizedPkg = normalize(pkg);
162
+ if (normalizedPkg === popular && normalizedPkg !== pkg) {
163
+ return {
164
+ isMatch: true,
165
+ pattern: 'homoglyph',
166
+ similarity: 0.95,
167
+ };
168
+ }
169
+ return { isMatch: false, pattern: '', similarity: 0 };
170
+ }
171
+ /**
172
+ * Check for combosquatting (e.g., react-native-safe vs react)
173
+ */
174
+ checkCombosquatting(pkg, popular) {
175
+ if (pkg.includes(popular) && pkg !== popular) {
176
+ // Check if it's just adding common suffixes/prefixes
177
+ const commonAdditions = ['-js', '-node', '-utils', '-core', '-plugin', '-webpack', '-babel'];
178
+ for (const addition of commonAdditions) {
179
+ if (pkg === popular + addition || pkg === addition + popular) {
180
+ return {
181
+ isMatch: true,
182
+ pattern: 'combosquatting',
183
+ similarity: 0.7,
184
+ };
185
+ }
186
+ }
187
+ }
188
+ return { isMatch: false, pattern: '', similarity: 0 };
189
+ }
190
+ /**
191
+ * Check Levenshtein distance
192
+ */
193
+ checkLevenshtein(pkg, popular) {
194
+ const distance = this.levenshteinDistance(pkg, popular);
195
+ const maxLength = Math.max(pkg.length, popular.length);
196
+ const similarity = 1 - (distance / maxLength);
197
+ // Consider it suspicious if similarity > 0.8
198
+ if (similarity >= 0.8 && similarity < 1.0) {
199
+ return {
200
+ isMatch: true,
201
+ pattern: 'levenshtein_distance',
202
+ similarity,
203
+ };
204
+ }
205
+ return { isMatch: false, pattern: '', similarity: 0 };
206
+ }
207
+ /**
208
+ * Calculate Levenshtein distance
209
+ */
210
+ levenshteinDistance(a, b) {
211
+ const matrix = [];
212
+ // Initialize matrix with proper dimensions
213
+ for (let i = 0; i <= b.length; i++) {
214
+ matrix[i] = [];
215
+ for (let j = 0; j <= a.length; j++) {
216
+ matrix[i][j] = 0;
217
+ }
218
+ }
219
+ // Set first column
220
+ for (let i = 0; i <= b.length; i++) {
221
+ matrix[i][0] = i;
222
+ }
223
+ // Set first row
224
+ for (let j = 0; j <= a.length; j++) {
225
+ matrix[0][j] = j;
226
+ }
227
+ for (let i = 1; i <= b.length; i++) {
228
+ for (let j = 1; j <= a.length; j++) {
229
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
230
+ matrix[i][j] = matrix[i - 1][j - 1];
231
+ }
232
+ else {
233
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
234
+ matrix[i][j - 1] + 1, // insertion
235
+ matrix[i - 1][j] + 1 // deletion
236
+ );
237
+ }
238
+ }
239
+ }
240
+ return matrix[b.length][a.length];
241
+ }
242
+ /**
243
+ * Get popular packages list
244
+ */
245
+ async getPopularPackages() {
246
+ return Array.from(this.popularPackages);
247
+ }
248
+ /**
249
+ * Add custom popular package
250
+ */
251
+ addPopularPackage(packageName) {
252
+ this.popularPackages.add(packageName);
253
+ }
254
+ }
255
+ exports.TyposquatDetector = TyposquatDetector;
256
+ // Export singleton
257
+ exports.typosquatDetector = new TyposquatDetector();
@@ -0,0 +1,114 @@
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
+ export interface Vulnerability {
10
+ id: string;
11
+ source: 'osv' | 'github' | 'nvd' | 'npm';
12
+ severity: 'low' | 'medium' | 'high' | 'critical';
13
+ cvssScore?: number;
14
+ title: string;
15
+ description: string;
16
+ affectedVersions: string[];
17
+ patchedVersions: string[];
18
+ references: string[];
19
+ publishedAt: Date;
20
+ updatedAt: Date;
21
+ cwe?: string[];
22
+ }
23
+ export interface VulnerabilityCheckResult {
24
+ package: string;
25
+ version: string;
26
+ vulnerabilities: Vulnerability[];
27
+ isVulnerable: boolean;
28
+ highestSeverity: 'none' | 'low' | 'medium' | 'high' | 'critical';
29
+ recommendedVersion?: string;
30
+ }
31
+ export interface VulnerabilityReport {
32
+ projectPath: string;
33
+ scanDate: Date;
34
+ totalPackages: number;
35
+ vulnerablePackages: number;
36
+ results: VulnerabilityCheckResult[];
37
+ summary: {
38
+ critical: number;
39
+ high: number;
40
+ medium: number;
41
+ low: number;
42
+ };
43
+ }
44
+ export declare class VulnerabilityDatabase {
45
+ private osvApiUrl;
46
+ private npmAuditUrl;
47
+ /**
48
+ * Check a single package for vulnerabilities
49
+ */
50
+ checkPackage(name: string, version: string): Promise<VulnerabilityCheckResult>;
51
+ /**
52
+ * Check multiple packages in bulk
53
+ */
54
+ checkPackages(packages: {
55
+ name: string;
56
+ version: string;
57
+ }[]): Promise<VulnerabilityCheckResult[]>;
58
+ /**
59
+ * Query OSV (Open Source Vulnerabilities) API
60
+ */
61
+ private queryOSV;
62
+ /**
63
+ * Parse OSV API response
64
+ */
65
+ private parseOSVResponse;
66
+ /**
67
+ * Query npm audit API
68
+ */
69
+ private queryNpmAudit;
70
+ /**
71
+ * Parse npm audit response
72
+ */
73
+ private parseNpmAuditResponse;
74
+ /**
75
+ * Map OSV severity to standard levels
76
+ */
77
+ private mapOSVSeverity;
78
+ /**
79
+ * Extract affected versions from OSV format
80
+ */
81
+ private extractAffectedVersions;
82
+ /**
83
+ * Extract patched versions from OSV format
84
+ */
85
+ private extractPatchedVersions;
86
+ /**
87
+ * Deduplicate vulnerabilities by ID
88
+ */
89
+ private deduplicateVulnerabilities;
90
+ /**
91
+ * Build result object
92
+ */
93
+ private buildResult;
94
+ /**
95
+ * Generate a full vulnerability report for a project
96
+ */
97
+ generateReport(projectPath: string, packages: {
98
+ name: string;
99
+ version: string;
100
+ }[]): Promise<VulnerabilityReport>;
101
+ /**
102
+ * Clear vulnerability cache
103
+ */
104
+ clearCache(): void;
105
+ /**
106
+ * Get cache statistics
107
+ */
108
+ getCacheStats(): {
109
+ size: number;
110
+ oldestEntry: Date | null;
111
+ };
112
+ }
113
+ export declare const vulnerabilityDatabase: VulnerabilityDatabase;
114
+ //# sourceMappingURL=vulnerability-db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vulnerability-db.d.ts","sourceRoot":"","sources":["../../src/supply-chain/vulnerability-db.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;IACzC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,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;AAKD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,WAAW,CAAkE;IAIrF;;OAEG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAoCpF;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAevG;;OAEG;YACW,QAAQ;IA6BtB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;OAEG;YACW,aAAa;IAyB3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAmB7B;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAsB9B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAWlC;;OAEG;IACH,OAAO,CAAC,WAAW;IAwBnB;;OAEG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4BtH;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAA;KAAE;CAY5D;AAGD,eAAO,MAAM,qBAAqB,uBAA8B,CAAC"}
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ /**
3
+ * Vulnerability Database Integration
4
+ *
5
+ * Integrates with multiple vulnerability databases:
6
+ * - OSV (Open Source Vulnerabilities)
7
+ * - GitHub Security Advisories
8
+ * - NVD (National Vulnerability Database)
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.vulnerabilityDatabase = exports.VulnerabilityDatabase = void 0;
12
+ const vulnerabilityCache = new Map();
13
+ const CACHE_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours
14
+ class VulnerabilityDatabase {
15
+ osvApiUrl = 'https://api.osv.dev/v1';
16
+ npmAuditUrl = 'https://registry.npmjs.org/-/npm/v1/security/advisories/bulk';
17
+ // GitHub API available for future enhancement with authentication
18
+ // private githubApiUrl = 'https://api.github.com/advisories';
19
+ /**
20
+ * Check a single package for vulnerabilities
21
+ */
22
+ async checkPackage(name, version) {
23
+ const cacheKey = `${name}@${version}`;
24
+ const cached = vulnerabilityCache.get(cacheKey);
25
+ if (cached && (Date.now() - cached.fetchedAt.getTime()) < CACHE_TTL_MS) {
26
+ return this.buildResult(name, version, cached.data);
27
+ }
28
+ const vulnerabilities = [];
29
+ // Query multiple sources in parallel
30
+ const [osvVulns, npmVulns] = await Promise.allSettled([
31
+ this.queryOSV(name, version),
32
+ this.queryNpmAudit(name, version),
33
+ ]);
34
+ if (osvVulns.status === 'fulfilled') {
35
+ vulnerabilities.push(...osvVulns.value);
36
+ }
37
+ if (npmVulns.status === 'fulfilled') {
38
+ vulnerabilities.push(...npmVulns.value);
39
+ }
40
+ // Deduplicate by ID
41
+ const uniqueVulns = this.deduplicateVulnerabilities(vulnerabilities);
42
+ // Cache the result
43
+ vulnerabilityCache.set(cacheKey, {
44
+ data: uniqueVulns,
45
+ fetchedAt: new Date(),
46
+ });
47
+ return this.buildResult(name, version, uniqueVulns);
48
+ }
49
+ /**
50
+ * Check multiple packages in bulk
51
+ */
52
+ async checkPackages(packages) {
53
+ const batchSize = 20;
54
+ const results = [];
55
+ for (let i = 0; i < packages.length; i += batchSize) {
56
+ const batch = packages.slice(i, i + batchSize);
57
+ const batchResults = await Promise.all(batch.map(pkg => this.checkPackage(pkg.name, pkg.version)));
58
+ results.push(...batchResults);
59
+ }
60
+ return results;
61
+ }
62
+ /**
63
+ * Query OSV (Open Source Vulnerabilities) API
64
+ */
65
+ async queryOSV(packageName, version) {
66
+ try {
67
+ const response = await fetch(`${this.osvApiUrl}/query`, {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ },
72
+ body: JSON.stringify({
73
+ package: {
74
+ name: packageName,
75
+ ecosystem: 'npm',
76
+ },
77
+ version: version,
78
+ }),
79
+ signal: AbortSignal.timeout(10000),
80
+ });
81
+ if (!response.ok) {
82
+ return [];
83
+ }
84
+ const data = await response.json();
85
+ return this.parseOSVResponse(data);
86
+ }
87
+ catch (error) {
88
+ console.error(`OSV query failed for ${packageName}@${version}:`, error);
89
+ return [];
90
+ }
91
+ }
92
+ /**
93
+ * Parse OSV API response
94
+ */
95
+ parseOSVResponse(data) {
96
+ if (!data.vulns || !Array.isArray(data.vulns)) {
97
+ return [];
98
+ }
99
+ return data.vulns.map((vuln) => ({
100
+ id: vuln.id,
101
+ source: 'osv',
102
+ severity: this.mapOSVSeverity(vuln.severity),
103
+ cvssScore: vuln.severity?.[0]?.score,
104
+ title: vuln.summary || vuln.id,
105
+ description: vuln.details || '',
106
+ affectedVersions: this.extractAffectedVersions(vuln.affected),
107
+ patchedVersions: this.extractPatchedVersions(vuln.affected),
108
+ references: (vuln.references || []).map((r) => r.url),
109
+ publishedAt: new Date(vuln.published || Date.now()),
110
+ updatedAt: new Date(vuln.modified || Date.now()),
111
+ cwe: vuln.database_specific?.cwe_ids || [],
112
+ }));
113
+ }
114
+ /**
115
+ * Query npm audit API
116
+ */
117
+ async queryNpmAudit(packageName, version) {
118
+ try {
119
+ const response = await fetch(this.npmAuditUrl, {
120
+ method: 'POST',
121
+ headers: {
122
+ 'Content-Type': 'application/json',
123
+ },
124
+ body: JSON.stringify({
125
+ [packageName]: [version],
126
+ }),
127
+ signal: AbortSignal.timeout(10000),
128
+ });
129
+ if (!response.ok) {
130
+ return [];
131
+ }
132
+ const data = await response.json();
133
+ return this.parseNpmAuditResponse(data, packageName);
134
+ }
135
+ catch (error) {
136
+ console.error(`npm audit query failed for ${packageName}@${version}:`, error);
137
+ return [];
138
+ }
139
+ }
140
+ /**
141
+ * Parse npm audit response
142
+ */
143
+ parseNpmAuditResponse(data, packageName) {
144
+ const advisories = data[packageName] || [];
145
+ return advisories.map((advisory) => ({
146
+ id: `npm-${advisory.id || advisory.github_advisory_id}`,
147
+ source: 'npm',
148
+ severity: advisory.severity || 'medium',
149
+ cvssScore: advisory.cvss?.score,
150
+ title: advisory.title || 'Security Advisory',
151
+ description: advisory.overview || advisory.recommendation || '',
152
+ affectedVersions: [advisory.vulnerable_versions || '*'],
153
+ patchedVersions: [advisory.patched_versions || 'No patch available'],
154
+ references: [advisory.url].filter(Boolean),
155
+ publishedAt: new Date(advisory.created || Date.now()),
156
+ updatedAt: new Date(advisory.updated || Date.now()),
157
+ cwe: advisory.cwe ? [advisory.cwe] : [],
158
+ }));
159
+ }
160
+ /**
161
+ * Map OSV severity to standard levels
162
+ */
163
+ mapOSVSeverity(severity) {
164
+ if (!severity || !Array.isArray(severity) || severity.length === 0) {
165
+ return 'medium';
166
+ }
167
+ const score = severity[0]?.score || 0;
168
+ if (score >= 9.0)
169
+ return 'critical';
170
+ if (score >= 7.0)
171
+ return 'high';
172
+ if (score >= 4.0)
173
+ return 'medium';
174
+ return 'low';
175
+ }
176
+ /**
177
+ * Extract affected versions from OSV format
178
+ */
179
+ extractAffectedVersions(affected) {
180
+ if (!affected || !Array.isArray(affected)) {
181
+ return ['*'];
182
+ }
183
+ const versions = [];
184
+ for (const aff of affected) {
185
+ if (aff.versions) {
186
+ versions.push(...aff.versions);
187
+ }
188
+ if (aff.ranges) {
189
+ for (const range of aff.ranges) {
190
+ if (range.events) {
191
+ const introduced = range.events.find((e) => e.introduced);
192
+ const fixed = range.events.find((e) => e.fixed);
193
+ if (introduced) {
194
+ versions.push(`>=${introduced.introduced}${fixed ? ` <${fixed.fixed}` : ''}`);
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
200
+ return versions.length > 0 ? versions : ['*'];
201
+ }
202
+ /**
203
+ * Extract patched versions from OSV format
204
+ */
205
+ extractPatchedVersions(affected) {
206
+ if (!affected || !Array.isArray(affected)) {
207
+ return [];
208
+ }
209
+ const versions = [];
210
+ for (const aff of affected) {
211
+ if (aff.ranges) {
212
+ for (const range of aff.ranges) {
213
+ if (range.events) {
214
+ const fixed = range.events.find((e) => e.fixed);
215
+ if (fixed) {
216
+ versions.push(fixed.fixed);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+ return versions;
223
+ }
224
+ /**
225
+ * Deduplicate vulnerabilities by ID
226
+ */
227
+ deduplicateVulnerabilities(vulns) {
228
+ const seen = new Set();
229
+ return vulns.filter(vuln => {
230
+ if (seen.has(vuln.id)) {
231
+ return false;
232
+ }
233
+ seen.add(vuln.id);
234
+ return true;
235
+ });
236
+ }
237
+ /**
238
+ * Build result object
239
+ */
240
+ buildResult(name, version, vulnerabilities) {
241
+ const severityOrder = { critical: 4, high: 3, medium: 2, low: 1, none: 0 };
242
+ let highestSeverity = 'none';
243
+ for (const vuln of vulnerabilities) {
244
+ if (severityOrder[vuln.severity] > severityOrder[highestSeverity]) {
245
+ highestSeverity = vuln.severity;
246
+ }
247
+ }
248
+ // Find recommended version from patches
249
+ const patchedVersions = vulnerabilities.flatMap(v => v.patchedVersions).filter(Boolean);
250
+ const recommendedVersion = patchedVersions.length > 0 ? patchedVersions[0] : undefined;
251
+ return {
252
+ package: name,
253
+ version,
254
+ vulnerabilities,
255
+ isVulnerable: vulnerabilities.length > 0,
256
+ highestSeverity,
257
+ recommendedVersion,
258
+ };
259
+ }
260
+ /**
261
+ * Generate a full vulnerability report for a project
262
+ */
263
+ async generateReport(projectPath, packages) {
264
+ const results = await this.checkPackages(packages);
265
+ const summary = {
266
+ critical: 0,
267
+ high: 0,
268
+ medium: 0,
269
+ low: 0,
270
+ };
271
+ for (const result of results) {
272
+ for (const vuln of result.vulnerabilities) {
273
+ summary[vuln.severity]++;
274
+ }
275
+ }
276
+ const vulnerablePackages = results.filter(r => r.isVulnerable).length;
277
+ return {
278
+ projectPath,
279
+ scanDate: new Date(),
280
+ totalPackages: packages.length,
281
+ vulnerablePackages,
282
+ results,
283
+ summary,
284
+ };
285
+ }
286
+ /**
287
+ * Clear vulnerability cache
288
+ */
289
+ clearCache() {
290
+ vulnerabilityCache.clear();
291
+ }
292
+ /**
293
+ * Get cache statistics
294
+ */
295
+ getCacheStats() {
296
+ let oldest = null;
297
+ for (const entry of vulnerabilityCache.values()) {
298
+ if (!oldest || entry.fetchedAt < oldest) {
299
+ oldest = entry.fetchedAt;
300
+ }
301
+ }
302
+ return {
303
+ size: vulnerabilityCache.size,
304
+ oldestEntry: oldest,
305
+ };
306
+ }
307
+ }
308
+ exports.VulnerabilityDatabase = VulnerabilityDatabase;
309
+ // Export singleton
310
+ exports.vulnerabilityDatabase = new VulnerabilityDatabase();