-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #105 from snyk/chore/extract-package-validator
chore: extract Package Validator [OSM-2240]
- Loading branch information
Showing
5 changed files
with
235 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
core/src/main/java/io/snyk/plugins/artifactory/scanner/PackageValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package io.snyk.plugins.artifactory.scanner; | ||
|
||
import io.snyk.plugins.artifactory.configuration.ConfigurationModule; | ||
import io.snyk.plugins.artifactory.configuration.PluginConfiguration; | ||
import io.snyk.sdk.model.Severity; | ||
import io.snyk.sdk.model.TestResult; | ||
import org.artifactory.exception.CancelException; | ||
import org.artifactory.repo.RepoPath; | ||
import org.artifactory.repo.Repositories; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import static io.snyk.plugins.artifactory.configuration.ArtifactProperty.ISSUE_LICENSES_FORCE_DOWNLOAD; | ||
import static io.snyk.plugins.artifactory.configuration.ArtifactProperty.ISSUE_VULNERABILITIES_FORCE_DOWNLOAD; | ||
import static java.lang.String.format; | ||
|
||
public class PackageValidator { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(PackageValidator.class); | ||
|
||
private final ConfigurationModule configurationModule; | ||
private final Repositories repositories; | ||
|
||
public PackageValidator(ConfigurationModule configurationModule, Repositories repositories) { | ||
this.configurationModule = configurationModule; | ||
this.repositories = repositories; | ||
} | ||
|
||
public void validate(TestResult testResult, RepoPath repoPath) { | ||
validateVulnerabilityIssues(testResult, repoPath); | ||
validateLicenseIssues(testResult, repoPath); | ||
} | ||
|
||
private void validateVulnerabilityIssues(TestResult testResult, RepoPath repoPath) { | ||
final String vulnerabilitiesForceDownloadProperty = ISSUE_VULNERABILITIES_FORCE_DOWNLOAD.propertyKey(); | ||
final String vulnerabilitiesForceDownload = repositories.getProperty(repoPath, vulnerabilitiesForceDownloadProperty); | ||
final boolean forceDownload = "true".equalsIgnoreCase(vulnerabilitiesForceDownload); | ||
if (forceDownload) { | ||
LOG.debug("Allowing download. Artifact Property \"{}\" is \"true\". {}", vulnerabilitiesForceDownloadProperty, repoPath); | ||
return; | ||
} | ||
|
||
Severity vulnerabilityThreshold = Severity.of(configurationModule.getPropertyOrDefault(PluginConfiguration.SCANNER_VULNERABILITY_THRESHOLD)); | ||
if (vulnerabilityThreshold == Severity.LOW) { | ||
if (!testResult.issues.vulnerabilities.isEmpty()) { | ||
LOG.debug("Found vulnerabilities in {} returning 403", repoPath); | ||
throw new CancelException(format("Artifact has vulnerabilities. %s", repoPath), 403); | ||
} | ||
} else if (vulnerabilityThreshold == Severity.MEDIUM) { | ||
long count = testResult.issues.vulnerabilities.stream() | ||
.filter(vulnerability -> vulnerability.severity == Severity.MEDIUM || vulnerability.severity == Severity.HIGH || vulnerability.severity == Severity.CRITICAL) | ||
.count(); | ||
if (count > 0) { | ||
LOG.debug("Found {} vulnerabilities in {} returning 403", count, repoPath); | ||
throw new CancelException(format("Artifact has vulnerabilities with medium, high or critical severity. %s", repoPath), 403); | ||
} | ||
} else if (vulnerabilityThreshold == Severity.HIGH) { | ||
long count = testResult.issues.vulnerabilities.stream() | ||
.filter(vulnerability -> vulnerability.severity == Severity.HIGH || vulnerability.severity == Severity.CRITICAL) | ||
.count(); | ||
if (count > 0) { | ||
LOG.debug("Found {}, vulnerabilities in {} returning 403", count, repoPath); | ||
throw new CancelException(format("Artifact has vulnerabilities with high or critical severity. %s", repoPath), 403); | ||
} | ||
} else if (vulnerabilityThreshold == Severity.CRITICAL) { | ||
long count = testResult.issues.vulnerabilities.stream() | ||
.filter(vulnerability -> vulnerability.severity == Severity.CRITICAL) | ||
.count(); | ||
if (count > 0) { | ||
LOG.debug("Found {} vulnerabilities in {} returning 403", count, repoPath); | ||
throw new CancelException(format("Artifact has vulnerabilities with critical severity. %s", repoPath), 403); | ||
} | ||
} | ||
} | ||
|
||
private void validateLicenseIssues(TestResult testResult, RepoPath repoPath) { | ||
final String licensesForceDownloadProperty = ISSUE_LICENSES_FORCE_DOWNLOAD.propertyKey(); | ||
final String licensesForceDownload = repositories.getProperty(repoPath, licensesForceDownloadProperty); | ||
final boolean forceDownload = "true".equalsIgnoreCase(licensesForceDownload); | ||
if (forceDownload) { | ||
LOG.debug("Allowing download. Artifact Property \"{}\" is \"true\". {}", repoPath, licensesForceDownloadProperty); | ||
return; | ||
} | ||
|
||
Severity licensesThreshold = Severity.of(configurationModule.getPropertyOrDefault(PluginConfiguration.SCANNER_LICENSE_THRESHOLD)); | ||
if (licensesThreshold == Severity.LOW) { | ||
if (!testResult.issues.licenses.isEmpty()) { | ||
LOG.debug("Found license issues in {} returning 403", repoPath); | ||
throw new CancelException(format("Artifact has license issues. %s", repoPath), 403); | ||
} | ||
} else if (licensesThreshold == Severity.MEDIUM) { | ||
long count = testResult.issues.licenses.stream() | ||
.filter(vulnerability -> vulnerability.severity == Severity.MEDIUM || vulnerability.severity == Severity.HIGH) | ||
.count(); | ||
if (count > 0) { | ||
LOG.debug("Found {} license issues in {} returning 403", count, repoPath); | ||
throw new CancelException(format("Artifact has license issues with medium or high severity. %s", repoPath), 403); | ||
} | ||
} else if (licensesThreshold == Severity.HIGH) { | ||
long count = testResult.issues.licenses.stream() | ||
.filter(vulnerability -> vulnerability.severity == Severity.HIGH) | ||
.count(); | ||
if (count > 0) { | ||
LOG.debug("Found {} license issues in {} returning 403", count, repoPath); | ||
throw new CancelException(format("Artifact has license issues with high severity. %s", repoPath), 403); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
core/src/test/java/io/snyk/plugins/artifactory/scanner/PackageValidatorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package io.snyk.plugins.artifactory.scanner; | ||
|
||
import io.snyk.plugins.artifactory.configuration.ConfigurationModule; | ||
import io.snyk.plugins.artifactory.configuration.PluginConfiguration; | ||
import io.snyk.sdk.model.*; | ||
import org.artifactory.exception.CancelException; | ||
import org.artifactory.repo.RepoPath; | ||
import org.artifactory.repo.Repositories; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.List; | ||
import java.util.Properties; | ||
import java.util.stream.Collectors; | ||
|
||
import static io.snyk.plugins.artifactory.configuration.ArtifactProperty.ISSUE_LICENSES_FORCE_DOWNLOAD; | ||
import static io.snyk.plugins.artifactory.configuration.ArtifactProperty.ISSUE_VULNERABILITIES_FORCE_DOWNLOAD; | ||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
class PackageValidatorTest { | ||
|
||
@Test | ||
void validate_severityBelowThreshold_allowed() { | ||
Repositories repositories = mock(Repositories.class); | ||
RepoPath repoPath = mock(RepoPath.class); | ||
|
||
ConfigurationModule configurationModule = pluginConfig(Severity.MEDIUM, Severity.CRITICAL); | ||
|
||
PackageValidator validator = new PackageValidator(configurationModule, repositories); | ||
TestResult testResult = getTestResult(List.of(Severity.LOW), List.of(Severity.MEDIUM)); | ||
|
||
assertDoesNotThrow(() -> validator.validate(testResult, repoPath)); | ||
} | ||
|
||
@Test | ||
void validate_vulnIssueAboveThreshold_forbidden() { | ||
Repositories repositories = mock(Repositories.class); | ||
RepoPath repoPath = mock(RepoPath.class); | ||
|
||
ConfigurationModule configurationModule = pluginConfig(Severity.HIGH, Severity.LOW); | ||
|
||
PackageValidator validator = new PackageValidator(configurationModule, repositories); | ||
TestResult testResult = getTestResult(List.of(Severity.HIGH), List.of()); | ||
|
||
assertThrows(CancelException.class, () -> validator.validate(testResult, repoPath)); | ||
} | ||
|
||
@Test | ||
void validate_vulnForceDownload_allowed() { | ||
Repositories repositories = mock(Repositories.class); | ||
RepoPath repoPath = mock(RepoPath.class); | ||
|
||
when(repositories.getProperty(repoPath, ISSUE_VULNERABILITIES_FORCE_DOWNLOAD.propertyKey())).thenReturn("true"); | ||
|
||
ConfigurationModule configurationModule = pluginConfig(Severity.HIGH, Severity.LOW); | ||
|
||
PackageValidator validator = new PackageValidator(configurationModule, repositories); | ||
TestResult testResult = getTestResult(List.of(Severity.HIGH), List.of()); | ||
|
||
assertDoesNotThrow(() -> validator.validate(testResult, repoPath)); | ||
} | ||
|
||
@Test | ||
void validate_licenseIssueAboveThreshold_forbidden() { | ||
Repositories repositories = mock(Repositories.class); | ||
RepoPath repoPath = mock(RepoPath.class); | ||
|
||
ConfigurationModule configurationModule = pluginConfig(Severity.LOW, Severity.MEDIUM); | ||
|
||
PackageValidator validator = new PackageValidator(configurationModule, repositories); | ||
TestResult testResult = getTestResult(List.of(), List.of(Severity.MEDIUM)); | ||
|
||
assertThrows(CancelException.class, () -> validator.validate(testResult, repoPath)); | ||
} | ||
|
||
@Test | ||
void validate_licenseForceDownload_allowed() { | ||
Repositories repositories = mock(Repositories.class); | ||
RepoPath repoPath = mock(RepoPath.class); | ||
|
||
when(repositories.getProperty(repoPath, ISSUE_LICENSES_FORCE_DOWNLOAD.propertyKey())).thenReturn("true"); | ||
|
||
ConfigurationModule configurationModule = pluginConfig(Severity.LOW, Severity.MEDIUM); | ||
|
||
PackageValidator validator = new PackageValidator(configurationModule, repositories); | ||
TestResult testResult = getTestResult(List.of(), List.of(Severity.MEDIUM)); | ||
|
||
assertDoesNotThrow(() -> validator.validate(testResult, repoPath)); | ||
} | ||
|
||
private static @NotNull ConfigurationModule pluginConfig(Severity vulnThreshold, Severity licenseThreshold) { | ||
Properties properties = new Properties(); | ||
properties.setProperty(PluginConfiguration.SCANNER_VULNERABILITY_THRESHOLD.propertyKey(), vulnThreshold.getSeverityLevel()); | ||
properties.setProperty(PluginConfiguration.SCANNER_LICENSE_THRESHOLD.propertyKey(), licenseThreshold.getSeverityLevel()); | ||
return new ConfigurationModule(properties); | ||
} | ||
|
||
private static @NotNull TestResult getTestResult(List<Severity> vulnSeverities, List<Severity> licenseSeverities) { | ||
TestResult testResult = new TestResult(); | ||
|
||
testResult.issues = new Issues(); | ||
|
||
testResult.issues.vulnerabilities = vulnSeverities.stream().map(severity -> { | ||
Vulnerability vuln = new Vulnerability(); | ||
vuln.severity = severity; | ||
return vuln; | ||
}).collect(Collectors.toList()); | ||
|
||
testResult.issues.licenses = licenseSeverities.stream().map(severity -> { | ||
Issue issue = new Vulnerability(); | ||
issue.severity = severity; | ||
return issue; | ||
}).collect(Collectors.toList()); | ||
return testResult; | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.