diff --git a/src/main/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDao.java b/src/main/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDao.java index dc1e4aa4d..c24f4036e 100644 --- a/src/main/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDao.java +++ b/src/main/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDao.java @@ -3,6 +3,7 @@ import org.dependencytrack.persistence.jdbi.mapping.VulnPolicyAnalysisArgumentFactory; import org.dependencytrack.persistence.jdbi.mapping.VulnPolicyRatingsArgumentFactory; import org.dependencytrack.persistence.jdbi.mapping.VulnerabilityPolicyRowMapper; +import org.dependencytrack.policy.vulnerability.VulnerabilityPolicy; import org.jdbi.v3.sqlobject.config.RegisterArgumentFactories; import org.jdbi.v3.sqlobject.config.RegisterArgumentFactory; import org.jdbi.v3.sqlobject.config.RegisterRowMapper; @@ -21,35 +22,41 @@ public interface VulnerabilityPolicyDao { @SqlQuery(""" - SELECT "ID", "ANALYSIS", "AUTHOR", "CONDITIONS", "CREATED", "DESCRIPTION", "NAME", "RATINGS", "UPDATED", "VALID_FROM", "VALID_UNTIL" - FROM "VULNERABILITY_POLICY"; + SELECT * FROM "VULNERABILITY_POLICY"; """) - @RegisterRowMapper(VulnerabilityPolicyRowMapper.class) - List getAllVulnerabilityPolicies(); + List getAllVulnerabilityPolicies(); @SqlQuery(""" - SELECT "ID", "ANALYSIS", "AUTHOR", "CONDITIONS", "CREATED", "DESCRIPTION", "NAME", "RATINGS", "UPDATED", "VALID_FROM", "VALID_UNTIL" - FROM "VULNERABILITY_POLICY" WHERE "NAME" = ?; + SELECT * FROM "VULNERABILITY_POLICY" WHERE "NAME" = ?; """) - @RegisterRowMapper(VulnerabilityPolicyRowMapper.class) - org.dependencytrack.policy.vulnerability.VulnerabilityPolicy getVulnerabilityPolicyByName(@Bind String name); + VulnerabilityPolicy getVulnerabilityPolicyByName(@Bind String name); @SqlUpdate(""" - INSERT INTO "VULNERABILITY_POLICY"( - "ANALYSIS", "AUTHOR", "CONDITIONS", "CREATED", "DESCRIPTION", "NAME", "RATINGS", "UPDATED", "VALID_FROM", "VALID_UNTIL") - VALUES (:vulnerabilityPolicy.analysis::JSON, :vulnerabilityPolicy.author, :vulnerabilityPolicy.conditions, :vulnerabilityPolicy.created, :vulnerabilityPolicy.description, :vulnerabilityPolicy.name, :vulnerabilityPolicy.ratings::JSON, :vulnerabilityPolicy.updated, :vulnerabilityPolicy.validFrom, :vulnerabilityPolicy.validUntil) - - """) - int createVulnerabilityPolicy(@BindBean("vulnerabilityPolicy") org.dependencytrack.policy.vulnerability.VulnerabilityPolicy vulnerabilityPolicy); + INSERT INTO "VULNERABILITY_POLICY" + ("ANALYSIS", "AUTHOR", "CONDITIONS", "CREATED", "DESCRIPTION", "NAME", "RATINGS", "UPDATED", "VALID_FROM", "VALID_UNTIL") + VALUES + ((:analysis)::JSON, :author, :conditions, :created, :description, :name, (:ratings)::JSON, :updated, :validFrom, :validUntil) + """) + int createVulnerabilityPolicy(@BindBean VulnerabilityPolicy vulnerabilityPolicy); @SqlUpdate(""" - DELETE FROM "VULNERABILITY_POLICY" WHERE "NAME" = ? - """) + DELETE FROM "VULNERABILITY_POLICY" WHERE "NAME" = ? + """) int deleteVulnerabilityPolicyByName(@Bind String name); + @SqlUpdate(""" - UPDATE "VULNERABILITY_POLICY" - SET "ANALYSIS"=:vulnerabilityPolicy.analysis::JSON, "AUTHOR"=:vulnerabilityPolicy.author, "CONDITIONS"=:vulnerabilityPolicy.conditions, "DESCRIPTION"=:vulnerabilityPolicy.description, "RATINGS"=:vulnerabilityPolicy.ratings::JSON, "UPDATED"=:vulnerabilityPolicy.updated, "VALID_FROM"=:vulnerabilityPolicy.validFrom, "VALID_UNTIL"=:vulnerabilityPolicy.validUntil - WHERE "NAME" = :name - """) - int updateVulnerabilityPolicyByName(@BindBean("vulnerabilityPolicy") org.dependencytrack.policy.vulnerability.VulnerabilityPolicy vulnerabilityPolicy, @Bind String name); + UPDATE "VULNERABILITY_POLICY" + SET + "ANALYSIS" = :analysis::JSON, + "AUTHOR" = :author, + "CONDITIONS" = :conditions, + "DESCRIPTION" = :description, + "RATINGS" = :ratings::JSON, + "UPDATED" = :updated, + "VALID_FROM" = :validFrom, + "VALID_UNTIL" = :validUntil + WHERE "NAME" = :name + """) + int updateVulnerabilityPolicyByName(@BindBean VulnerabilityPolicy vulnerabilityPolicy); + } diff --git a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTask.java b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTask.java index 19b10d2dc..2fab80f2f 100644 --- a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTask.java +++ b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTask.java @@ -7,13 +7,16 @@ import net.javacrumbs.shedlock.core.LockingTaskExecutor.Task; import org.dependencytrack.common.ConfigKey; import org.dependencytrack.event.VulnerabilityPolicyFetchEvent; +import org.dependencytrack.model.VulnerabilityPolicyBundle; import org.dependencytrack.model.WorkflowState; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.tasks.vulnerabilitypolicy.blobstorage.BlobStorageAccessFactory; import org.dependencytrack.tasks.vulnerabilitypolicy.blobstorage.BlobStorageAccessHandler; +import org.dependencytrack.tasks.vulnerabilitypolicy.blobstorage.VulnerabilityPolicyBundleFile; import org.dependencytrack.util.VulnerabilityPolicyUtil; import javax.naming.OperationNotSupportedException; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Date; import java.util.zip.ZipInputStream; @@ -68,20 +71,40 @@ private void syncPolicyBundle(final QueryManager qm) throws IOException { } try { - // perform head request on file server to see if the hash of file has changed from previous fetch - if (handler.verifyDownloadNeeded()) { - LOGGER.info("It has been verified that file download would be needed from bundle source"); - //if hash has changed, get the new zip file and unzip it to get the policy file - try (ZipInputStream inputStream = handler.downloadZippedContent()) { - LOGGER.info("Parsing downloaded policies for saving/ updating"); - VulnerabilityPolicyUtil.parseAndSavePolicies(inputStream); - inputStream.close(); - LOGGER.info("Policies saved to database successfully"); - } - } else { + if (!handler.verifyDownloadNeeded()) { LOGGER.info("The zipped file content has not changed since last check. Will check in the next iteration"); + qm.updateWorkflowStateToComplete(workflowState); + return; } + // perform head request on file server to see if the hash of file has changed from previous fetch + LOGGER.info("It has been verified that file download would be needed from bundle source"); + final VulnerabilityPolicyBundleFile bundleFile = handler.download(); + //if hash has changed, get the new zip file and unzip it to get the policy file + try (ZipInputStream inputStream = new ZipInputStream(new ByteArrayInputStream(bundleFile.content()))) { + LOGGER.info("Parsing downloaded policies for saving/ updating"); + VulnerabilityPolicyUtil.parseAndSavePolicies(inputStream); + LOGGER.info("Policies saved to database successfully"); + } + + qm.runInTransaction(() -> { + final var now = new Date(); + VulnerabilityPolicyBundle policyBundle = qm.getVulnerabilityPolicyBundle(); + if (policyBundle == null) { + policyBundle = new VulnerabilityPolicyBundle(); + policyBundle.setUrl(bundleFile.url()); + policyBundle.setHash(bundleFile.hash()); + policyBundle.setCreated(now); + policyBundle.setLastSuccessfulSync(now); + qm.getPersistenceManager().makePersistent(policyBundle); + } else { + policyBundle.setUrl(bundleFile.url()); + policyBundle.setHash(bundleFile.hash()); + policyBundle.setUpdated(now); + policyBundle.setLastSuccessfulSync(now); + } + }); + qm.updateWorkflowStateToComplete(workflowState); } catch (RuntimeException e) { qm.updateWorkflowStateToFailed(workflowState, e.getMessage()); diff --git a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/BlobStorageAccessHandler.java b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/BlobStorageAccessHandler.java index beed44cb8..c9dd4028a 100644 --- a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/BlobStorageAccessHandler.java +++ b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/BlobStorageAccessHandler.java @@ -2,16 +2,11 @@ import java.io.IOException; -import java.util.zip.ZipInputStream; public interface BlobStorageAccessHandler { - default boolean verifyDownloadNeeded() throws IOException { - return false; - } + boolean verifyDownloadNeeded() throws IOException; - default ZipInputStream downloadZippedContent() throws IOException { - return null; - } + VulnerabilityPolicyBundleFile download() throws IOException; } diff --git a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandler.java b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandler.java index 1bf2433ff..b62683a46 100644 --- a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandler.java +++ b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandler.java @@ -1,14 +1,15 @@ package org.dependencytrack.tasks.vulnerabilitypolicy.blobstorage; import alpine.Config; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.Header; -import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; import org.dependencytrack.common.ConfigKey; import org.dependencytrack.common.HttpClientPool; import org.dependencytrack.util.HttpUtil; @@ -17,7 +18,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Optional; -import java.util.zip.ZipInputStream; public class NginxStorageHandler implements BlobStorageAccessHandler { @@ -90,9 +90,17 @@ public boolean verifyDownloadNeeded() throws IOException { } @Override - public ZipInputStream downloadZippedContent() throws IOException { - CloseableHttpResponse response = performGetRequest(); - HttpEntity entity = response.getEntity(); - return new ZipInputStream(entity.getContent()); + public VulnerabilityPolicyBundleFile download() throws IOException { + final byte[] bundleFileContent; + try (final CloseableHttpResponse response = performGetRequest()) { + bundleFileContent = EntityUtils.toByteArray(response.getEntity()); + } + + return new VulnerabilityPolicyBundleFile( + Config.getInstance().getProperty(ConfigKey.VULNERABILITY_POLICY_BUNDLE_URL), + DigestUtils.sha256Hex(bundleFileContent), + bundleFileContent + ); } + } diff --git a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandler.java b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandler.java index 0df5fc304..0a5b832bf 100644 --- a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandler.java +++ b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandler.java @@ -1,16 +1,16 @@ package org.dependencytrack.tasks.vulnerabilitypolicy.blobstorage; import alpine.Config; +import org.apache.commons.codec.digest.DigestUtils; import org.dependencytrack.common.ConfigKey; import org.dependencytrack.tasks.vulnerabilitypolicy.S3Client; import org.dependencytrack.util.VulnerabilityPolicyUtil; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.NoSuchElementException; -import java.util.zip.ZipInputStream; public class S3StorageHandler implements BlobStorageAccessHandler { + private final S3Client s3Client; public S3StorageHandler() { @@ -34,8 +34,11 @@ public boolean verifyDownloadNeeded() throws IOException { } @Override - public ZipInputStream downloadZippedContent() throws IOException { - return new ZipInputStream(new ByteArrayInputStream(s3Client.getObject())); + public VulnerabilityPolicyBundleFile download() throws IOException { + final var bundleUrl = "%s/%s/%s".formatted(Config.getInstance().getProperty(ConfigKey.VULNERABILITY_POLICY_BUNDLE_URL), s3Client.getS3BucketName(), s3Client.getBundleToFetch()); + final byte[] bundleContent = s3Client.getObject(); + final String bundleHash = DigestUtils.sha256Hex(bundleContent); + return new VulnerabilityPolicyBundleFile(bundleUrl, bundleHash, bundleContent); } } diff --git a/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/VulnerabilityPolicyBundleFile.java b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/VulnerabilityPolicyBundleFile.java new file mode 100644 index 000000000..c1d00b633 --- /dev/null +++ b/src/main/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/VulnerabilityPolicyBundleFile.java @@ -0,0 +1,11 @@ +package org.dependencytrack.tasks.vulnerabilitypolicy.blobstorage; + +/** + * A vulnerability policy bundle file downloaded via {@link BlobStorageAccessHandler}. + * + * @param url URL the file was downloaded from + * @param hash SHA256 digest of the file content + * @param content The file content + */ +public record VulnerabilityPolicyBundleFile(String url, String hash, byte[] content) { +} diff --git a/src/main/java/org/dependencytrack/util/VulnerabilityPolicyUtil.java b/src/main/java/org/dependencytrack/util/VulnerabilityPolicyUtil.java index b6ad5596a..3ad288e04 100644 --- a/src/main/java/org/dependencytrack/util/VulnerabilityPolicyUtil.java +++ b/src/main/java/org/dependencytrack/util/VulnerabilityPolicyUtil.java @@ -2,28 +2,28 @@ import alpine.common.logging.Logger; import alpine.model.ConfigProperty; -import alpine.server.util.DbUtil; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import com.networknt.schema.ValidationMessage; import org.dependencytrack.model.ConfigPropertyConstants; -import org.dependencytrack.model.VulnerabilityPolicy; import org.dependencytrack.persistence.QueryManager; +import org.dependencytrack.persistence.jdbi.VulnerabilityPolicyDao; import org.dependencytrack.policy.cel.CelPolicyScriptHost; import org.dependencytrack.policy.cel.CelPolicyType; +import org.dependencytrack.policy.vulnerability.VulnerabilityPolicy; import org.projectnessie.cel.tools.ScriptCreateException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.sql.Connection; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -31,10 +31,14 @@ import java.util.zip.ZipInputStream; import static org.apache.commons.io.IOUtils.resourceToString; +import static org.dependencytrack.persistence.jdbi.JdbiFactory.jdbi; public class VulnerabilityPolicyUtil { + private static final Logger LOGGER = Logger.getLogger(VulnerabilityPolicyUtil.class); - private static final ObjectMapper MAPPER = new ObjectMapper(new YAMLFactory()).registerModule(new JsonOrgModule()); + private static final ObjectMapper MAPPER = new ObjectMapper(new YAMLFactory()) + .registerModule(new JavaTimeModule()) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); private static JsonSchema schema = null; @@ -89,8 +93,8 @@ public static void parseAndSavePolicies(ZipInputStream zipInputStream) throws IO ZipEntry zipEntry = zipInputStream.getNextEntry(); List createVulnerabilityPolicyList = new ArrayList<>(); List updateVulnerabilityPolicyList = new ArrayList<>(); - try (QueryManager queryManager = new QueryManager()) { - List existingVulnerabilityPolicies = queryManager.getAllVulnerabilityPolicies(); + try (final var qm = new QueryManager()) { + List existingVulnerabilityPolicies = jdbi(qm).withExtension(VulnerabilityPolicyDao.class, VulnerabilityPolicyDao::getAllVulnerabilityPolicies); List policyNames = existingVulnerabilityPolicies.stream().map(VulnerabilityPolicy::getName).toList(); int numTotalPolicies = 0, numParsingFailures = 0; while (zipEntry != null) { @@ -116,7 +120,7 @@ public static void parseAndSavePolicies(ZipInputStream zipInputStream) throws IO Policy bundles can only be applied when all policy definitions within them are valid.\ """.formatted(numParsingFailures, numTotalPolicies)); } - saveParsedVulnerabilities(queryManager, createVulnerabilityPolicyList, updateVulnerabilityPolicyList, policyNames); + saveParsedVulnerabilities(qm, createVulnerabilityPolicyList, updateVulnerabilityPolicyList, policyNames); } } @@ -146,7 +150,7 @@ public static boolean matchWithHashConfigProperty(String etag) { } } - public static void saveParsedVulnerabilities(QueryManager queryManager, List createVulnerabilityPolicyList, + public static void saveParsedVulnerabilities(QueryManager qm, List createVulnerabilityPolicyList, List updateVulnerabilityPolicyList, List policyNames) { List receivedPolicyNames = new ArrayList<>(); @@ -158,26 +162,21 @@ public static void saveParsedVulnerabilities(QueryManager queryManager, List vulnerabilityPoliciesToBeDeleted = policyNames.stream() .filter(policyName -> !receivedPolicyNames.contains(policyName)).toList(); - queryManager.runInTransaction(() -> { - Connection connection = null; - try { - connection = (Connection) queryManager.getPersistenceManager().getDataStoreConnection(); - for (var vulnerabilityPolicy : createVulnerabilityPolicyList) { - LOGGER.info("Creating vulnerability policy: %s".formatted(vulnerabilityPolicy.getName())); - queryManager.createVulnerabilityPolicy(vulnerabilityPolicy, connection); - } - for (var vulnerabilityPolicy : updateVulnerabilityPolicyList) { - LOGGER.info("Updating vulnerability policy: %s".formatted(vulnerabilityPolicy.getName())); - queryManager.updateVulnerablePolicyByName(vulnerabilityPolicy, connection); - } - for (var name : vulnerabilityPoliciesToBeDeleted) { - LOGGER.info("Deleting vulnerability policy: %s".formatted(name)); - queryManager.deleteVulnerabilityPolicyByName(name, connection); - } - } finally { - DbUtil.close(connection); - } + jdbi(qm).useTransaction(jdbiHandle -> { + final var vulnPolicyDao = jdbiHandle.attach(VulnerabilityPolicyDao.class); + for (var vulnerabilityPolicy : createVulnerabilityPolicyList) { + LOGGER.info("Creating vulnerability policy: %s".formatted(vulnerabilityPolicy.getName())); + vulnPolicyDao.createVulnerabilityPolicy(vulnerabilityPolicy); + } + for (var vulnerabilityPolicy : updateVulnerabilityPolicyList) { + LOGGER.info("Updating vulnerability policy: %s".formatted(vulnerabilityPolicy.getName())); + vulnPolicyDao.updateVulnerabilityPolicyByName(vulnerabilityPolicy); + } + for (var name : vulnerabilityPoliciesToBeDeleted) { + LOGGER.info("Deleting vulnerability policy: %s".formatted(name)); + vulnPolicyDao.deleteVulnerabilityPolicyByName(name); + } }); } diff --git a/src/test/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDaoTest.java b/src/test/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDaoTest.java index 1bbb70a1f..7fcad9028 100644 --- a/src/test/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDaoTest.java +++ b/src/test/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDaoTest.java @@ -159,7 +159,7 @@ public void testUpdateVulnerabilityPolicyByName() throws Exception { vulnPolicy.setAuthor("Jon Doe"); int updatedCount = JdbiFactory.jdbi(qm).withExtension(VulnerabilityPolicyDao.class, - dao -> dao.updateVulnerabilityPolicyByName(vulnPolicy, "name")); + dao -> dao.updateVulnerabilityPolicyByName(vulnPolicy)); assertThat(updatedCount).isEqualTo(1); diff --git a/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTaskTest.java b/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTaskTest.java index 4e37661b8..07c865ac0 100644 --- a/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/VulnerabilityPolicyFetchTaskTest.java @@ -7,6 +7,7 @@ import org.apache.http.HttpStatus; import org.dependencytrack.AbstractPostgresEnabledTest; import org.dependencytrack.event.VulnerabilityPolicyFetchEvent; +import org.dependencytrack.model.VulnerabilityPolicyBundle; import org.dependencytrack.model.WorkflowState; import org.dependencytrack.model.WorkflowStatus; import org.dependencytrack.model.WorkflowStep; @@ -34,7 +35,6 @@ import java.util.Date; import java.util.List; import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; @@ -66,7 +66,7 @@ public void testInform() throws Exception { blobStorageAccessFactory.when(BlobStorageAccessFactory::createStorageHandler).thenReturn(handler); Mockito.when(handler.verifyDownloadNeeded()).thenReturn(false); new VulnerabilityPolicyFetchTask(handler).inform(new VulnerabilityPolicyFetchEvent()); - final ZipInputStream zipInputStream = Mockito.verify(handler, Mockito.never()).downloadZippedContent(); + Mockito.verify(handler, Mockito.never()).download(); blobStorageAccessFactory.close(); } @@ -80,7 +80,7 @@ public void testInformPolicyAnalysisNotEnabled() throws Exception { blobStorageAccessFactory.when(BlobStorageAccessFactory::createStorageHandler).thenReturn(handler); Mockito.when(handler.verifyDownloadNeeded()).thenReturn(false); new VulnerabilityPolicyFetchTask(handler).inform(new VulnerabilityPolicyFetchEvent()); - final ZipInputStream zipInputStream = Mockito.verify(handler, Mockito.never()).downloadZippedContent(); + Mockito.verify(handler, Mockito.never()).download(); Mockito.verify(handler, Mockito.never()).verifyDownloadNeeded(); blobStorageAccessFactory.close(); @@ -93,9 +93,9 @@ public void testInformIOException() throws Exception { BlobStorageAccessHandler handler = Mockito.mock(NginxStorageHandler.class); blobStorageAccessFactory = Mockito.mockStatic(BlobStorageAccessFactory.class); blobStorageAccessFactory.when(BlobStorageAccessFactory::createStorageHandler).thenReturn(handler); - final ZipInputStream zipInputStream = Mockito.doThrow(IOException.class).when(handler).downloadZippedContent(); + Mockito.doThrow(IOException.class).when(handler).download(); new VulnerabilityPolicyFetchTask(handler).inform(new VulnerabilityPolicyFetchEvent()); - final ZipInputStream zipInputStream1 = Mockito.verify(handler, Mockito.times(0)).downloadZippedContent(); + Mockito.verify(handler, Mockito.times(0)).download(); Mockito.verify(handler, Mockito.times(1)).verifyDownloadNeeded(); blobStorageAccessFactory.close(); @@ -193,6 +193,14 @@ public void testInformWithValidPolicy() throws Exception { assertThat(policy.getAnalysis().getState()).isEqualTo(VulnerabilityPolicyAnalysis.State.IN_TRIAGE); }); + final VulnerabilityPolicyBundle policyBundle = qm.getVulnerabilityPolicyBundle(); + assertThat(policyBundle).isNotNull(); + assertThat(policyBundle.getCreated()).isNotNull(); + assertThat(policyBundle.getUpdated()).isNull(); + assertThat(policyBundle.getLastSuccessfulSync()).isNotNull(); + assertThat(policyBundle.getUrl()).isEqualTo(wireMockRule.url("/bundles/bundle.zip")); + assertThat(policyBundle.getHash()).matches("^[a-z0-9]{64}$"); + qm.getPersistenceManager().refresh(workflowState); assertThat(workflowState.getStatus()).isEqualTo(WorkflowStatus.COMPLETED); assertThat(workflowState.getFailureReason()).isNull(); @@ -268,6 +276,8 @@ public void testInformWithOneValidAndOneInvalidPolicy() throws Exception { assertThat(qm.getAllVulnerabilityPolicies()).isEmpty(); + assertThat(qm.getVulnerabilityPolicyBundle()).isNull(); + // TODO: Failure reason should include information about validation errors per policy. // The current message is quite generic and not helpful at all to end users. qm.getPersistenceManager().refresh(workflowState); diff --git a/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandlerTest.java b/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandlerTest.java index 761a1d8a4..44089c2cc 100644 --- a/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandlerTest.java +++ b/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/NginxStorageHandlerTest.java @@ -6,7 +6,7 @@ import org.assertj.core.api.Assertions; import org.dependencytrack.AbstractPostgresEnabledTest; import org.dependencytrack.model.ConfigPropertyConstants; -import org.dependencytrack.model.VulnerabilityPolicy; +import org.dependencytrack.policy.vulnerability.VulnerabilityPolicy; import org.dependencytrack.util.VulnerabilityPolicyUtil; import org.junit.After; import org.junit.Before; diff --git a/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandlerTest.java b/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandlerTest.java index 362b8c776..11aebb51b 100644 --- a/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandlerTest.java +++ b/src/test/java/org/dependencytrack/tasks/vulnerabilitypolicy/blobstorage/S3StorageHandlerTest.java @@ -3,12 +3,6 @@ import io.minio.MakeBucketArgs; import io.minio.PutObjectArgs; -import io.minio.errors.ErrorResponseException; -import io.minio.errors.InsufficientDataException; -import io.minio.errors.InternalException; -import io.minio.errors.InvalidResponseException; -import io.minio.errors.ServerException; -import io.minio.errors.XmlParserException; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.tasks.vulnerabilitypolicy.S3Client; import org.junit.After; @@ -19,12 +13,9 @@ import org.junit.jupiter.api.Assertions; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.zip.ZipEntry; @@ -58,9 +49,7 @@ public void afterTest() { } @Test - public void testVerifyDownloadNeededBucketDoesNotExist() throws IOException, ServerException, InsufficientDataException, - ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, - XmlParserException, InternalException { + public void testVerifyDownloadNeededBucketDoesNotExist() { S3Client s3Client = new S3Client("http://" + minioContainer.getHostAddress(), "test.zip", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "testbucket", null); s3StorageHandler = new S3StorageHandler(s3Client); @@ -68,9 +57,7 @@ public void testVerifyDownloadNeededBucketDoesNotExist() throws IOException, Ser } @Test - public void testVerifyDownloadNeededBucketExist() throws IOException, ServerException, InsufficientDataException, - ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, - XmlParserException, InternalException { + public void testVerifyDownloadNeededBucketExist() throws Exception { S3Client s3Client = new S3Client("http://" + minioContainer.getHostAddress(), "test.zip", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "testbucket", null); @@ -84,9 +71,7 @@ public void testVerifyDownloadNeededBucketExist() throws IOException, ServerExce } @Test - public void testDownload() throws IOException, ServerException, InsufficientDataException, - ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, - XmlParserException, InternalException { + public void testDownload() throws Exception { S3Client s3Client = new S3Client("http://" + minioContainer.getHostAddress(), "test.zip", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "testbucket", null); Path path = Paths.get("src/test/resources/unit/tasks/vulnerabilitypolicy/test.zip"); @@ -95,8 +80,9 @@ public void testDownload() throws IOException, ServerException, InsufficientData s3Client.getMinioClient().putObject(PutObjectArgs.builder().bucket("testbucket"). object("test.zip").stream(new ByteArrayInputStream(arr), -1, 10485760).build()); s3StorageHandler = new S3StorageHandler(s3Client); + final VulnerabilityPolicyBundleFile bundleFile = s3StorageHandler.download(); ArrayList zipEntries; - try (ZipInputStream zipInputStream = s3StorageHandler.downloadZippedContent()) { + try (ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(bundleFile.content()))) { ZipEntry zipEntry = zipInputStream.getNextEntry(); zipEntries = new ArrayList<>(); while (zipEntry != null) { diff --git a/src/test/java/org/dependencytrack/util/VulnerabilityPolicyUtilTest.java b/src/test/java/org/dependencytrack/util/VulnerabilityPolicyUtilTest.java index 1a0598ac3..f1a004d8c 100644 --- a/src/test/java/org/dependencytrack/util/VulnerabilityPolicyUtilTest.java +++ b/src/test/java/org/dependencytrack/util/VulnerabilityPolicyUtilTest.java @@ -2,7 +2,7 @@ import org.dependencytrack.AbstractPostgresEnabledTest; import org.dependencytrack.model.ConfigPropertyConstants; -import org.dependencytrack.model.VulnerabilityPolicy; +import org.dependencytrack.policy.vulnerability.VulnerabilityPolicy; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.projectnessie.cel.tools.ScriptCreateException;