From 0e97be1f2c924479be9371411420990b433cc24f Mon Sep 17 00:00:00 2001 From: Johannes Zahn Date: Thu, 12 Aug 2021 15:59:15 +0200 Subject: [PATCH 1/2] use JSONAssert instead of assertEquals with JSON Objects Signed-off-by: Johannes Zahn --- .../persistence-defectdojo/hook/build.gradle | 1 + .../persistence-defectdojo/hook/lombok.config | 4 --- ...CodeBoxFindingsToDefectDojoMapperTest.java | 33 +++++++++---------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/hooks/persistence-defectdojo/hook/build.gradle b/hooks/persistence-defectdojo/hook/build.gradle index 403270147d..ac133509fa 100644 --- a/hooks/persistence-defectdojo/hook/build.gradle +++ b/hooks/persistence-defectdojo/hook/build.gradle @@ -35,6 +35,7 @@ dependencies { testImplementation(platform('org.junit:junit-bom:5.7.0')) testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation group: 'org.skyscreamer', name: 'jsonassert', version: '1.5.0' testImplementation "org.mockito:mockito-core:2.+" testImplementation "org.mockito:mockito-junit-jupiter:2.+" diff --git a/hooks/persistence-defectdojo/hook/lombok.config b/hooks/persistence-defectdojo/hook/lombok.config index 6c740c2267..6aa51d71ec 100644 --- a/hooks/persistence-defectdojo/hook/lombok.config +++ b/hooks/persistence-defectdojo/hook/lombok.config @@ -1,6 +1,2 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 - # This file is generated by the 'io.freefair.lombok' Gradle plugin config.stopBubbling = true diff --git a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java index a09b6e8b16..79726a3f35 100644 --- a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java +++ b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java @@ -4,13 +4,16 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.securecodebox.persistence.models.SecureCodeBoxFinding; +import org.json.JSONException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; +import org.skyscreamer.jsonassert.JSONAssert; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import static org.junit.jupiter.api.Assertions.*; @@ -18,26 +21,16 @@ @ExtendWith(MockitoExtension.class) public class SecureCodeBoxFindingsToDefectDojoMapperTest { + ClassLoader cl = getClass().getClassLoader(); @Test - public void yieldsCorrectResult() throws IOException { + public void yieldsCorrectResult() throws IOException, JSONException { String ddFindingsPath = "kubehunter-dd-findings.json"; String scbFindingsPath = "kubehunter-scb-findings.json"; - ClassLoader cl = getClass().getClassLoader(); - - File ddFindingsFile = new File(cl.getResource(ddFindingsPath).getFile()); - File scbFindingsFile = new File(cl.getResource(scbFindingsPath).getFile()); - String expectedResult = new String(Files.readAllBytes(ddFindingsFile.toPath())); - String scbFindingsContent = new String(Files.readAllBytes(scbFindingsFile.toPath())); - String result = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeboxFindingsJson(scbFindingsContent); - ObjectMapper mapper = new ObjectMapper(); - JsonNode actualJSON = mapper.readTree(result); - JsonNode expectedJSON = mapper.readTree(expectedResult); - assertNotNull(actualJSON); - // if whitespaces should be ignored in strings, a Custom Comperator could be used - // then the result and expected result would not have to match exactly. - // see https://www.baeldung.com/jackson-compare-two-json-objects - assertEquals(actualJSON, expectedJSON); + String expectedDefectDojoFindings = readFileAsString(ddFindingsPath); + String scbJsonString = readFileAsString(scbFindingsPath); + String actualDefectDojoFindings = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeboxFindingsJson(scbJsonString); + JSONAssert.assertEquals(expectedDefectDojoFindings, actualDefectDojoFindings,false); } @Test @@ -90,4 +83,10 @@ public void doesntThrowUnexpectedExceptionOnEmptyFinding() throws JsonProcessing assertNull(ddFinding.getTitle()); assertNull(ddFinding.getDescription()); } + + public String readFileAsString(String fileName) throws IOException + { + Path filePath = Paths.get(cl.getResource(fileName).getPath()); + return new String(Files.readAllBytes(filePath)); + } } From 931e4c4a05bf2da40935bae2c239ca707fc26c85 Mon Sep 17 00:00:00 2001 From: Johannes Zahn Date: Thu, 12 Aug 2021 18:36:10 +0200 Subject: [PATCH 2/2] add option to customize defectdojo timezone Signed-off-by: Johannes Zahn --- .../config/FindingMapperConfig.java | 20 ++++++++++++++ .../config/PersistenceProviderConfig.java | 3 +++ ...cureCodeBoxFindingToDefectDojoMapper.java} | 26 ++++++++++++------- .../persistence/service/ScanService.java | 5 ++-- ...CodeBoxFindingToDefectDojoMapperTest.java} | 20 +++++++++----- 5 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/FindingMapperConfig.java rename hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/{SecureCodeBoxFindingsToDefectDojoMapper.java => SecureCodeBoxFindingToDefectDojoMapper.java} (83%) rename hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/{SecureCodeBoxFindingsToDefectDojoMapperTest.java => SecureCodeBoxFindingToDefectDojoMapperTest.java} (81%) diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/FindingMapperConfig.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/FindingMapperConfig.java new file mode 100644 index 0000000000..686aafc2f1 --- /dev/null +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/FindingMapperConfig.java @@ -0,0 +1,20 @@ +package io.securecodebox.persistence.config; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.ZoneId; +import java.util.TimeZone; + +/** + * Stores the config for parsing + */ +@NoArgsConstructor +@AllArgsConstructor +public class FindingMapperConfig { + // In contrast to the secureCodeBox, DefectDojo Dates have no TimeZone Information + // Therefore to consistently convert the Findings in both directions a TimeZone has to be assumed or specified + @Getter + private TimeZone defectDojoTimezone = TimeZone.getTimeZone(ZoneId.systemDefault()); +} diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/PersistenceProviderConfig.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/PersistenceProviderConfig.java index 96e891c06e..3a9ac5852f 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/PersistenceProviderConfig.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/config/PersistenceProviderConfig.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import java.util.List; +import java.util.TimeZone; /** * Reads the configured Up / Download Urls for RawResults and Findings from the command line args and determines if @@ -23,6 +24,8 @@ public class PersistenceProviderConfig { final int RAW_RESULT_UPLOAD_ARG_POSITION = 2; final int FINDING_UPLOAD_ARG_POSITION = 3; + TimeZone defectDojoTimeZone = TimeZone.getDefault(); + // Download Urls @Getter final String rawResultDownloadUrl; diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingToDefectDojoMapper.java similarity index 83% rename from hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java rename to hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingToDefectDojoMapper.java index 770ae51fa0..1476cd9cc1 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingToDefectDojoMapper.java @@ -7,8 +7,10 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import io.securecodebox.persistence.config.FindingMapperConfig; import io.securecodebox.persistence.models.DefectDojoImportFinding; import io.securecodebox.persistence.models.SecureCodeBoxFinding; +import lombok.NoArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,10 +24,16 @@ import java.util.Collections; import java.util.List; -public class SecureCodeBoxFindingsToDefectDojoMapper { - private static final Logger LOG = LoggerFactory.getLogger(SecureCodeBoxFindingsToDefectDojoMapper.class); +@NoArgsConstructor +public class SecureCodeBoxFindingToDefectDojoMapper { + private static final Logger LOG = LoggerFactory.getLogger(SecureCodeBoxFindingToDefectDojoMapper.class); private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final ObjectWriter prettyJSONPrinter = new ObjectMapper().findAndRegisterModules().writerWithDefaultPrettyPrinter(); + private FindingMapperConfig mappingConfig = new FindingMapperConfig(); + + public SecureCodeBoxFindingToDefectDojoMapper(FindingMapperConfig config){ + this.mappingConfig = config; + } /** * Converts a SecureCodeBox Findings JSON String to a DefectDojo Findings JSON String. @@ -34,7 +42,7 @@ public class SecureCodeBoxFindingsToDefectDojoMapper { * @return DefectDojo Findings JSON File as String, compatible with the DefectDojo Generic JSON Parser * @throws IOException */ - public static String fromSecureCodeboxFindingsJson(String scbFindingsJson) throws IOException { + public String fromSecureCodeboxFindingsJson(String scbFindingsJson) throws IOException { LOG.debug("Converting SecureCodeBox Findings to DefectDojo Findings"); ObjectMapper mapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) @@ -52,7 +60,7 @@ public static String fromSecureCodeboxFindingsJson(String scbFindingsJson) throw return ddFindingJson.toString(); } - protected static String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severities severity) { + protected String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severities severity) { if (severity == null) { return "Info"; } @@ -79,7 +87,7 @@ protected static String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severit * @return Finding in DefectDojo Format, compatible with the DefectDojo Generic JSON Parser * @throws JsonProcessingException */ - protected static DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxFinding secureCodeBoxFinding) throws JsonProcessingException { + protected DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxFinding secureCodeBoxFinding) throws JsonProcessingException { //set basic Finding info DefectDojoImportFinding result = new DefectDojoImportFinding(); result.setTitle(secureCodeBoxFinding.getName()); @@ -97,7 +105,7 @@ protected static DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxF return result; } - private static void setFindingLocation(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) { + private void setFindingLocation(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) { if (secureCodeBoxFinding.getLocation() != null && !secureCodeBoxFinding.getLocation().isEmpty()) { try { URI.create(secureCodeBoxFinding.getLocation()); @@ -108,7 +116,7 @@ private static void setFindingLocation(SecureCodeBoxFinding secureCodeBoxFinding } } - private static void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) { + private void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) { Instant instant = null; if (secureCodeBoxFinding.getIdentifiedAt() != null && !secureCodeBoxFinding.getIdentifiedAt().isEmpty()) { instant = Instant.parse(secureCodeBoxFinding.getIdentifiedAt()); @@ -118,7 +126,8 @@ private static void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, De else { instant = Instant.now(); } - LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + ZoneId zoneId = this.mappingConfig.getDefectDojoTimezone().toZoneId(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant,zoneId); result.setDate(dtf.format(localDateTime)); } @@ -126,7 +135,6 @@ private static String capitalize(String str) { if (str == null || str.isEmpty()) { return str; } - return str.substring(0, 1).toUpperCase() + str.substring(1); } } diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/service/ScanService.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/service/ScanService.java index 3d5864d5dd..bc81d928f6 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/service/ScanService.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/service/ScanService.java @@ -2,7 +2,7 @@ import io.securecodebox.persistence.config.PersistenceProviderConfig; import io.securecodebox.persistence.defectdojo.models.ScanFile; -import io.securecodebox.persistence.mapping.SecureCodeBoxFindingsToDefectDojoMapper; +import io.securecodebox.persistence.mapping.SecureCodeBoxFindingToDefectDojoMapper; import io.securecodebox.persistence.models.Scan; import io.securecodebox.persistence.util.ScanNameMapping; import org.apache.commons.io.FilenameUtils; @@ -24,7 +24,8 @@ public static ScanFile downloadScan(Scan scan, PersistenceProviderConfig ppConfi LOG.debug("No explicit Parser specified for ScanType {}, using Findings JSON Scan Result", scanType); downloadUrl = ppConfig.getFindingDownloadUrl(); var findingsJSON = s3Service.downloadFile(downloadUrl); - scanResults = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeboxFindingsJson(findingsJSON); + SecureCodeBoxFindingToDefectDojoMapper findingMapper = new SecureCodeBoxFindingToDefectDojoMapper(); + scanResults = findingMapper.fromSecureCodeboxFindingsJson(findingsJSON); } else { LOG.debug("Explicit Parser is specified for ScanType {}, using Raw Scan Result", scanNameMapping.scanType); downloadUrl = ppConfig.getRawResultDownloadUrl(); diff --git a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingToDefectDojoMapperTest.java similarity index 81% rename from hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java rename to hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingToDefectDojoMapperTest.java index 79726a3f35..64d33c4d50 100644 --- a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java +++ b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingToDefectDojoMapperTest.java @@ -1,10 +1,11 @@ package io.securecodebox.persistence.mapping; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import io.securecodebox.persistence.config.FindingMapperConfig; import io.securecodebox.persistence.models.SecureCodeBoxFinding; import org.json.JSONException; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -14,14 +15,22 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.ZoneId; import java.util.HashMap; +import java.util.TimeZone; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MockitoExtension.class) -public class SecureCodeBoxFindingsToDefectDojoMapperTest { +public class SecureCodeBoxFindingToDefectDojoMapperTest { ClassLoader cl = getClass().getClassLoader(); + SecureCodeBoxFindingToDefectDojoMapper findingMapper; + + public SecureCodeBoxFindingToDefectDojoMapperTest(){ + FindingMapperConfig mapperConfig = new FindingMapperConfig(TimeZone.getTimeZone(ZoneId.of("+0"))); + findingMapper = new SecureCodeBoxFindingToDefectDojoMapper(mapperConfig); + } @Test public void yieldsCorrectResult() throws IOException, JSONException { @@ -29,7 +38,7 @@ public void yieldsCorrectResult() throws IOException, JSONException { String scbFindingsPath = "kubehunter-scb-findings.json"; String expectedDefectDojoFindings = readFileAsString(ddFindingsPath); String scbJsonString = readFileAsString(scbFindingsPath); - String actualDefectDojoFindings = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeboxFindingsJson(scbJsonString); + String actualDefectDojoFindings = findingMapper.fromSecureCodeboxFindingsJson(scbJsonString); JSONAssert.assertEquals(expectedDefectDojoFindings, actualDefectDojoFindings,false); } @@ -50,8 +59,7 @@ public void correctlyParsesFindings() throws IOException { var scbFinding = SecureCodeBoxFinding.builder().name(name).description(description) .severity(SecureCodeBoxFinding.Severities.HIGH).id(id).location(location).attributes(attributes) .parsedAt(parsedAt).build(); - - var ddFinding = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeBoxFinding(scbFinding); + var ddFinding = findingMapper.fromSecureCodeBoxFinding(scbFinding); assertEquals(ddFinding.getTitle(), name); assertEquals(ddFinding.getSeverity(), severity); @@ -79,7 +87,7 @@ public void correctlyParsesFindings() throws IOException { @Test public void doesntThrowUnexpectedExceptionOnEmptyFinding() throws JsonProcessingException { var emptyScbFinding = SecureCodeBoxFinding.builder().build(); - var ddFinding = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeBoxFinding(emptyScbFinding); + var ddFinding = findingMapper.fromSecureCodeBoxFinding(emptyScbFinding); assertNull(ddFinding.getTitle()); assertNull(ddFinding.getDescription()); }