@openrewrite/recipes-nodejs 0.36.0-20251211-172625 → 0.36.0-20251212-170419

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.
@@ -0,0 +1,8 @@
1
+ /*
2
+ * Copyright 2025 the original author or authors.
3
+ *
4
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
5
+ */
6
+
7
+ export * from "./vulnerability";
8
+ export * from "./dependency-vulnerability-check";
@@ -0,0 +1,265 @@
1
+ /*
2
+ * Copyright 2025 the original author or authors.
3
+ *
4
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+
10
+ /**
11
+ * Severity levels for vulnerabilities, ordered from lowest to highest.
12
+ */
13
+ export enum Severity {
14
+ LOW = 'LOW',
15
+ MODERATE = 'MODERATE',
16
+ HIGH = 'HIGH',
17
+ CRITICAL = 'CRITICAL'
18
+ }
19
+
20
+ /**
21
+ * Returns the numeric ordinal of a severity level for comparison.
22
+ */
23
+ export function severityOrdinal(severity: Severity): number {
24
+ switch (severity) {
25
+ case Severity.LOW: return 0;
26
+ case Severity.MODERATE: return 1;
27
+ case Severity.HIGH: return 2;
28
+ case Severity.CRITICAL: return 3;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Parses a severity string into a Severity enum value.
34
+ */
35
+ export function parseSeverity(value: string): Severity {
36
+ const upper = value.toUpperCase();
37
+ if (upper in Severity) {
38
+ return Severity[upper as keyof typeof Severity];
39
+ }
40
+ return Severity.LOW;
41
+ }
42
+
43
+ /**
44
+ * Represents a known vulnerability from the advisory database.
45
+ *
46
+ * CSV format:
47
+ * CVE,publishedAt,summary,packageName,introducedVersion,fixedVersion,lastAffectedVersion,severity,cwes
48
+ *
49
+ * Example:
50
+ * CVE-2021-44228,2021-12-10T00:00:00Z,"Log4j RCE",log4j,2.0.0,2.17.0,,CRITICAL,CWE-400;CWE-502
51
+ */
52
+ export interface Vulnerability {
53
+ /** CVE identifier (e.g., "CVE-2021-44228") */
54
+ readonly cve: string;
55
+
56
+ /** When the vulnerability was published */
57
+ readonly publishedAt: string;
58
+
59
+ /** Brief description of the vulnerability */
60
+ readonly summary: string;
61
+
62
+ /** Name of the affected npm package */
63
+ readonly packageName: string;
64
+
65
+ /** First version where vulnerability was introduced (e.g., "2.0.0") */
66
+ readonly introducedVersion: string;
67
+
68
+ /** Version where vulnerability was fixed (e.g., "2.17.0"), empty if no fix available */
69
+ readonly fixedVersion: string;
70
+
71
+ /** Last version affected if no fixed version (alternative to fixedVersion) */
72
+ readonly lastAffectedVersion: string;
73
+
74
+ /** Severity level */
75
+ readonly severity: Severity;
76
+
77
+ /** CWE identifiers separated by semicolons (e.g., "CWE-79;CWE-89") */
78
+ readonly cwes: string;
79
+ }
80
+
81
+ /**
82
+ * Parses a CSV line, handling quoted fields correctly.
83
+ */
84
+ function parseCsvLine(line: string): string[] {
85
+ const fields: string[] = [];
86
+ let current = '';
87
+ let inQuotes = false;
88
+
89
+ for (let i = 0; i < line.length; i++) {
90
+ const char = line[i];
91
+
92
+ if (char === '"') {
93
+ if (inQuotes && line[i + 1] === '"') {
94
+ // Escaped quote
95
+ current += '"';
96
+ i++;
97
+ } else {
98
+ // Toggle quote mode
99
+ inQuotes = !inQuotes;
100
+ }
101
+ } else if (char === ',' && !inQuotes) {
102
+ fields.push(current);
103
+ current = '';
104
+ } else {
105
+ current += char;
106
+ }
107
+ }
108
+ fields.push(current);
109
+
110
+ return fields;
111
+ }
112
+
113
+ /**
114
+ * Parses a vulnerability from a CSV line.
115
+ *
116
+ * Expected format:
117
+ * CVE,publishedAt,summary,packageName,introducedVersion,fixedVersion,lastAffectedVersion,severity,cwes
118
+ */
119
+ function parseVulnerability(line: string): Vulnerability | undefined {
120
+ const fields = parseCsvLine(line);
121
+ if (fields.length < 9) {
122
+ return undefined;
123
+ }
124
+
125
+ const [cve, publishedAt, summary, packageName, introducedVersion, fixedVersion, lastAffectedVersion, severity, cwes] = fields;
126
+
127
+ // Skip if missing required fields
128
+ if (!cve || !packageName) {
129
+ return undefined;
130
+ }
131
+
132
+ return {
133
+ cve,
134
+ publishedAt,
135
+ summary,
136
+ packageName,
137
+ introducedVersion: introducedVersion || '0',
138
+ fixedVersion: fixedVersion || '',
139
+ lastAffectedVersion: lastAffectedVersion || '',
140
+ severity: parseSeverity(severity),
141
+ cwes: cwes || ''
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Vulnerability database indexed by package name for efficient lookup.
147
+ */
148
+ export class VulnerabilityDatabase {
149
+ private readonly byPackage: Map<string, Vulnerability[]>;
150
+
151
+ private constructor(vulnerabilities: Vulnerability[]) {
152
+ this.byPackage = new Map();
153
+ for (const v of vulnerabilities) {
154
+ const existing = this.byPackage.get(v.packageName);
155
+ if (existing) {
156
+ existing.push(v);
157
+ } else {
158
+ this.byPackage.set(v.packageName, [v]);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Gets all vulnerabilities for a package name.
165
+ */
166
+ getVulnerabilities(packageName: string): Vulnerability[] {
167
+ return this.byPackage.get(packageName) || [];
168
+ }
169
+
170
+ /**
171
+ * Checks if a package has any known vulnerabilities.
172
+ */
173
+ hasVulnerabilities(packageName: string): boolean {
174
+ return this.byPackage.has(packageName);
175
+ }
176
+
177
+ /**
178
+ * Gets all package names with known vulnerabilities.
179
+ */
180
+ getAffectedPackages(): string[] {
181
+ return Array.from(this.byPackage.keys());
182
+ }
183
+
184
+ /**
185
+ * Gets the total number of vulnerabilities in the database.
186
+ */
187
+ get size(): number {
188
+ let count = 0;
189
+ for (const vulns of this.byPackage.values()) {
190
+ count += vulns.length;
191
+ }
192
+ return count;
193
+ }
194
+
195
+ /**
196
+ * Loads the vulnerability database from the bundled CSV file.
197
+ */
198
+ static load(): VulnerabilityDatabase {
199
+ // Find the resources directory relative to this file
200
+ // In development: src/security/vulnerability.ts -> resources/advisories-npm.csv
201
+ // In production: dist/security/vulnerability.js -> resources/advisories-npm.csv
202
+ const possiblePaths = [
203
+ path.join(__dirname, '..', '..', 'resources', 'advisories-npm.csv'),
204
+ path.join(__dirname, '..', '..', '..', 'resources', 'advisories-npm.csv'),
205
+ ];
206
+
207
+ let csvPath: string | undefined;
208
+ for (const p of possiblePaths) {
209
+ if (fs.existsSync(p)) {
210
+ csvPath = p;
211
+ break;
212
+ }
213
+ }
214
+
215
+ if (!csvPath) {
216
+ // Return empty database if CSV not found - this allows the recipe to
217
+ // run without errors but won't find any vulnerabilities
218
+ return new VulnerabilityDatabase([]);
219
+ }
220
+
221
+ return VulnerabilityDatabase.loadFromFile(csvPath);
222
+ }
223
+
224
+ /**
225
+ * Loads the vulnerability database from a CSV file path.
226
+ */
227
+ static loadFromFile(filePath: string): VulnerabilityDatabase {
228
+ const content = fs.readFileSync(filePath, 'utf-8');
229
+ return VulnerabilityDatabase.loadFromString(content);
230
+ }
231
+
232
+ /**
233
+ * Loads the vulnerability database from a CSV string.
234
+ */
235
+ static loadFromString(content: string): VulnerabilityDatabase {
236
+ const vulnerabilities: Vulnerability[] = [];
237
+ const lines = content.split('\n');
238
+
239
+ for (const line of lines) {
240
+ const trimmed = line.trim();
241
+ if (!trimmed) continue;
242
+
243
+ const vuln = parseVulnerability(trimmed);
244
+ if (vuln) {
245
+ vulnerabilities.push(vuln);
246
+ }
247
+ }
248
+
249
+ return new VulnerabilityDatabase(vulnerabilities);
250
+ }
251
+
252
+ /**
253
+ * Creates an empty vulnerability database (for testing).
254
+ */
255
+ static empty(): VulnerabilityDatabase {
256
+ return new VulnerabilityDatabase([]);
257
+ }
258
+
259
+ /**
260
+ * Creates a vulnerability database from a list of vulnerabilities (for testing).
261
+ */
262
+ static fromList(vulnerabilities: Vulnerability[]): VulnerabilityDatabase {
263
+ return new VulnerabilityDatabase(vulnerabilities);
264
+ }
265
+ }