Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hooks/persistence-defectdojo/hook/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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.+"
Expand Down
4 changes: 0 additions & 4 deletions hooks/persistence-defectdojo/hook/lombok.config
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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());
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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.
Expand All @@ -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)
Expand All @@ -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";
}
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -118,15 +126,15 @@ 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));
}

private static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}

return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,45 @@
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;
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.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 {
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 = findingMapper.fromSecureCodeboxFindingsJson(scbJsonString);
JSONAssert.assertEquals(expectedDefectDojoFindings, actualDefectDojoFindings,false);
}

@Test
Expand All @@ -57,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);
Expand Down Expand Up @@ -86,8 +87,14 @@ 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());
}

public String readFileAsString(String fileName) throws IOException
{
Path filePath = Paths.get(cl.getResource(fileName).getPath());
return new String(Files.readAllBytes(filePath));
}
}