From 81f90fe867f79396c51a8e48c1ace15cd60f54d6 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Fri, 13 Sep 2024 03:31:47 -0700 Subject: [PATCH] Vulnerability check overhaul (#124) Overhaul DependencyVulnerabilityCheck. Enhancements and fixes include: - Can now upgrade dependency versions in freestanding gradle scripts - Can now upgrade dependency versions in gradle.properties - Fixes upgrading dependencies with version suffixes like "M1" - Validates accuracy of fix versions, looking up an appropriate version when Works around inaccurate fix versions in the database --- .../DependencyVulnerabilityCheck.java | 349 +++++++----------- .../UpgradeTransitiveDependencyVersion.java | 31 +- .../DependencyVulnerabilityCheckTest.java | 115 +++--- 3 files changed, 217 insertions(+), 278 deletions(-) diff --git a/src/main/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheck.java b/src/main/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheck.java index 87b3699..739d69f 100644 --- a/src/main/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheck.java +++ b/src/main/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheck.java @@ -18,12 +18,10 @@ import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import lombok.EqualsAndHashCode; -import lombok.Value; +import lombok.*; import lombok.experimental.NonFinal; import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.gradle.UpgradeTransitiveDependencyVersion; import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.groovy.GroovyIsoVisitor; @@ -36,11 +34,9 @@ import org.openrewrite.java.dependencies.table.VulnerabilityReport; import org.openrewrite.java.marker.JavaProject; import org.openrewrite.marker.CommitMessage; -import org.openrewrite.marker.SearchResult; -import org.openrewrite.maven.AddManagedDependency; -import org.openrewrite.maven.MavenIsoVisitor; -import org.openrewrite.maven.MavenVisitor; -import org.openrewrite.maven.UpgradeDependencyVersion; +import org.openrewrite.maven.*; +import org.openrewrite.maven.internal.MavenPomDownloader; +import org.openrewrite.maven.table.MavenMetadataFailures; import org.openrewrite.maven.tree.*; import org.openrewrite.semver.LatestPatch; import org.openrewrite.xml.tree.Xml; @@ -51,11 +47,10 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static java.util.Objects.requireNonNull; - @Value @EqualsAndHashCode(callSuper = false) public class DependencyVulnerabilityCheck extends ScanningRecipe { + transient MavenMetadataFailures metadataFailures = new MavenMetadataFailures(this); transient VersionParser versionParser = new VersionParser(); transient VulnerabilityReport report = new VulnerabilityReport(this); @@ -75,14 +70,6 @@ public class DependencyVulnerabilityCheck extends ScanningRecipe validate() { })); } - @Value + @Getter + @RequiredArgsConstructor public static class Accumulator { - Map> db; - Map projectToVulnerabilities; - Scope scope; - org.openrewrite.maven.UpgradeDependencyVersion.Accumulator mavenUdvAcc; - org.openrewrite.gradle.UpgradeDependencyVersion.DependencyVersionState gradleUdvAcc; + final Map> db; + final Scope scope; + final org.openrewrite.java.dependencies.UpgradeDependencyVersion.Accumulator dependencyAcc; + final AddManagedDependency.Scanned transitiveAcc; + + Map projectToVulnerabilities = new LinkedHashMap<>(); + + @Nullable + private Map> upgradeableVulnerabilities = null; + + public Map> upgradeableVulnerabilities() { + if (upgradeableVulnerabilities == null) { + upgradeableVulnerabilities = new LinkedHashMap<>(); + for (Vulnerabilities vuln : projectToVulnerabilities.values()) { + for (Map.Entry> resolvedGroupArtifactVersionSetEntry : vuln.getGavToVulnerabilities().entrySet()) { + ResolvedGroupArtifactVersion gav = resolvedGroupArtifactVersionSetEntry.getKey(); + Set vulnerabilities = resolvedGroupArtifactVersionSetEntry.getValue(); + upgradeableVulnerabilities.compute(gav, (k, upgradeableSoFar) -> { + Set newUpgradableVulnerabilities = vulnerabilities.stream() + .filter(it -> StringUtils.isNotEmpty(it.vulnerability.getFixedVersion())) + .filter(it -> new LatestPatch(null) + .isValid(gav.getVersion(), it.vulnerability.getFixedVersion())) + .collect(Collectors.toCollection(LinkedHashSet::new)); + if (newUpgradableVulnerabilities.isEmpty()) { + return upgradeableSoFar; + } + if (upgradeableSoFar == null) { + upgradeableSoFar = newUpgradableVulnerabilities; + } else { + upgradeableSoFar.addAll(newUpgradableVulnerabilities); + } + + return upgradeableSoFar; + }); + + } + } + } + return upgradeableVulnerabilities; + } } @Value public static class Vulnerabilities { - public static Vulnerabilities NONE = new Vulnerabilities(Collections.emptyMap()); - Map> gavToVulnerabilities; public Set computeIfAbsent(ResolvedGroupArtifactVersion gav, Function> mappingFunction) { @@ -148,9 +169,11 @@ public Accumulator getInitialValue(ExecutionContext ctx) { throw new RuntimeException(e); } - return new Accumulator(db, new LinkedHashMap<>(), parsedScope, - new org.openrewrite.maven.UpgradeDependencyVersion.Accumulator(), - new org.openrewrite.gradle.UpgradeDependencyVersion.DependencyVersionState()); + return new Accumulator(db, parsedScope, + new UpgradeDependencyVersion("", "", "", null, null, null) + .getInitialValue(ctx), + new UpgradeTransitiveDependencyVersion("", "", "", null, null, null, null, null, null, null, true) + .getInitialValue(ctx)); } @Override @@ -163,11 +186,11 @@ public TreeVisitor getScanner(Accumulator acc) { } scanMaven(acc.getDb(), acc.getProjectToVulnerabilities(), acc.getScope()).visitNonNull(tree, ctx); scanGradleGroovy(acc.getDb(), acc.getProjectToVulnerabilities(), acc.getScope()).visitNonNull(tree, ctx); - new UpgradeDependencyVersion("", "", "", null, null, null) - .getScanner(acc.getMavenUdvAcc()) + new org.openrewrite.java.dependencies.UpgradeDependencyVersion("", "", "", null, null, null) + .getScanner(acc.getDependencyAcc()) .visit(tree, ctx); - new org.openrewrite.gradle.UpgradeDependencyVersion("", "", null, null) - .getScanner(acc.getGradleUdvAcc()) + new org.openrewrite.java.dependencies.UpgradeTransitiveDependencyVersion("", "", "", null, null, null, null, null, null, null, true) + .getScanner(acc.getTransitiveAcc()) .visit(tree, ctx); return tree; } @@ -182,8 +205,7 @@ public Collection generate(Accumulator acc, ExecutionContext ctx) { for (MinimumDepthVulnerability vDepth : vulnerabilitiesByGav.getValue()) { Vulnerability v = vDepth.getVulnerability(); ResolvedGroupArtifactVersion gav = vulnerabilitiesByGav.getKey(); - boolean fixWithVersionUpdateOnly = (vDepth.getMinDepth() == 0 || Boolean.TRUE.equals(overrideTransitive)) - && new LatestPatch(null).isValid(gav.getVersion(), v.getFixedVersion()); + boolean fixWithVersionUpdateOnly = new LatestPatch(null).isValid(gav.getVersion(), v.getFixedVersion()); report.insertRow(ctx, new VulnerabilityReport.Row( projectName, v.getCve(), @@ -207,92 +229,85 @@ public Collection generate(Accumulator acc, ExecutionContext ctx) { public TreeVisitor getVisitor(Accumulator acc) { return new TreeVisitor() { @Override - public Tree visit(@Nullable Tree tree, ExecutionContext ctx) { + public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree == null) { return null; } Tree t = tree; - - if (t.getMarkers().findFirst(MavenResolutionResult.class).isPresent()) { - Map fixes = new LinkedHashMap<>(); - Vulnerabilities vulnerabilities = acc.getProjectToVulnerabilities().getOrDefault(projectName(t), Vulnerabilities.NONE); - if (!vulnerabilities.getGavToVulnerabilities().isEmpty()) { - t = enumerateFixesMaven(vulnerabilities.getGavToVulnerabilities(), acc.getScope(), fixes).visitNonNull(t, ctx); - for (Map.Entry gav : fixes.entrySet()) { - Tree t2 = new UpgradeDependencyVersion(gav.getKey().getGroupId(), gav.getKey().getArtifactId(), - gav.getValue().getVersion(), null, overrideTransitive, null) - .getVisitor(acc.getMavenUdvAcc()) - .visitNonNull(t, ctx); - if (t == t2 && gav.getValue().isDotReleaseAmbiguous()) { - t2 = new UpgradeDependencyVersion(gav.getKey().getGroupId(), gav.getKey().getArtifactId(), - gav.getValue().getVersion() + ".RELEASE", null, overrideTransitive, null) - .getVisitor(acc.getMavenUdvAcc()) - .visitNonNull(t, ctx); - } - if (t == t2) { - if (Boolean.TRUE.equals(overrideTransitive)) { - AddManagedDependency amd = new AddManagedDependency(gav.getKey().getGroupId(), gav.getKey().getArtifactId(), gav.getValue().getVersion(), null, null, null, null, null, null, null); - t2 = amd.getVisitor(amd.getInitialValue(ctx)) - .visitNonNull(t, ctx); - if (t == t2 && gav.getValue().isDotReleaseAmbiguous()) { - amd = new AddManagedDependency(gav.getKey().getGroupId(), gav.getKey().getArtifactId(), gav.getValue().getVersion() + ".RELEASE", null, null, null, null, null, null, null); - t2 = amd.getVisitor(amd.getInitialValue(ctx)) - .visitNonNull(t, ctx); - } - if (t != t2) { - t = CommitMessage.message(t2, DependencyVulnerabilityCheck.this, requireNonNull(gav.getValue().getBecause())); - } - } - } else { - t = CommitMessage.message(t2, DependencyVulnerabilityCheck.this, requireNonNull(gav.getValue().getBecause())); - } - } + Map> upgradeableVulnerabilities = + acc.upgradeableVulnerabilities(); + for (Map.Entry> gavToUpgradeableVulnerabilities : upgradeableVulnerabilities.entrySet()) { + ResolvedGroupArtifactVersion gav = gavToUpgradeableVulnerabilities.getKey(); + Set vulnerabilities = gavToUpgradeableVulnerabilities.getValue(); + String versionToRequest = versionToRequest(vulnerabilities, Collections.singletonList(MavenRepository.MAVEN_CENTRAL), ctx); + Tree t2 = new UpgradeDependencyVersion(gav.getGroupId(), gav.getArtifactId(), versionToRequest, null, overrideTransitive, null) + .getVisitor(acc.getDependencyAcc()) + .visitNonNull(t, ctx); + String because = null; + if (t2 == t) { + because = because(vulnerabilities); + t2 = new UpgradeTransitiveDependencyVersion(gav.getGroupId(), gav.getArtifactId(), versionToRequest, scope, null, null, null, because, null, null, true) + .getVisitor(acc.getTransitiveAcc()) + .visitNonNull(t2, ctx); } - } else if (t.getMarkers().findFirst(GradleProject.class).isPresent()) { - Map fixes = new LinkedHashMap<>(); - Vulnerabilities vulnerabilities = acc.getProjectToVulnerabilities().getOrDefault(projectName(t), Vulnerabilities.NONE); - if (!vulnerabilities.getGavToVulnerabilities().isEmpty()) { - t = enumerateFixesGradleGroovy(vulnerabilities.getGavToVulnerabilities(), acc.getScope(), fixes).visitNonNull(t, ctx); - for (Map.Entry gaToVb : fixes.entrySet()) { - VersionBecause vb = gaToVb.getValue(); - Tree t2 = new org.openrewrite.gradle.UpgradeDependencyVersion(gaToVb.getKey().getGroupId(), gaToVb.getKey().getArtifactId(), - vb.getVersion(), null) - .getVisitor(acc.getGradleUdvAcc()) - .visitNonNull(t, ctx); - if (t == t2 && vb.isDotReleaseAmbiguous()) { - t2 = new org.openrewrite.gradle.UpgradeDependencyVersion(gaToVb.getKey().getGroupId(), gaToVb.getKey().getArtifactId(), - vb.getVersion() + ".RELEASE", null) - .getVisitor(acc.getGradleUdvAcc()) - .visitNonNull(t, ctx); - } - if (t == t2) { - if (Boolean.TRUE.equals(overrideTransitive)) { - t2 = new UpgradeTransitiveDependencyVersion(gaToVb.getKey().getGroupId(), gaToVb.getKey().getArtifactId(), - vb.getVersion(), null, vb.getBecause(), null) - .getVisitor() - .visitNonNull(t, ctx); - if(t == t2 && vb.isDotReleaseAmbiguous()) { - t2 = new UpgradeTransitiveDependencyVersion(gaToVb.getKey().getGroupId(), gaToVb.getKey().getArtifactId(), - vb.getVersion() + ".RELEASE", null, vb.getBecause(), null) - .getVisitor() - .visitNonNull(t, ctx); - } - if (t != t2) { - t = CommitMessage.message(t2, DependencyVulnerabilityCheck.this, vb.getBecause()); - } - } - } else { - t = CommitMessage.message(t2, DependencyVulnerabilityCheck.this, vb.getBecause()); - } + t = t2; + + if (t != tree) { + if (because == null) { + because = because(vulnerabilities); } + CommitMessage.message(t2, DependencyVulnerabilityCheck.this, because); } } - return t; } }; } + /** + * Of the vulnerabilities with valid upgrade paths, take the highest fixed version. + * See if the highest fixed version can be resolved from the available repositories. + * Sometimes a fix version in the database will slightly inaccurate, such as missing a suffix (milestone, timestamp, etc.). + * If the fix version from the database cannot be validated to exist, leave discovery up to upgrade dependency + * recipes by falling back to "latest.patch". + */ + private String versionToRequest(Set vulnerabilities, List repositories, ExecutionContext ctx) { + Comparator vc = new StaticVersionComparator(); + Vulnerability highestFix = vulnerabilities.stream() + .max(Comparator.comparing( + it -> versionParser.transform(stripExtraneousVersionSuffix(it.getVulnerability().getFixedVersion())), + vc)) + .map(MinimumDepthVulnerability::getVulnerability) + .orElse(null); + if (highestFix != null) { + String[] groupArtifact = highestFix.getGroupArtifact().split(":"); + String groupId = groupArtifact[0]; + String artifactId = groupArtifact[1]; + + try { + MavenMetadata metadata = metadataFailures.insertRows(ctx, () -> new MavenPomDownloader(ctx).downloadMetadata( + new GroupArtifact(groupId, artifactId), null, repositories)); + List versions = metadata.getVersioning().getVersions(); + if (versions.contains(highestFix.getFixedVersion())) { + return highestFix.getFixedVersion(); + } + } catch (MavenDownloadingException e) { + return "latest.patch"; + } + } + return "latest.patch"; + } + + @Nullable + private static String because(Collection reasons) { + String because = reasons.stream() + .map(MinimumDepthVulnerability::getVulnerability) + .map(Vulnerability::getCve) + .filter(StringUtils::isNotEmpty) + .collect(Collectors.joining(", ")); + return StringUtils.isBlank(because) ? null : because; + } + private MavenVisitor scanMaven( Map> db, Map projectToVulnerabilities, @@ -361,16 +376,6 @@ public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionCon }; } - @Value - private static class VersionBecause { - String version; - - @Nullable - String because; - - boolean dotReleaseAmbiguous; - } - private void analyzeDependency( Map> db, Vulnerabilities vulnerabilities, @@ -389,7 +394,6 @@ private void analyzeDependency( // This inconsistency complicates dependency upgrade since we don't know which version number format to request // Therefore ignore the suffix during comparison but record it so that version upgrades can try both with and without the suffix // The edge case of ".RELEASE" being introduced into a version scheme between patch versions is possible but hopefully rare - boolean dotReleaseAmbiguous = resolvedDependency.getVersion().endsWith(".RELEASE") && !v.getFixedVersion().endsWith(".RELEASE"); boolean isLessThanFixed = StringUtils.isBlank(v.getFixedVersion()); if (!isLessThanFixed && vc.compare( @@ -415,116 +419,12 @@ private void analyzeDependency( } } - gavVs.add(new MinimumDepthVulnerability(resolvedDependency.getDepth(), v, dotReleaseAmbiguous)); + gavVs.add(new MinimumDepthVulnerability(resolvedDependency.getDepth(), v)); } } } } - private MavenVisitor enumerateFixesMaven( - Map> vulnerabilities, - Scope aScope, - Map fixes - ) { - return new MavenIsoVisitor() { - @Override - public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { - if (isDependencyTag()) { - ResolvedDependency resolved = findDependency(tag, aScope); - if (resolved != null) { - for (Map.Entry> vulnerabilitiesByGav : vulnerabilities.entrySet()) { - ResolvedGroupArtifactVersion gav = vulnerabilitiesByGav.getKey(); - ResolvedDependency match = resolved.findDependency(requireNonNull(gav.getGroupId()), gav.getArtifactId()); - if (match != null) { - boolean vulnerable = false; - - for (MinimumDepthVulnerability vDepth : vulnerabilitiesByGav.getValue()) { - Vulnerability v = vDepth.getVulnerability(); - vulnerable = true; - - GroupArtifact ga = new GroupArtifact(gav.getGroupId(), gav.getArtifactId()); - String fixVersion = (fixes.get(ga) == null) ? null : fixes.get(ga).getVersion(); - if (!StringUtils.isBlank(v.getFixedVersion()) && - new LatestPatch(null).isValid(gav.getVersion(), v.getFixedVersion())) { - if (fixVersion == null || new StaticVersionComparator().compare(versionParser.transform(v.getFixedVersion()), versionParser.transform(fixVersion)) > 0) { - fixes.put(ga, new VersionBecause(v.getFixedVersion(), v.getCve(), vDepth.dotReleaseAmbiguous)); - } - } - } - - if (vulnerable && Boolean.TRUE.equals(addMarkers)) { - return SearchResult.found(tag, "This dependency includes " + gav + " which has the following vulnerabilities:\n" + - vulnerabilitiesByGav.getValue().stream() - .map(vDepth -> { - Vulnerability v = vDepth.getVulnerability(); - return v.getCve() + " (" + v.getSeverity() + " severity" + - (StringUtils.isBlank(v.getFixedVersion()) ? "" : ", fixed in " + v.getFixedVersion()) + - ") - " + v.getSummary(); - }) - .collect(Collectors.joining("\n"))); - } - } - } - } - } - return super.visitTag(tag, ctx); - } - }; - } - - private GroovyVisitor enumerateFixesGradleGroovy( - Map> vulnerabilities, - Scope aScope, - Map fixes - ) { - return new GroovyIsoVisitor() { - @Override - public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { - G.CompilationUnit c = cu; - //noinspection OptionalGetWithoutIsPresent - GradleProject gp = c.getMarkers().findFirst(GradleProject.class).get(); - for (Map.Entry> vulnerabilitiesByGav : vulnerabilities.entrySet()) { - ResolvedGroupArtifactVersion gav = vulnerabilitiesByGav.getKey(); - if (Boolean.TRUE.equals(addMarkers)) { - c = SearchResult.found(c, "This project has the following vulnerabilities:\n" + - vulnerabilitiesByGav.getValue().stream() - .map(vDepth -> { - Vulnerability v = vDepth.getVulnerability(); - return "Dependency " + gav + " has " + v.getCve() + " (" + v.getSeverity() + " severity" + - (StringUtils.isBlank(v.getFixedVersion()) ? "" : ", fixed in " + v.getFixedVersion()) + - ") - " + v.getSummary(); - }) - .collect(Collectors.joining("\n"))); - } - for (GradleDependencyConfiguration configuration : gp.getConfigurations()) { - if (scopeExcludesConfiguration(configuration, aScope) || !configuration.isCanBeResolved()) { - continue; - } - List resolved = configuration.getResolved(); - for (ResolvedDependency resolvedDependency : resolved) { - if (Objects.equals(resolvedDependency.getGroupId(), gav.getGroupId()) - && Objects.equals(resolvedDependency.getArtifactId(), gav.getArtifactId())) { - - for (MinimumDepthVulnerability vDepth : vulnerabilitiesByGav.getValue()) { - Vulnerability v = vDepth.getVulnerability(); - - GroupArtifact ga = new GroupArtifact(gav.getGroupId(), gav.getArtifactId()); - String fixVersion = (fixes.get(ga) == null) ? null : fixes.get(ga).getVersion(); - if (!StringUtils.isBlank(v.getFixedVersion()) && - new LatestPatch(null).isValid(gav.getVersion(), v.getFixedVersion())) { - if (fixVersion == null || new StaticVersionComparator().compare(versionParser.transform(v.getFixedVersion()), versionParser.transform(fixVersion)) > 0) { - fixes.put(ga, new VersionBecause(v.getFixedVersion(), v.getCve(), vDepth.dotReleaseAmbiguous)); - } - } - } - } - } - } - } - return c; - } - }; - } @Value public static class MinimumDepthVulnerability { @@ -532,7 +432,6 @@ public static class MinimumDepthVulnerability { int minDepth; Vulnerability vulnerability; - boolean dotReleaseAmbiguous; } private static String stripExtraneousVersionSuffix(String version) { diff --git a/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java b/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java index e447ea2..f8a0ce7 100644 --- a/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java +++ b/src/main/java/org/openrewrite/java/dependencies/UpgradeTransitiveDependencyVersion.java @@ -19,7 +19,6 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.gradle.UpgradeDependencyVersion; import org.openrewrite.maven.AddManagedDependency; @Value @@ -85,6 +84,13 @@ public String getDescription() { @Nullable String versionPattern; + @Option(displayName = "Because", + description = "The reason for upgrading the transitive dependency. For example, we could be responding to a vulnerability.", + required = false, + example = "CVE-2021-1234") + @Nullable + String because; + @Option(displayName = "Releases only", description = "Whether to exclude snapshots from consideration when using a semver selector", required = false) @@ -107,28 +113,28 @@ public String getDescription() { @Override public AddManagedDependency.Scanned getInitialValue(ExecutionContext ctx) { - return getMavenUpgradeDependencyVersion().getInitialValue(ctx); + return getMavenUpgradeTransitive().getInitialValue(ctx); } @Override public TreeVisitor getScanner(AddManagedDependency.Scanned acc) { - return getMavenUpgradeDependencyVersion().getScanner(acc); + return getMavenUpgradeTransitive().getScanner(acc); } @Override public TreeVisitor getVisitor(AddManagedDependency.Scanned acc) { - TreeVisitor gradleUDV = new UpgradeDependencyVersion(groupId, artifactId, version, versionPattern).getVisitor(); - TreeVisitor mavenUTDV = getMavenUpgradeDependencyVersion().getVisitor(acc); + TreeVisitor gradleUDV = getGradleUpgradeTransitive().getVisitor(); + TreeVisitor mavenUTDV = getMavenUpgradeTransitive().getVisitor(acc); return new TreeVisitor() { @Override public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { - if(!(tree instanceof SourceFile)) { + if (!(tree instanceof SourceFile)) { return tree; } SourceFile t = (SourceFile) tree; - if(gradleUDV.isAcceptable(t, ctx)) { + if (gradleUDV.isAcceptable(t, ctx)) { t = (SourceFile) gradleUDV.visitNonNull(t, ctx); - } else if(mavenUTDV.isAcceptable(t, ctx)) { + } else if (mavenUTDV.isAcceptable(t, ctx)) { t = (SourceFile) mavenUTDV.visitNonNull(t, ctx); } return t; @@ -136,8 +142,11 @@ public TreeVisitor getVisitor(AddManagedDependency.Scanned }; } - private org.openrewrite.maven.UpgradeTransitiveDependencyVersion getMavenUpgradeDependencyVersion() { - return new org.openrewrite.maven.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, versionPattern, - scope, type, versionPattern, releasesOnly, onlyIfUsing, addToRootPom); + private org.openrewrite.gradle.UpgradeTransitiveDependencyVersion getGradleUpgradeTransitive() { + return new org.openrewrite.gradle.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, versionPattern, because, null); + } + + private org.openrewrite.maven.UpgradeTransitiveDependencyVersion getMavenUpgradeTransitive() { + return new org.openrewrite.maven.UpgradeTransitiveDependencyVersion(groupId, artifactId, version, scope, type, classifier, versionPattern, releasesOnly, onlyIfUsing, addToRootPom); } } diff --git a/src/test/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheckTest.java b/src/test/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheckTest.java index 3065cf3..117be46 100644 --- a/src/test/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheckTest.java +++ b/src/test/java/org/openrewrite/java/dependencies/DependencyVulnerabilityCheckTest.java @@ -27,45 +27,45 @@ import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; import static org.openrewrite.maven.Assertions.pomXml; +@SuppressWarnings("GroovyAssignabilityCheck") class DependencyVulnerabilityCheckTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new DependencyVulnerabilityCheck("runtime", true, true)); + spec.recipe(new DependencyVulnerabilityCheck("runtime", true)); } @DocumentExample @Test - void gradle() { + void gradleTransitive() { rewriteRun( - spec -> spec.beforeRecipe(withToolingApi()), + spec -> spec.beforeRecipe(withToolingApi()) + .recipe(new DependencyVulnerabilityCheck(null, true)), //language=groovy buildGradle( """ - plugins { - id 'java' - } - - repositories { - mavenCentral() - } + plugins { id 'java' } + repositories { mavenCentral() } dependencies { - implementation 'org.apache.logging.log4j:log4j:2.13.1' + implementation 'org.openrewrite:rewrite-java:7.0.0' } """, """ - /*~~(This project has the following vulnerabilities: - Dependency org.apache.logging.log4j:log4j:2.13.1 has CVE-2020-9488 (LOW severity, fixed in 2.13.2) - Improper validation of certificate with host mismatch in Apache Log4j SMTP appender)~~>*/plugins { - id 'java' - } - - repositories { - mavenCentral() - } + plugins { id 'java' } + repositories { mavenCentral() } dependencies { - implementation 'org.apache.logging.log4j:log4j:2.13.2' + constraints { + runtimeOnly('io.github.classgraph:classgraph:4.8.112') { + because 'CVE-2021-47621' + } + implementation('com.fasterxml.jackson.core:jackson-databind:2.12.7.1') { + because 'CVE-2020-36518, CVE-2021-46877, CVE-2022-42003, CVE-2022-42004' + } + } + + implementation 'org.openrewrite:rewrite-java:7.0.0' } """ ) @@ -73,10 +73,10 @@ void gradle() { } @Test - void gradleTransitive() { + void milestoneVersion() { rewriteRun( spec -> spec.beforeRecipe(withToolingApi()) - .recipe(new DependencyVulnerabilityCheck(null, true, false)), + .recipe(new DependencyVulnerabilityCheck(null, true)), //language=groovy buildGradle( """ @@ -84,7 +84,7 @@ void gradleTransitive() { repositories { mavenCentral() } dependencies { - implementation 'org.openrewrite:rewrite-java:7.0.0' + implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.0.0-M1' } """, """ @@ -92,17 +92,52 @@ void gradleTransitive() { repositories { mavenCentral() } dependencies { - constraints { - runtimeOnly('io.github.classgraph:classgraph:4.8.112') { - because 'CVE-2021-47621' - } - implementation('com.fasterxml.jackson.core:jackson-databind:2.12.7.1') { - because 'CVE-2022-42003' - } + implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.0.27' + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.beforeRecipe(withToolingApi()), + //language=groovy + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") } - - implementation 'org.openrewrite:rewrite-java:7.0.0' } + dependencies { + implementation("org.apache.logging.log4j:log4j:2.13.1") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.apache.logging.log4j:log4j:2.13.2") + } + """, + spec -> spec.path("dependencies.gradle") + ), + //language=groovy + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' """ ) ); @@ -111,7 +146,7 @@ void gradleTransitive() { @Test void mavenTransitive() { rewriteRun( - spec -> spec.recipe(new DependencyVulnerabilityCheck(null, true, false)), + spec -> spec.recipe(new DependencyVulnerabilityCheck(null, true)), //language=xml pomXml( """ @@ -191,16 +226,12 @@ void maven() { my-app 1 - + org.springframework.security spring-security-core - 4.2.16.RELEASE + 4.2.20.RELEASE - + org.apache.logging.log4j log4j 2.13.2 @@ -217,7 +248,7 @@ void maven() { void mavenSnakeyamlMajorMinor() { rewriteRun( spec -> spec - .recipe(new DependencyVulnerabilityCheck("compile", false, false)), + .recipe(new DependencyVulnerabilityCheck("compile", false)), //language=xml pomXml( """ @@ -262,7 +293,7 @@ void mavenSnakeyamlMajorMinor() { void mavenJacksonMajorMinorPatch() { rewriteRun( spec -> spec - .recipe(new DependencyVulnerabilityCheck("compile", false, false)), + .recipe(new DependencyVulnerabilityCheck("compile", false)), //language=xml pomXml( """