8000 Improved Test Structure and Tests in DefectDojo Hook by JohannesZahn · Pull Request #600 · secureCodeBox/secureCodeBox · GitHub
[go: up one dir, main page]

Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import io.securecodebox.persistence.defectdojo.service.EndpointService;
import io.securecodebox.persistence.mapping.DefectDojoFindingToSecureCodeBoxMapper;
import io.securecodebox.persistence.models.Scan;
import io.securecodebox.persistence.service.ScanService;
import io.securecodebox.persistence.service.scanresult.ScanResultService;
import io.securecodebox.persistence.service.KubernetesService;
import io.securecodebox.persistence.service.S3Service;
import io.securecodebox.persistence.strategies.VersionedEngagementsStrategy;
Expand All @@ -33,7 +33,7 @@ public static void main(String[] args) throws Exception {
scan.validate();

LOG.info("Downloading Scan Result");
var scanResultFile = ScanService.downloadScan(scan, persistenceProviderConfig, s3Service);
var scanResultFile = ScanResultService.build(scan, s3Service).getScanResult(persistenceProviderConfig);

var config = DefectDojoConfig.fromEnv();
LOG.info("Uploading Findings to DefectDojo at: {}", config.getUrl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.ZoneId;
import java.util.List;

/**
Expand All @@ -23,6 +24,12 @@ public class PersistenceProviderConfig {
final int RAW_RESULT_UPLOAD_ARG_POSITION = 2;
final int FINDING_UPLOAD_ARG_POSITION = 3;

// DefectDojo does in contrast to secureCodeBox not pay attention to time zones
// to guarantee consistent results when converting back and forth a time zone
// has to be assumed for DefectDojo. It defaults to the Time Zone of the system clock
@Getter
ZoneId defectDojoTimezoneId = ZoneId.systemDefault();

// Download Urls
@Getter
final String rawResultDownloadUrl;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,62 +1,38 @@
package io.securecodebox.persistence.mapping;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
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.PersistenceProviderConfig;
import io.securecodebo F440 x.persistence.models.DefectDojoImportFinding;
import io.securecodebox.persistence.models.SecureCodeBoxFinding;
import io.securecodebox.persistence.service.KubernetesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SecureCodeBoxFindingsToDefectDojoMapper {
private static final Logger LOG = LoggerFactory.getLogger(SecureCodeBoxFindingsToDefectDojoMapper.class);
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final ObjectWriter prettyJSONPrinter = new ObjectMapper().findAndRegisterModules().writerWithDefaultPrettyPrinter();
private static final Logger LOG = LoggerFactory.getLogger(KubernetesService.class);
private final DateTimeFormatter ddDateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final ObjectWriter attributeJsonPrinter = new ObjectMapper().writer(new DefaultPrettyPrinter()
.withObjectIndenter(new DefaultIndenter().withLinefeed("\n")));
private PersistenceProviderConfig ppConfig;

/**
* Converts a SecureCodeBox Findings JSON String to a DefectDojo Findings JSON String.
*
* @param scbFindingsJson SecureCodeBox Findings JSON File as String
* @return DefectDojo Findings JSON File as String, compatible with the DefectDojo Generic JSON Parser
* @throws IOException
*/
public static 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)
.findAndRegisterModules();
List<DefectDojoImportFinding> DefectDojoImportFindings = new ArrayList<>();
List<SecureCodeBoxFinding> secureCodeBoxFindings = mapper.readValue(scbFindingsJson, new TypeReference<>() {
});
for (SecureCodeBoxFinding secureCodeBoxFinding : secureCodeBoxFindings) {
DefectDojoImportFindings.add(fromSecureCodeBoxFinding(secureCodeBoxFinding));
}
// create the result where the format has to be {"findings": [finding1, findings2, ...]}
ObjectNode ddFindingJson = mapper.createObjectNode();
ArrayNode arrayNode = mapper.valueToTree(DefectDojoImportFindings);
ddFindingJson.putArray("findings").addAll(arrayNode);
return ddFindingJson.toString();
public SecureCodeBoxFindingsToDefectDojoMapper(PersistenceProviderConfig ppConfig){
this.ppConfig= ppConfig;
}

protected static String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severities severity) {
protected String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severities severity) {
if (severity == null) {
return "Info";
}

switch (severity) {
case HIGH:
return "High";
Expand All @@ -67,7 +43,6 @@ protected static String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severit
case INFORMATIONAL:
return "Info";
}

return "Info";
}

Expand All @@ -77,9 +52,8 @@ protected static String convertToDefectDojoSeverity(SecureCodeBoxFinding.Severit
*
* @param secureCodeBoxFinding Finding in SecureCodeBox format.
* @return Finding in DefectDojo Format, compatible with the DefectDojo Generic JSON Parser
* @throws JsonProcessingException
*/
protected static DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxFinding secureCodeBoxFinding) throws JsonProcessingException {
public DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxFinding secureCodeBoxFinding){
//set basic Finding info
DefectDojoImportFinding result = new DefectDojoImportFinding();
result.setTitle(secureCodeBoxFinding.getName());
Expand All @@ -88,16 +62,20 @@ protected static DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxF
// set DefectDojo description as combination of SecureCodeBox Finding description and Finding attributes
String description = secureCodeBoxFinding.getDescription();
if (secureCodeBoxFinding.getAttributes() != null) {
String attributesJson = prettyJSONPrinter.writeValueAsString(secureCodeBoxFinding.getAttributes());
description = description + "\n " + attributesJson;
try {
var attributesJson = attributeJsonPrinter.writeValueAsString(secureCodeBoxFinding.getAttributes());
description = description + "\n " + attributesJson;
} catch (JsonProcessingException e) {
LOG.warn("Could not write the secureCodeBox Finding Attributes as JSON: ",e);
}
}
result.setDescription(description);
setFindingDate(secureCodeBoxFinding, result);
setFindingLocation(secureCodeBoxFinding, result);
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,8 +86,8 @@ private static void setFindingLocation(SecureCodeBoxFinding secureCodeBoxFinding
}
}

private static void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) {
Instant instant = null;
private void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) {
Instant instant;
if (secureCodeBoxFinding.getIdentifiedAt() != null && !secureCodeBoxFinding.getIdentifiedAt().isEmpty()) {
instant = Instant.parse(secureCodeBoxFinding.getIdentifiedAt());
} else if (secureCodeBoxFinding.getParsedAt() != null && !secureCodeBoxFinding.getParsedAt().isEmpty()){
Expand All @@ -118,8 +96,8 @ C3E7 @ private static void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, De
else {
instant = Instant.now();
}
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
result.setDate(dtf.format(localDateTime));
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ppConfig.getDefectDojoTimezoneId());
result.setDate(ddDateFormatter.format(localDateTime));
}

private static String capitalize(String str) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.securecodebox.persistence.service.scanresult;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.securecodebox.persistence.config.PersistenceProviderConfig;
import io.securecodebox.persistence.defectdojo.models.ScanFile;
import io.securecodebox.persistence.mapping.SecureCodeBoxFindingsToDefectDojoMapper;
import io.securecodebox.persistence.models.DefectDojoImportFinding;
import io.securecodebox.persistence.models.SecureCodeBoxFinding;
import io.securecodebox.persistence.service.S3Service;
import org.apache.commons.io.FilenameUtils;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
* Responsible for returning scan results that are compatible with the DefectDojo Generic JSON Parser
*/
public class GenericParserScanResultService extends ScanResultService {

private static final ObjectMapper jsonObjectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).findAndRegisterModules();

public GenericParserScanResultService(S3Service s3Service) {
super(s3Service);
}

/**
* Fetches the secureCodeBox Findings.json file and converts it to a json file that is compatible with
* the DefectDojo Generic JSON Parser. This result as a string is then returned together with a filename
* in a ScanFile object. The ending of the filename is essential as it is evaluated by DefectDojo
* @param ppConfig config where the location of the scan result is specified
* @return
* @throws IOException
* @throws InterruptedException
*/
@Override
public ScanFile getScanResult(PersistenceProviderConfig ppConfig) throws IOException, InterruptedException {
LOG.debug("No explicit Parser specified. Using Findings JSON Scan Result");
var scbToDdMapper = new SecureCodeBoxFindingsToDefectDojoMapper(ppConfig);
var downloadUrl = ppConfig.getFindingDownloadUrl();
var findingsJSON = s3Service.downloadFile(downloadUrl);
List<SecureCodeBoxFinding> secureCodeBoxFindings = Arrays.asList(jsonObjectMapper.readValue(findingsJSON, SecureCodeBoxFinding[].class));
List<DefectDojoImportFinding> defectDojoImportFindings = secureCodeBoxFindings.stream().map(scbToDdMapper::fromSecureCodeBoxFinding).collect(Collectors.toList());
// for the generic defectDojo Parser the findings need to be wrapper in a json object called "findings"
var defectDojoFindingJson = Collections.singletonMap("findings",defectDojoImportFindings);
var scanResult = jsonObjectMapper.writeValueAsString(defectDojoFindingJson);
return new ScanFile(scanResult, FilenameUtils.getName(new URL(downloadUrl).getPath()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.securecodebox.persistence.service.scanresult;

import io.securecodebox.persistence.config.PersistenceProviderConfig;
import io.securecodebox.persistence.defectdojo.models.ScanFile;
import io.securecodebox.persistence.models.Scan;
import io.securecodebox.persistence.service.S3Service;
import io.securecodebox.persistence.util.ScanNameMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
* Abstract class that forces children to implement a method to get the scan results from a source
* (e.g. download link) specified in the PersistenceProviderConfig.
* It also builds the correct subclass to use, depending on the Scan (especially it's type)
*/
public abstract class ScanResultService {
protected static final Logger LOG = LoggerFactory.getLogger(ScanResultService.class);
protected S3Service s3Service;
protected ScanResultService(S3Service s3Service) {
this.s3Service = s3Service;
}

public static ScanResultService build(Scan scan, S3Service s3Service){
ScanNameMapping scanNameMapping = ScanNameMapping.bySecureCodeBoxScanType(scan.getSpec().getScanType());
if (scanNameMapping.equals(ScanNameMapping.GENERIC))
return new GenericParserScanResultService(s3Service);
else return new SpecificParserScanResultService(s3Service);
}

public abstract ScanFile getScanResult(PersistenceProviderConfig ppConfig) throws IOException, InterruptedException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.securecodebox.persistence.service.scanresult;

import io.securecodebox.persistence.config.PersistenceProviderConfig;
import io.securecodebox.persistence.defectdojo.models.ScanFile;
import io.securecodebox.persistence.service.S3Service;
import org.apache.commons.io.FilenameUtils;

import java.io.IOException;
import java.net.URL;

/**
* Responsible for returning the raw scan results produced by the secureCodeBox
*/
public class SpecificParserScanResultService extends ScanResultService {

public SpecificParserScanResultService(S3Service s3Service) {
super(s3Service);
}

/**
* Fetches the secureCodeBox raw scan results and returns it together with the filename
* in a ScanFile object. The ending of the filename is essential as it is evaluated by DefectDojo.
* This is usually used when DefectDojo natively supports the scanner that produced the raw result.
* @param ppConfig config where the location of the scan result is specified
* @return
* @throws IOException
* @throws InterruptedException
*/
@Override
public ScanFile getScanResult(PersistenceProviderConfig ppConfig) throws IOException, InterruptedException {
LOG.debug("Explicit Parser is specified. Using Raw Scan Result");
var downloadUrl = ppConfig.getRawResultDownloadUrl();
var scanResult = s3Service.downloadFile(downloadUrl);
return new ScanFile(scanResult, FilenameUtils.getName(new URL(downloadUrl).getPath()));
}
}
Loading
0