@openrewrite/recipes-nodejs 0.37.0-20260103-170432 → 0.37.0-20260106-082310
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/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/advisories-npm.csv +19 -4
- package/dist/security/dependency-vulnerability-check.d.ts +25 -2
- package/dist/security/dependency-vulnerability-check.d.ts.map +1 -1
- package/dist/security/dependency-vulnerability-check.js +338 -96
- package/dist/security/dependency-vulnerability-check.js.map +1 -1
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1 -0
- package/dist/security/index.js.map +1 -1
- package/dist/security/npm-utils.d.ts +21 -0
- package/dist/security/npm-utils.d.ts.map +1 -0
- package/dist/security/npm-utils.js +268 -0
- package/dist/security/npm-utils.js.map +1 -0
- package/dist/security/remove-redundant-overrides.d.ts +40 -0
- package/dist/security/remove-redundant-overrides.d.ts.map +1 -0
- package/dist/security/remove-redundant-overrides.js +379 -0
- package/dist/security/remove-redundant-overrides.js.map +1 -0
- package/package.json +7 -3
- package/src/index.ts +2 -1
- package/src/security/dependency-vulnerability-check.ts +622 -66
- package/src/security/index.ts +1 -0
- package/src/security/npm-utils.ts +414 -0
- package/src/security/remove-redundant-overrides.ts +515 -0
|
@@ -59,6 +59,7 @@ const javascript_1 = require("@openrewrite/rewrite/javascript");
|
|
|
59
59
|
const semver = __importStar(require("semver"));
|
|
60
60
|
const path = __importStar(require("path"));
|
|
61
61
|
const vulnerability_1 = require("./vulnerability");
|
|
62
|
+
const npm_utils_1 = require("./npm-utils");
|
|
62
63
|
const ALL_DEPENDENCY_SCOPES = [
|
|
63
64
|
'dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'
|
|
64
65
|
];
|
|
@@ -159,7 +160,7 @@ __decorate([
|
|
|
159
160
|
], VulnerabilityReportRow.prototype, "dependencyPath", void 0);
|
|
160
161
|
class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
161
162
|
constructor(options) {
|
|
162
|
-
var _a, _b, _c;
|
|
163
|
+
var _a, _b, _c, _d, _e;
|
|
163
164
|
super(options);
|
|
164
165
|
this.name = "org.openrewrite.node.dependency-vulnerability-check";
|
|
165
166
|
this.displayName = "Find and fix vulnerable npm dependencies";
|
|
@@ -169,9 +170,11 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
169
170
|
"If a minor or major upgrade is required to reach the fixed version, this can be controlled using the `maximumUpgradeDelta` option. " +
|
|
170
171
|
"Vulnerability information comes from the GitHub Security Advisory Database.";
|
|
171
172
|
this.vulnerabilityReport = new rewrite_1.DataTable("org.openrewrite.nodejs.table.VulnerabilityReport", "Vulnerability Report", "Lists all vulnerabilities found in project dependencies.", VulnerabilityReportRow);
|
|
172
|
-
(_a = this.
|
|
173
|
-
(_b = this.
|
|
174
|
-
(_c = this.
|
|
173
|
+
(_a = this.transitiveFixStrategy) !== null && _a !== void 0 ? _a : (this.transitiveFixStrategy = 'report');
|
|
174
|
+
(_b = this.maximumUpgradeDelta) !== null && _b !== void 0 ? _b : (this.maximumUpgradeDelta = 'patch');
|
|
175
|
+
(_c = this.minimumSeverity) !== null && _c !== void 0 ? _c : (this.minimumSeverity = vulnerability_1.Severity.LOW);
|
|
176
|
+
(_d = this.fixDeclaredVersions) !== null && _d !== void 0 ? _d : (this.fixDeclaredVersions = false);
|
|
177
|
+
(_e = this.addOverrideComments) !== null && _e !== void 0 ? _e : (this.addOverrideComments = true);
|
|
175
178
|
if (options === null || options === void 0 ? void 0 : options.minimumSeverity) {
|
|
176
179
|
this.minimumSeverity = (0, vulnerability_1.parseSeverity)(options.minimumSeverity);
|
|
177
180
|
}
|
|
@@ -179,10 +182,47 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
179
182
|
try {
|
|
180
183
|
this.cvePatternRegex = new RegExp(this.cvePattern);
|
|
181
184
|
}
|
|
182
|
-
catch (
|
|
185
|
+
catch (_f) {
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
188
|
}
|
|
189
|
+
shouldScanTransitives() {
|
|
190
|
+
return this.transitiveFixStrategy !== 'report';
|
|
191
|
+
}
|
|
192
|
+
shouldFixTransitives() {
|
|
193
|
+
return this.transitiveFixStrategy !== 'report';
|
|
194
|
+
}
|
|
195
|
+
shouldVerifyTransitiveFixes() {
|
|
196
|
+
return this.transitiveFixStrategy === 'override' ||
|
|
197
|
+
this.transitiveFixStrategy === 'prefer-direct-upgrade';
|
|
198
|
+
}
|
|
199
|
+
filterRemainingTransitiveFixes(fixes, lockFileContent, packageManager, db) {
|
|
200
|
+
const result = [];
|
|
201
|
+
for (const fix of fixes) {
|
|
202
|
+
if (!fix.isTransitive || fix.fixViaDirectUpgrade) {
|
|
203
|
+
result.push(fix);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const resolvedVersion = (0, npm_utils_1.extractVersionFromLockFile)(lockFileContent, fix.packageName, packageManager);
|
|
207
|
+
if (!resolvedVersion) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const isStillVulnerable = this.isVersionStillVulnerable(fix.packageName, resolvedVersion, fix.cves, db);
|
|
211
|
+
if (isStillVulnerable) {
|
|
212
|
+
result.push(fix);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
isVersionStillVulnerable(packageName, version, cves, db) {
|
|
218
|
+
const vulns = db.getVulnerabilities(packageName);
|
|
219
|
+
for (const vuln of vulns) {
|
|
220
|
+
if (cves.includes(vuln.cve) && this.isVersionAffected(version, vuln)) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
186
226
|
initialValue(_ctx) {
|
|
187
227
|
return Object.assign(Object.assign({}, (0, javascript_1.createDependencyRecipeAccumulator)()), { db: vulnerability_1.VulnerabilityDatabase.load(), vulnerableByProject: new Map(), fixesByProject: new Map(), originalLockFiles: new Map(), allPackageJsonContents: new Map(), workspaceRoots: new Map(), modifiedWorkspaceMemberContents: new Map(), workspaceDetectionComplete: false });
|
|
188
228
|
}
|
|
@@ -324,7 +364,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
324
364
|
});
|
|
325
365
|
}
|
|
326
366
|
}
|
|
327
|
-
if (this.
|
|
367
|
+
if (this.shouldScanTransitives()) {
|
|
328
368
|
const transitives = [
|
|
329
369
|
...(resolved.dependencies || []),
|
|
330
370
|
...(resolved.devDependencies || []),
|
|
@@ -352,6 +392,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
352
392
|
continue;
|
|
353
393
|
const vulns = db.getVulnerabilities(dep.name);
|
|
354
394
|
const affectedCves = [];
|
|
395
|
+
const affectedCveSummaries = new Map();
|
|
355
396
|
let highestFixVersion;
|
|
356
397
|
for (const vuln of vulns) {
|
|
357
398
|
if ((0, vulnerability_1.severityOrdinal)(vuln.severity) < (0, vulnerability_1.severityOrdinal)(this.minimumSeverity)) {
|
|
@@ -363,6 +404,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
363
404
|
if (this.isVersionAffected(declaredMinVersion, vuln) &&
|
|
364
405
|
!this.isVersionAffected(dep.resolved.version, vuln)) {
|
|
365
406
|
affectedCves.push(vuln.cve);
|
|
407
|
+
affectedCveSummaries.set(vuln.cve, vuln.summary);
|
|
366
408
|
const fixVersion = vuln.fixedVersion;
|
|
367
409
|
if (fixVersion && (!highestFixVersion || semver.gt(fixVersion, highestFixVersion))) {
|
|
368
410
|
highestFixVersion = fixVersion;
|
|
@@ -378,6 +420,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
378
420
|
scope,
|
|
379
421
|
isTransitive: false,
|
|
380
422
|
cves: affectedCves,
|
|
423
|
+
cveSummaries: affectedCveSummaries,
|
|
381
424
|
originalMajorVersion: majorVersion
|
|
382
425
|
});
|
|
383
426
|
}
|
|
@@ -454,76 +497,145 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
454
497
|
return false;
|
|
455
498
|
}
|
|
456
499
|
}
|
|
457
|
-
computeFixes(vulnerabilities, db) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
500
|
+
computeFixes(vulnerabilities, db, projectContext) {
|
|
501
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
502
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
503
|
+
if (this.isReportOnly()) {
|
|
504
|
+
return [];
|
|
505
|
+
}
|
|
506
|
+
const byPackageAndMajor = new Map();
|
|
507
|
+
for (const vuln of vulnerabilities) {
|
|
508
|
+
const parsed = semver.parse(vuln.resolved.version);
|
|
509
|
+
const major = (_a = parsed === null || parsed === void 0 ? void 0 : parsed.major) !== null && _a !== void 0 ? _a : 0;
|
|
510
|
+
const key = `${vuln.resolved.name}@${major}`;
|
|
511
|
+
const existing = byPackageAndMajor.get(key) || [];
|
|
512
|
+
existing.push(vuln);
|
|
513
|
+
byPackageAndMajor.set(key, existing);
|
|
514
|
+
}
|
|
515
|
+
const majorVersionsByPackage = new Map();
|
|
516
|
+
for (const vuln of vulnerabilities) {
|
|
517
|
+
const parsed = semver.parse(vuln.resolved.version);
|
|
518
|
+
const major = (_b = parsed === null || parsed === void 0 ? void 0 : parsed.major) !== null && _b !== void 0 ? _b : 0;
|
|
519
|
+
const existing = majorVersionsByPackage.get(vuln.resolved.name) || new Set();
|
|
520
|
+
existing.add(major);
|
|
521
|
+
majorVersionsByPackage.set(vuln.resolved.name, existing);
|
|
522
|
+
}
|
|
523
|
+
const fixes = [];
|
|
524
|
+
for (const [key, vulns] of byPackageAndMajor) {
|
|
525
|
+
const packageName = vulns[0].resolved.name;
|
|
526
|
+
const originalMajor = (_d = (_c = semver.parse(vulns[0].resolved.version)) === null || _c === void 0 ? void 0 : _c.major) !== null && _d !== void 0 ? _d : 0;
|
|
527
|
+
const hasMultipleMajorVersions = ((_f = (_e = majorVersionsByPackage.get(packageName)) === null || _e === void 0 ? void 0 : _e.size) !== null && _f !== void 0 ? _f : 0) > 1;
|
|
528
|
+
let highestFixVersion;
|
|
529
|
+
const cves = [];
|
|
530
|
+
const cveSummaries = new Map();
|
|
531
|
+
let isTransitive = true;
|
|
532
|
+
let scope;
|
|
533
|
+
let originalVersion;
|
|
534
|
+
let directDepInfo;
|
|
535
|
+
for (const vuln of vulns) {
|
|
536
|
+
if (!originalVersion) {
|
|
537
|
+
originalVersion = vuln.resolved.version;
|
|
538
|
+
}
|
|
539
|
+
if (!vuln.isDirect && vuln.path.length > 0 && !directDepInfo) {
|
|
540
|
+
const directDep = vuln.path[0];
|
|
541
|
+
directDepInfo = {
|
|
542
|
+
name: directDep.name,
|
|
543
|
+
version: directDep.version,
|
|
544
|
+
scope: vuln.scope || 'dependencies'
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (!this.isUpgradeableWithinDelta(vuln.resolved.version, vuln.vulnerability)) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const fixVersion = this.getUpgradeVersion(vuln.vulnerability);
|
|
551
|
+
if (fixVersion) {
|
|
552
|
+
const fixMajor = (_g = semver.parse(fixVersion)) === null || _g === void 0 ? void 0 : _g.major;
|
|
553
|
+
const shouldConsiderFix = hasMultipleMajorVersions
|
|
554
|
+
? fixMajor === originalMajor
|
|
555
|
+
: true;
|
|
556
|
+
if (shouldConsiderFix) {
|
|
557
|
+
if (!highestFixVersion || semver.gt(fixVersion, highestFixVersion)) {
|
|
558
|
+
highestFixVersion = fixVersion;
|
|
559
|
+
}
|
|
505
560
|
}
|
|
506
561
|
}
|
|
562
|
+
cves.push(vuln.vulnerability.cve);
|
|
563
|
+
cveSummaries.set(vuln.vulnerability.cve, vuln.vulnerability.summary);
|
|
564
|
+
if (vuln.isDirect) {
|
|
565
|
+
isTransitive = false;
|
|
566
|
+
scope = vuln.scope;
|
|
567
|
+
}
|
|
507
568
|
}
|
|
508
|
-
cves.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
569
|
+
if (highestFixVersion && cves.length > 0 && originalVersion) {
|
|
570
|
+
const safeVersion = this.findHighestSafeVersion(packageName, originalVersion, highestFixVersion, db);
|
|
571
|
+
const fix = {
|
|
572
|
+
packageName,
|
|
573
|
+
newVersion: safeVersion || highestFixVersion,
|
|
574
|
+
isTransitive,
|
|
575
|
+
cves,
|
|
576
|
+
cveSummaries,
|
|
577
|
+
scope,
|
|
578
|
+
originalMajorVersion: originalMajor,
|
|
579
|
+
directDepInfo: isTransitive && directDepInfo ? {
|
|
580
|
+
name: directDepInfo.name,
|
|
581
|
+
version: directDepInfo.version,
|
|
582
|
+
scope: directDepInfo.scope
|
|
583
|
+
} : undefined
|
|
584
|
+
};
|
|
585
|
+
fixes.push(fix);
|
|
512
586
|
}
|
|
513
587
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
588
|
+
return fixes;
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
tryFindDirectDepUpgrade(transitivePackageName, requiredFixVersion, directDepInfo, projectContext, db) {
|
|
592
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
593
|
+
const isVulnerable = (version) => {
|
|
594
|
+
try {
|
|
595
|
+
return semver.lt(version, requiredFixVersion);
|
|
596
|
+
}
|
|
597
|
+
catch (_a) {
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
const isWithinDelta = (from, to) => {
|
|
602
|
+
return this.isVersionWithinDelta(from, to);
|
|
603
|
+
};
|
|
604
|
+
try {
|
|
605
|
+
const upgradeVersion = yield (0, npm_utils_1.findDirectUpgradeThatFixesTransitive)(projectContext.packageManager, directDepInfo.name, directDepInfo.version, transitivePackageName, isVulnerable, isWithinDelta, projectContext.originalPackageJson, directDepInfo.scope, projectContext.configFiles);
|
|
606
|
+
return upgradeVersion;
|
|
524
607
|
}
|
|
525
|
-
|
|
526
|
-
|
|
608
|
+
catch (_a) {
|
|
609
|
+
return undefined;
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
tryDirectUpgradesForTransitives(fixes, updateInfo, db) {
|
|
614
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
615
|
+
const result = [];
|
|
616
|
+
for (const fix of fixes) {
|
|
617
|
+
if (!fix.directDepInfo) {
|
|
618
|
+
result.push(fix);
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const directUpgradeVersion = yield this.tryFindDirectDepUpgrade(fix.packageName, fix.newVersion, fix.directDepInfo, {
|
|
622
|
+
packageManager: updateInfo.packageManager,
|
|
623
|
+
originalPackageJson: updateInfo.originalPackageJson,
|
|
624
|
+
configFiles: updateInfo.configFiles
|
|
625
|
+
}, db);
|
|
626
|
+
if (directUpgradeVersion) {
|
|
627
|
+
result.push(Object.assign(Object.assign({}, fix), { fixViaDirectUpgrade: {
|
|
628
|
+
directDepName: fix.directDepInfo.name,
|
|
629
|
+
directDepVersion: directUpgradeVersion,
|
|
630
|
+
directDepScope: fix.directDepInfo.scope
|
|
631
|
+
} }));
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
result.push(fix);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return result;
|
|
638
|
+
});
|
|
527
639
|
}
|
|
528
640
|
scanner(acc) {
|
|
529
641
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -597,7 +709,12 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
597
709
|
let fixes = [];
|
|
598
710
|
if (vulnerabilities.length > 0) {
|
|
599
711
|
acc.vulnerableByProject.set(doc.sourcePath, vulnerabilities);
|
|
600
|
-
|
|
712
|
+
const originalPackageJson = storedContent || (yield rewrite_1.TreePrinters.print(doc));
|
|
713
|
+
fixes = yield recipe.computeFixes(vulnerabilities, acc.db, {
|
|
714
|
+
packageManager: pm,
|
|
715
|
+
originalPackageJson,
|
|
716
|
+
configFiles: Object.keys(configFiles).length > 0 ? configFiles : undefined
|
|
717
|
+
});
|
|
601
718
|
}
|
|
602
719
|
if (recipe.fixDeclaredVersions) {
|
|
603
720
|
const preventiveFixes = recipe.findPreventiveFixes(marker, scopesToCheck, acc.db);
|
|
@@ -894,6 +1011,37 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
894
1011
|
}
|
|
895
1012
|
runPackageManagerInstall(acc, updateInfo, fixes) {
|
|
896
1013
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1014
|
+
const directFixes = fixes.filter(f => !f.isTransitive || f.fixViaDirectUpgrade);
|
|
1015
|
+
const transitiveFixes = fixes.filter(f => f.isTransitive && !f.fixViaDirectUpgrade);
|
|
1016
|
+
if (this.shouldVerifyTransitiveFixes() && transitiveFixes.length > 0) {
|
|
1017
|
+
const phase1PackageJson = this.createModifiedPackageJson(updateInfo.originalPackageJson, directFixes, updateInfo.packageManager);
|
|
1018
|
+
const phase1Result = yield (0, javascript_1.runInstallInTempDir)(updateInfo.packageManager, phase1PackageJson, {
|
|
1019
|
+
configFiles: updateInfo.configFiles
|
|
1020
|
+
});
|
|
1021
|
+
if (!phase1Result.success || !phase1Result.lockFileContent) {
|
|
1022
|
+
const modifiedPackageJson = this.createModifiedPackageJson(updateInfo.originalPackageJson, fixes, updateInfo.packageManager);
|
|
1023
|
+
const result = yield (0, javascript_1.runInstallInTempDir)(updateInfo.packageManager, modifiedPackageJson, {
|
|
1024
|
+
configFiles: updateInfo.configFiles
|
|
1025
|
+
});
|
|
1026
|
+
(0, javascript_1.storeInstallResult)(result, acc, updateInfo, modifiedPackageJson);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
let remainingTransitiveFixes = this.filterRemainingTransitiveFixes(transitiveFixes, phase1Result.lockFileContent, updateInfo.packageManager, acc.db);
|
|
1030
|
+
if (remainingTransitiveFixes.length === 0) {
|
|
1031
|
+
(0, javascript_1.storeInstallResult)(phase1Result, acc, updateInfo, phase1PackageJson);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
if (this.transitiveFixStrategy === 'prefer-direct-upgrade') {
|
|
1035
|
+
remainingTransitiveFixes = yield this.tryDirectUpgradesForTransitives(remainingTransitiveFixes, updateInfo, acc.db);
|
|
1036
|
+
}
|
|
1037
|
+
const finalFixes = [...directFixes, ...remainingTransitiveFixes];
|
|
1038
|
+
const finalPackageJson = this.createModifiedPackageJson(updateInfo.originalPackageJson, finalFixes, updateInfo.packageManager);
|
|
1039
|
+
const finalResult = yield (0, javascript_1.runInstallInTempDir)(updateInfo.packageManager, finalPackageJson, {
|
|
1040
|
+
configFiles: updateInfo.configFiles
|
|
1041
|
+
});
|
|
1042
|
+
(0, javascript_1.storeInstallResult)(finalResult, acc, updateInfo, finalPackageJson);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
897
1045
|
const modifiedPackageJson = this.createModifiedPackageJson(updateInfo.originalPackageJson, fixes, updateInfo.packageManager);
|
|
898
1046
|
const result = yield (0, javascript_1.runInstallInTempDir)(updateInfo.packageManager, modifiedPackageJson, {
|
|
899
1047
|
configFiles: updateInfo.configFiles
|
|
@@ -905,23 +1053,24 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
905
1053
|
return __awaiter(this, void 0, void 0, function* () {
|
|
906
1054
|
const memberPaths = acc.workspaceRoots.get(rootPath) || [];
|
|
907
1055
|
const pm = rootUpdateInfo.packageManager;
|
|
1056
|
+
const allDirectFixes = [];
|
|
908
1057
|
const allTransitiveFixes = [];
|
|
909
1058
|
const rootFixes = acc.fixesByProject.get(rootPath) || [];
|
|
910
|
-
const rootDirectFixes = rootFixes.filter(f => !f.isTransitive);
|
|
911
|
-
const rootTransitiveFixes = rootFixes.filter(f => f.isTransitive);
|
|
1059
|
+
const rootDirectFixes = rootFixes.filter(f => !f.isTransitive || f.fixViaDirectUpgrade);
|
|
1060
|
+
const rootTransitiveFixes = rootFixes.filter(f => f.isTransitive && !f.fixViaDirectUpgrade);
|
|
1061
|
+
allDirectFixes.push(...rootDirectFixes);
|
|
912
1062
|
allTransitiveFixes.push(...rootTransitiveFixes);
|
|
913
1063
|
const memberDirectFixes = new Map();
|
|
914
1064
|
for (const memberPath of memberPaths) {
|
|
915
1065
|
const memberFixes = acc.fixesByProject.get(memberPath) || [];
|
|
916
|
-
const directFixes = memberFixes.filter(f => !f.isTransitive);
|
|
917
|
-
const transitiveFixes = memberFixes.filter(f => f.isTransitive);
|
|
1066
|
+
const directFixes = memberFixes.filter(f => !f.isTransitive || f.fixViaDirectUpgrade);
|
|
1067
|
+
const transitiveFixes = memberFixes.filter(f => f.isTransitive && !f.fixViaDirectUpgrade);
|
|
918
1068
|
if (directFixes.length > 0) {
|
|
919
1069
|
memberDirectFixes.set(memberPath, directFixes);
|
|
1070
|
+
allDirectFixes.push(...directFixes);
|
|
920
1071
|
}
|
|
921
1072
|
allTransitiveFixes.push(...transitiveFixes);
|
|
922
1073
|
}
|
|
923
|
-
const rootOriginalContent = acc.allPackageJsonContents.get(rootPath) || rootUpdateInfo.originalPackageJson;
|
|
924
|
-
const modifiedRootPackageJson = this.createModifiedPackageJson(rootOriginalContent, [...rootDirectFixes, ...allTransitiveFixes], pm);
|
|
925
1074
|
const workspacePackages = {};
|
|
926
1075
|
for (const memberPath of memberPaths) {
|
|
927
1076
|
const originalContent = acc.allPackageJsonContents.get(memberPath);
|
|
@@ -939,6 +1088,39 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
939
1088
|
}
|
|
940
1089
|
workspacePackages[memberPath] = modifiedContent;
|
|
941
1090
|
}
|
|
1091
|
+
const rootOriginalContent = acc.allPackageJsonContents.get(rootPath) || rootUpdateInfo.originalPackageJson;
|
|
1092
|
+
if (this.shouldVerifyTransitiveFixes() && allTransitiveFixes.length > 0) {
|
|
1093
|
+
const phase1RootPackageJson = this.createModifiedPackageJson(rootOriginalContent, rootDirectFixes, pm);
|
|
1094
|
+
const phase1Result = yield (0, javascript_1.runWorkspaceInstallInTempDir)(pm, phase1RootPackageJson, {
|
|
1095
|
+
configFiles: rootUpdateInfo.configFiles,
|
|
1096
|
+
workspacePackages
|
|
1097
|
+
});
|
|
1098
|
+
if (!phase1Result.success || !phase1Result.lockFileContent) {
|
|
1099
|
+
const modifiedRootPackageJson = this.createModifiedPackageJson(rootOriginalContent, [...rootDirectFixes, ...allTransitiveFixes], pm);
|
|
1100
|
+
const result = yield (0, javascript_1.runWorkspaceInstallInTempDir)(pm, modifiedRootPackageJson, {
|
|
1101
|
+
configFiles: rootUpdateInfo.configFiles,
|
|
1102
|
+
workspacePackages
|
|
1103
|
+
});
|
|
1104
|
+
(0, javascript_1.storeInstallResult)(result, acc, rootUpdateInfo, modifiedRootPackageJson);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
let remainingTransitiveFixes = this.filterRemainingTransitiveFixes(allTransitiveFixes, phase1Result.lockFileContent, pm, acc.db);
|
|
1108
|
+
if (remainingTransitiveFixes.length === 0) {
|
|
1109
|
+
(0, javascript_1.storeInstallResult)(phase1Result, acc, rootUpdateInfo, phase1RootPackageJson);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (this.transitiveFixStrategy === 'prefer-direct-upgrade') {
|
|
1113
|
+
remainingTransitiveFixes = yield this.tryDirectUpgradesForTransitives(remainingTransitiveFixes, rootUpdateInfo, acc.db);
|
|
1114
|
+
}
|
|
1115
|
+
const finalRootPackageJson = this.createModifiedPackageJson(rootOriginalContent, [...rootDirectFixes, ...remainingTransitiveFixes], pm);
|
|
1116
|
+
const finalResult = yield (0, javascript_1.runWorkspaceInstallInTempDir)(pm, finalRootPackageJson, {
|
|
1117
|
+
configFiles: rootUpdateInfo.configFiles,
|
|
1118
|
+
workspacePackages
|
|
1119
|
+
});
|
|
1120
|
+
(0, javascript_1.storeInstallResult)(finalResult, acc, rootUpdateInfo, finalRootPackageJson);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const modifiedRootPackageJson = this.createModifiedPackageJson(rootOriginalContent, [...rootDirectFixes, ...allTransitiveFixes], pm);
|
|
942
1124
|
const result = yield (0, javascript_1.runWorkspaceInstallInTempDir)(pm, modifiedRootPackageJson, {
|
|
943
1125
|
configFiles: rootUpdateInfo.configFiles,
|
|
944
1126
|
workspacePackages
|
|
@@ -959,7 +1141,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
959
1141
|
return JSON.stringify(packageJson, null, 2);
|
|
960
1142
|
}
|
|
961
1143
|
createModifiedPackageJson(originalContent, fixes, packageManager) {
|
|
962
|
-
var _a
|
|
1144
|
+
var _a;
|
|
963
1145
|
let packageJson = JSON.parse(originalContent);
|
|
964
1146
|
const fixesByPackage = new Map();
|
|
965
1147
|
for (const fix of fixes) {
|
|
@@ -967,6 +1149,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
967
1149
|
existing.push(fix);
|
|
968
1150
|
fixesByPackage.set(fix.packageName, existing);
|
|
969
1151
|
}
|
|
1152
|
+
const overrideComments = new Map();
|
|
970
1153
|
for (const fix of fixes) {
|
|
971
1154
|
if (!fix.isTransitive && fix.scope) {
|
|
972
1155
|
if (packageJson[fix.scope] && packageJson[fix.scope][fix.packageName]) {
|
|
@@ -975,6 +1158,14 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
975
1158
|
}
|
|
976
1159
|
}
|
|
977
1160
|
else if (fix.isTransitive) {
|
|
1161
|
+
if (fix.fixViaDirectUpgrade) {
|
|
1162
|
+
const { directDepName, directDepVersion, directDepScope } = fix.fixViaDirectUpgrade;
|
|
1163
|
+
if ((_a = packageJson[directDepScope]) === null || _a === void 0 ? void 0 : _a[directDepName]) {
|
|
1164
|
+
const originalVersion = packageJson[directDepScope][directDepName];
|
|
1165
|
+
packageJson[directDepScope][directDepName] = applyVersionPrefix(originalVersion, directDepVersion);
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
978
1169
|
const directDepScope = findDirectDependencyScope(packageJson, fix.packageName);
|
|
979
1170
|
if (directDepScope) {
|
|
980
1171
|
const directVersion = packageJson[directDepScope][fix.packageName];
|
|
@@ -985,6 +1176,7 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
985
1176
|
if (!isYarn) {
|
|
986
1177
|
const versionSpecificKey = `${fix.packageName}@^${fix.originalMajorVersion}`;
|
|
987
1178
|
packageJson = (0, javascript_1.applyOverrideToPackageJson)(packageJson, packageManager, versionSpecificKey, fix.newVersion);
|
|
1179
|
+
overrideComments.set(versionSpecificKey, this.generateOverrideComment(fix));
|
|
988
1180
|
}
|
|
989
1181
|
else {
|
|
990
1182
|
}
|
|
@@ -1007,26 +1199,63 @@ class DependencyVulnerabilityCheck extends rewrite_1.ScanningRecipe {
|
|
|
1007
1199
|
if (useVersionSpecificOverride) {
|
|
1008
1200
|
const versionSpecificKey = `${fix.packageName}@^${fix.originalMajorVersion}`;
|
|
1009
1201
|
packageJson = (0, javascript_1.applyOverrideToPackageJson)(packageJson, packageManager, versionSpecificKey, fix.newVersion);
|
|
1202
|
+
overrideComments.set(versionSpecificKey, this.generateOverrideComment(fix));
|
|
1010
1203
|
}
|
|
1011
1204
|
else {
|
|
1012
1205
|
packageJson = (0, javascript_1.applyOverrideToPackageJson)(packageJson, packageManager, fix.packageName, fix.newVersion);
|
|
1013
|
-
|
|
1014
|
-
if (packageManager === "Pnpm") {
|
|
1015
|
-
if (!packageJson.devDependencies) {
|
|
1016
|
-
packageJson.devDependencies = {};
|
|
1017
|
-
}
|
|
1018
|
-
if (!((_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a[fix.packageName]) &&
|
|
1019
|
-
!packageJson.devDependencies[fix.packageName] &&
|
|
1020
|
-
!((_b = packageJson.peerDependencies) === null || _b === void 0 ? void 0 : _b[fix.packageName]) &&
|
|
1021
|
-
!((_c = packageJson.optionalDependencies) === null || _c === void 0 ? void 0 : _c[fix.packageName])) {
|
|
1022
|
-
const prefix = this.getVersionPrefixForDelta();
|
|
1023
|
-
packageJson.devDependencies[fix.packageName] = prefix + fix.newVersion;
|
|
1024
|
-
}
|
|
1206
|
+
overrideComments.set(fix.packageName, this.generateOverrideComment(fix));
|
|
1025
1207
|
}
|
|
1026
1208
|
}
|
|
1027
1209
|
}
|
|
1210
|
+
if (this.addOverrideComments && overrideComments.size > 0) {
|
|
1211
|
+
packageJson = this.addOverrideCommentsToPackageJson(packageJson, packageManager, overrideComments);
|
|
1212
|
+
}
|
|
1028
1213
|
return JSON.stringify(packageJson, null, 2);
|
|
1029
1214
|
}
|
|
1215
|
+
generateOverrideComment(fix) {
|
|
1216
|
+
if (!fix.cveSummaries || fix.cveSummaries.size === 0) {
|
|
1217
|
+
return fix.cves.join(', ');
|
|
1218
|
+
}
|
|
1219
|
+
const parts = [];
|
|
1220
|
+
for (const cve of fix.cves) {
|
|
1221
|
+
const summary = fix.cveSummaries.get(cve);
|
|
1222
|
+
if (summary) {
|
|
1223
|
+
const truncatedSummary = summary.length > 80
|
|
1224
|
+
? summary.substring(0, 77) + '...'
|
|
1225
|
+
: summary;
|
|
1226
|
+
parts.push(`${cve}: ${truncatedSummary}`);
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
parts.push(cve);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return parts.join('; ');
|
|
1233
|
+
}
|
|
1234
|
+
addOverrideCommentsToPackageJson(packageJson, packageManager, comments) {
|
|
1235
|
+
let commentFieldName;
|
|
1236
|
+
switch (packageManager) {
|
|
1237
|
+
case "Npm":
|
|
1238
|
+
case "Bun":
|
|
1239
|
+
commentFieldName = '//overrides';
|
|
1240
|
+
break;
|
|
1241
|
+
case "Pnpm":
|
|
1242
|
+
commentFieldName = '//pnpm.overrides';
|
|
1243
|
+
break;
|
|
1244
|
+
case "YarnClassic":
|
|
1245
|
+
case "YarnBerry":
|
|
1246
|
+
commentFieldName = '//resolutions';
|
|
1247
|
+
break;
|
|
1248
|
+
default:
|
|
1249
|
+
return packageJson;
|
|
1250
|
+
}
|
|
1251
|
+
const existingComments = packageJson[commentFieldName] || {};
|
|
1252
|
+
const mergedComments = Object.assign({}, existingComments);
|
|
1253
|
+
for (const [key, comment] of comments) {
|
|
1254
|
+
mergedComments[key] = comment;
|
|
1255
|
+
}
|
|
1256
|
+
packageJson[commentFieldName] = mergedComments;
|
|
1257
|
+
return packageJson;
|
|
1258
|
+
}
|
|
1030
1259
|
}
|
|
1031
1260
|
exports.DependencyVulnerabilityCheck = DependencyVulnerabilityCheck;
|
|
1032
1261
|
__decorate([
|
|
@@ -1041,14 +1270,17 @@ __decorate([
|
|
|
1041
1270
|
], DependencyVulnerabilityCheck.prototype, "scope", void 0);
|
|
1042
1271
|
__decorate([
|
|
1043
1272
|
(0, rewrite_1.Option)({
|
|
1044
|
-
displayName: "
|
|
1045
|
-
description: "
|
|
1046
|
-
"
|
|
1047
|
-
"
|
|
1273
|
+
displayName: "Transitive fix strategy",
|
|
1274
|
+
description: "Strategy for handling transitive dependency vulnerabilities. " +
|
|
1275
|
+
"'report' only reports them without fixing. " +
|
|
1276
|
+
"'override' adds overrides only for transitives not fixed by direct upgrades (runs extra npm install to verify). " +
|
|
1277
|
+
"'prefer-direct-upgrade' tries to find higher direct dependency versions that fix transitives, falls back to overrides (queries npm registry). " +
|
|
1278
|
+
"Default is 'report'.",
|
|
1048
1279
|
required: false,
|
|
1049
|
-
example: "
|
|
1280
|
+
example: "override",
|
|
1281
|
+
valid: ["report", "override", "prefer-direct-upgrade"]
|
|
1050
1282
|
})
|
|
1051
|
-
], DependencyVulnerabilityCheck.prototype, "
|
|
1283
|
+
], DependencyVulnerabilityCheck.prototype, "transitiveFixStrategy", void 0);
|
|
1052
1284
|
__decorate([
|
|
1053
1285
|
(0, rewrite_1.Option)({
|
|
1054
1286
|
displayName: "Maximum upgrade delta",
|
|
@@ -1096,6 +1328,16 @@ __decorate([
|
|
|
1096
1328
|
example: "true"
|
|
1097
1329
|
})
|
|
1098
1330
|
], DependencyVulnerabilityCheck.prototype, "fixDeclaredVersions", void 0);
|
|
1331
|
+
__decorate([
|
|
1332
|
+
(0, rewrite_1.Option)({
|
|
1333
|
+
displayName: "Add override comments",
|
|
1334
|
+
description: "When enabled, adds a comment field (e.g., '//overrides') alongside overrides to document which CVEs " +
|
|
1335
|
+
"each override is fixing. This helps with auditing and knowing when overrides can be removed. " +
|
|
1336
|
+
"Default is true.",
|
|
1337
|
+
required: false,
|
|
1338
|
+
example: "true"
|
|
1339
|
+
})
|
|
1340
|
+
], DependencyVulnerabilityCheck.prototype, "addOverrideComments", void 0);
|
|
1099
1341
|
function extractVersionPrefix(versionString) {
|
|
1100
1342
|
const match = versionString.match(/^([~^]|>=?|<=?|=)?(.*)$/);
|
|
1101
1343
|
if (match) {
|