From d393231f269f9493012ea5e9d661090bf2ad0592 Mon Sep 17 00:00:00 2001 From: maxjeffos <44034094+maxjeffos@users.noreply.github.com> Date: Tue, 2 Nov 2021 16:04:38 -0400 Subject: [PATCH 1/2] fix: handle ignore existing annotations with `critical` license severities --- .../io/snyk/plugins/nexus/util/Formatter.java | 37 +++++++++++++++---- .../plugins/nexus/util/FormatterTest.java | 25 +++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java b/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java index b0cea19..20bc385 100644 --- a/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java +++ b/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java @@ -7,10 +7,16 @@ import io.snyk.plugins.nexus.model.ScanResult; import io.snyk.sdk.model.Issue; import io.snyk.sdk.model.Severity; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static io.snyk.sdk.util.Predicates.distinctByKey; public final class Formatter { + + private static final Pattern LICENSE_ISSUES_WITH_CRITICAL_REGEX = Pattern.compile("(\\d+) critical, (\\d+) high, (\\d+) medium, (\\d+) low"); + private static final Pattern LICENSE_ISSUES_WITHOUT_CRITICAL_REGEX = Pattern.compile("(\\d+) high, (\\d+) medium, (\\d+) low"); + private Formatter() { } @@ -66,15 +72,32 @@ public static void enrichScanResultWithLicenseIssues(@Nonnull ScanResult scanRes return; } - String[] parts = formattedIssues.split(", "); + Matcher licenseIssuesWithCriticalMatch = LICENSE_ISSUES_WITH_CRITICAL_REGEX.matcher(formattedIssues); + if (licenseIssuesWithCriticalMatch.find()) { + String high = licenseIssuesWithCriticalMatch.group(2); // group(1) is critical which we don't use for licenses + String medium = licenseIssuesWithCriticalMatch.group(3); + String low = licenseIssuesWithCriticalMatch.group(4); + + scanResult.highLicenseIssueCount = Long.parseLong(high); + scanResult.mediumLicenseIssueCount = Long.parseLong(medium); + scanResult.lowLicenseIssueCount = Long.parseLong(low); + + return; + } + + Matcher licenseIssuesWithoutCriticalMatch = LICENSE_ISSUES_WITHOUT_CRITICAL_REGEX.matcher(formattedIssues); + if (licenseIssuesWithoutCriticalMatch.matches()) { + String high = licenseIssuesWithoutCriticalMatch.group(1); + String medium = licenseIssuesWithoutCriticalMatch.group(2); + String low = licenseIssuesWithoutCriticalMatch.group(3); - String high = parts[0].replace(" high", ""); - scanResult.highLicenseIssueCount = Long.parseLong(high); + scanResult.highLicenseIssueCount = Long.parseLong(high); + scanResult.mediumLicenseIssueCount = Long.parseLong(medium); + scanResult.lowLicenseIssueCount = Long.parseLong(low); - String medium = parts[1].replace(" medium", ""); - scanResult.mediumLicenseIssueCount = Long.parseLong(medium); + return; + } - String low = parts[2].replace(" low", ""); - scanResult.lowLicenseIssueCount = Long.parseLong(low); + throw new RuntimeException(String.format("Invalid format for license issues: %s", formattedIssues)); } } diff --git a/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java b/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java index 70b873a..55cb5d4 100644 --- a/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java +++ b/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java @@ -3,6 +3,8 @@ import io.snyk.plugins.nexus.model.ScanResult; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class FormatterTest { @@ -128,4 +130,27 @@ void enrichScanResultWithLicenseIssues() { }); } + @Test + void enrichScanResultWithLicenseIssuesWhenFormattedIssuesContainsCritical() { + ScanResult scanResult = new ScanResult(); + + // when + Formatter.enrichScanResultWithLicenseIssues(scanResult, "6 critical, 10 high, 0 medium, 25 low"); + + // then + Assertions.assertAll( + () -> { + Assertions.assertEquals(10, scanResult.highLicenseIssueCount); + Assertions.assertEquals(0, scanResult.mediumLicenseIssueCount); + Assertions.assertEquals(25, scanResult.lowLicenseIssueCount); + }); + } + + @Test + void enrichScanResultWithLicenseIssuesThrowsIfInvalidFormat() { + ScanResult scanResult = new ScanResult(); + RuntimeException e = assertThrows(RuntimeException.class, () -> Formatter.enrichScanResultWithLicenseIssues(scanResult, "10 super, 15 elite, 20 fantastic")); + assertEquals(e.getMessage(), "Invalid format for license issues: 10 super, 15 elite, 20 fantastic"); + } + } From 2c4595452031a84aac0459a8dea40bee41935295 Mon Sep 17 00:00:00 2001 From: maxjeffos <44034094+maxjeffos@users.noreply.github.com> Date: Tue, 2 Nov 2021 17:35:34 -0400 Subject: [PATCH 2/2] refactor: improve parsing of formatted issue counts --- .../io/snyk/plugins/nexus/util/Formatter.java | 106 ++++++++++-------- .../plugins/nexus/util/FormatterTest.java | 21 ++++ 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java b/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java index 20bc385..da23912 100644 --- a/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java +++ b/plugin/src/main/java/io/snyk/plugins/nexus/util/Formatter.java @@ -1,12 +1,12 @@ package io.snyk.plugins.nexus.util; -import javax.annotation.Nonnull; - -import java.util.List; - import io.snyk.plugins.nexus.model.ScanResult; import io.snyk.sdk.model.Issue; import io.snyk.sdk.model.Severity; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -14,17 +14,17 @@ public final class Formatter { - private static final Pattern LICENSE_ISSUES_WITH_CRITICAL_REGEX = Pattern.compile("(\\d+) critical, (\\d+) high, (\\d+) medium, (\\d+) low"); - private static final Pattern LICENSE_ISSUES_WITHOUT_CRITICAL_REGEX = Pattern.compile("(\\d+) high, (\\d+) medium, (\\d+) low"); + private static final Pattern ISSUES_WITH_CRITICAL_REGEX = Pattern.compile("(\\d+) critical, (\\d+) high, (\\d+) medium, (\\d+) low"); + private static final Pattern ISSUES_WITHOUT_CRITICAL_REGEX = Pattern.compile("(\\d+) high, (\\d+) medium, (\\d+) low"); private Formatter() { } public static long getIssuesCountBySeverity(@Nonnull List issues, @Nonnull Severity severity) { return issues.stream() - .filter(issue -> issue.severity == severity) - .filter(distinctByKey(issue -> issue.id)) - .count(); + .filter(issue -> issue.severity == severity) + .filter(distinctByKey(issue -> issue.id)) + .count(); } public static String getVulnerabilityIssuesAsFormattedString(@Nonnull ScanResult scanResult) { @@ -44,24 +44,12 @@ public static void enrichScanResultWithVulnerabilityIssues(@Nonnull ScanResult s return; } - String[] parts = formattedIssues.split(", "); - - int i = 0; - if (parts.length == 4) { - String critical = parts[i].replace(" critical", ""); - scanResult.criticalVulnerabilityIssueCount = Long.parseLong(critical); - i++; - } - String high = parts[i].replace(" high", ""); - scanResult.highVulnerabilityIssueCount = Long.parseLong(high); - i++; - - String medium = parts[i].replace(" medium", ""); - scanResult.mediumVulnerabilityIssueCount = Long.parseLong(medium); - i++; - - String low = parts[i].replace(" low", ""); - scanResult.lowVulnerabilityIssueCount = Long.parseLong(low); + IssueCounts issueCounts = getIssueCountsFromFormattedString(formattedIssues) + .orElseThrow(() -> new RuntimeException(String.format("Invalid format for vulnerability issues: %s", formattedIssues))); + scanResult.criticalVulnerabilityIssueCount = issueCounts.critical.orElse(Long.valueOf(0)); + scanResult.highVulnerabilityIssueCount = issueCounts.high; + scanResult.mediumVulnerabilityIssueCount = issueCounts.medium; + scanResult.lowVulnerabilityIssueCount = issueCounts.low; } public static void enrichScanResultWithLicenseIssues(@Nonnull ScanResult scanResult, String formattedIssues) { @@ -72,32 +60,54 @@ public static void enrichScanResultWithLicenseIssues(@Nonnull ScanResult scanRes return; } - Matcher licenseIssuesWithCriticalMatch = LICENSE_ISSUES_WITH_CRITICAL_REGEX.matcher(formattedIssues); - if (licenseIssuesWithCriticalMatch.find()) { - String high = licenseIssuesWithCriticalMatch.group(2); // group(1) is critical which we don't use for licenses - String medium = licenseIssuesWithCriticalMatch.group(3); - String low = licenseIssuesWithCriticalMatch.group(4); - - scanResult.highLicenseIssueCount = Long.parseLong(high); - scanResult.mediumLicenseIssueCount = Long.parseLong(medium); - scanResult.lowLicenseIssueCount = Long.parseLong(low); + IssueCounts issueCounts = getIssueCountsFromFormattedString(formattedIssues) + .orElseThrow(() -> new RuntimeException(String.format("Invalid format for license issues: %s", formattedIssues))); + // regardless of if critical is present or not, we ignore it + scanResult.highLicenseIssueCount = issueCounts.high; + scanResult.mediumLicenseIssueCount = issueCounts.medium; + scanResult.lowLicenseIssueCount = issueCounts.low; + } - return; + public static Optional getIssueCountsFromFormattedString(String formattedIssues) { + Matcher issuesWithCriticalMatch = ISSUES_WITH_CRITICAL_REGEX.matcher(formattedIssues); + if (issuesWithCriticalMatch.matches()) { + return Optional.of(new IssueCounts( + issuesWithCriticalMatch.group(1), + issuesWithCriticalMatch.group(2), + issuesWithCriticalMatch.group(3), + issuesWithCriticalMatch.group(4) + )); } - Matcher licenseIssuesWithoutCriticalMatch = LICENSE_ISSUES_WITHOUT_CRITICAL_REGEX.matcher(formattedIssues); - if (licenseIssuesWithoutCriticalMatch.matches()) { - String high = licenseIssuesWithoutCriticalMatch.group(1); - String medium = licenseIssuesWithoutCriticalMatch.group(2); - String low = licenseIssuesWithoutCriticalMatch.group(3); + Matcher issuesWithoutCriticalMatch = ISSUES_WITHOUT_CRITICAL_REGEX.matcher(formattedIssues); + if (issuesWithoutCriticalMatch.matches()) { + return Optional.of(new IssueCounts( + issuesWithoutCriticalMatch.group(1), + issuesWithoutCriticalMatch.group(2), + issuesWithoutCriticalMatch.group(3) + )); + } - scanResult.highLicenseIssueCount = Long.parseLong(high); - scanResult.mediumLicenseIssueCount = Long.parseLong(medium); - scanResult.lowLicenseIssueCount = Long.parseLong(low); + return Optional.empty(); + } +} - return; - } +class IssueCounts { + Optional critical = Optional.empty(); + long high = 0; + long medium = 0; + long low = 0; + + IssueCounts(String critical, String high, String medium, String low) { + this.critical = Optional.of(Long.parseLong(critical)); + this.high = Long.parseLong(high); + this.medium = Long.parseLong(medium); + this.low = Long.parseLong(low); + } - throw new RuntimeException(String.format("Invalid format for license issues: %s", formattedIssues)); + IssueCounts(String high, String medium, String low) { + this.high = Long.parseLong(high); + this.medium = Long.parseLong(medium); + this.low = Long.parseLong(low); } } diff --git a/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java b/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java index 55cb5d4..7671a98 100644 --- a/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java +++ b/plugin/src/test/java/io/snyk/plugins/nexus/util/FormatterTest.java @@ -80,6 +80,20 @@ void enrichScanResultWithVulnerabilityIssues() { }); } + @Test + void enrichScanResultWithVulnerabilityIssuesThrowsIfInvalidFormat() { + ScanResult scanResult = new ScanResult(); + RuntimeException e = assertThrows(RuntimeException.class, () -> Formatter.enrichScanResultWithVulnerabilityIssues(scanResult, "10 super, 15 elite, 20 fantastic")); + assertEquals(e.getMessage(), "Invalid format for vulnerability issues: 10 super, 15 elite, 20 fantastic"); + } + + @Test + void enrichScanResultWithVulnerabilityIssuesThrowsIfInvalidValues() { + ScanResult scanResult = new ScanResult(); + RuntimeException e = assertThrows(RuntimeException.class, () -> Formatter.enrichScanResultWithVulnerabilityIssues(scanResult, "a critical, b high, c medium, d low")); + assertEquals(e.getMessage(), "Invalid format for vulnerability issues: a critical, b high, c medium, d low"); + } + @Test void enrichScanResultWithLicenseIssues_null() { // given @@ -153,4 +167,11 @@ void enrichScanResultWithLicenseIssuesThrowsIfInvalidFormat() { assertEquals(e.getMessage(), "Invalid format for license issues: 10 super, 15 elite, 20 fantastic"); } + @Test + void enrichScanResultWithLicenseIssuesThrowsIfInvalidValues() { + ScanResult scanResult = new ScanResult(); + RuntimeException e = assertThrows(RuntimeException.class, () -> Formatter.enrichScanResultWithLicenseIssues(scanResult, "x critical, y high, 0 medium, z low")); + assertEquals(e.getMessage(), "Invalid format for license issues: x critical, y high, 0 medium, z low"); + } + }