Skip to content
This repository was archived by the owner on Feb 26, 2021. It is now read-only.
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 @@ -41,6 +41,7 @@
import java.text.MessageFormat;
import java.time.Clock;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.LinkedList;
Expand All @@ -61,12 +62,16 @@ public class DefectDojoService {
protected String defectDojoDefaultUserName;

protected static final String DATE_FORMAT = "yyyy-MM-dd";
protected static final String DATE_TIME_FORMAT = "yyyy-MM-dd hh:m:ss";

Clock clock = Clock.systemDefaultZone();

private String currentDate() {
return LocalDate.now(clock).format(DateTimeFormatter.ofPattern(DATE_FORMAT));
}
private String currentDateTime() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does DefectDojo now support more detailed dates (with hour and minutes) for the tests?
If i remember correctly we tried to add them like this but it didn't used to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only tests needs to be defined with time. As you mostly create it with an engagement, you didn't had contact so far.

return LocalDateTime.now(clock).format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT));
}

private static final Logger LOG = LoggerFactory.getLogger(DefectDojoService.class);

Expand Down Expand Up @@ -128,6 +133,19 @@ public long retrieveProductId(String product){
throw new DefectDojoProductNotFound(MessageFormat.format("Could not find product: \"{0}\" in DefectDojo", product));
}
}
private long retrieveOrCreateProduct(String productName) {
long productId = 0;
try {
productId = retrieveProductId(productName);
} catch(DefectDojoProductNotFound e) {
LOG.debug("Given product does not exists");
}
if(productId == 0) {
ProductResponse productResponse = createProduct(productName);
productId = productResponse.getId();
}
return productId;
}

public Long retrieveOrCreateToolConfiguration(String toolUrl, String toolType){
if (toolUrl == null){
Expand Down Expand Up @@ -184,13 +202,14 @@ public EngagementResponse createEngagement(EngagementPayload engagementPayload)
throw new DefectDojoPersistenceException("Failed to create Engagement for SecurityTest", e);
}
}
public ImportScanResponse createFindings(String rawResult, long engagementId, long lead, String currentDate,String defectDojoScanName) {
return createFindings(rawResult, engagementId, lead, currentDate,defectDojoScanName, "");

public ImportScanResponse createFindings(String rawResult, long engagementId, long lead, String currentDate, String defectDojoScanName) {
return createFindings(rawResult, engagementId, lead, currentDate,defectDojoScanName, "", new LinkedMultiValueMap<>());
}
/**
* Till version 1.5.4. testName (in defectdojo _test_type_) must be defectDojoScanName, afterwards, you can have somethings else
* Before version 1.5.4. testName (in DefectDojo _test_type_) must be defectDojoScanName, afterwards, you can have somethings else
*/
public ImportScanResponse createFindings(String rawResult, long engagementId, long lead, String currentDate,String defectDojoScanName, String testName) {
public ImportScanResponse createFindings(String rawResult, long engagementId, long lead, String currentDate,String defectDojoScanName, String testName, MultiValueMap<String, Object> options) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = getHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
Expand All @@ -203,9 +222,18 @@ public ImportScanResponse createFindings(String rawResult, long engagementId, lo
mvn.add("scan_type", defectDojoScanName);
mvn.add("close_old_findings", "true");
mvn.add("skip_duplicates", "false");

if(!testName.isEmpty())
mvn.add("test_type", testName);

Iterator<String> it = options.keySet().iterator();
while(it.hasNext()){
String theKey = (String)it.next();
if(mvn.containsKey(theKey)) {
mvn.remove(theKey);
}
}
mvn.addAll(options);

try {
ByteArrayResource contentsAsResource = new ByteArrayResource(rawResult.getBytes(StandardCharsets.UTF_8)) {
Expand All @@ -226,11 +254,123 @@ public String getFilename() {
throw new DefectDojoPersistenceException("Failed to attach findings to engagement.");
}
}
/**
* When DefectDojo >= 1.5.4 is used, testType can be given. Add testName in case DefectDojo >= 1.5.4 is used
*/
private Optional<Long> getTestIdByEngagementName(long engagementId, String testName, long offset) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(defectDojoUrl + "/api/v2/tests")
.queryParam("engagement", Long.toString(engagementId))
.queryParam("limit", Long.toString(50L))
.queryParam("offset", Long.toString(offset));
if(testName!= null) builder.queryParam("testType", testName);

RestTemplate restTemplate = new RestTemplate();
HttpEntity engagementRequest = new HttpEntity(getHeaders());

ResponseEntity<DefectDojoResponse<TestResponse>> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, engagementRequest, new ParameterizedTypeReference<DefectDojoResponse<TestResponse>>(){});

Optional<Long> testResponseId = null;
for(TestResponse test : response.getBody().getResults()){
if(testName == null || test.getTitle().equals(testName)){
testResponseId = Optional.of(test.getId());
}
}
if(testResponseId != null) {
return testResponseId;
}

if(response.getBody().getNext() != null){
return getTestIdByEngagementName(engagementId, testName, offset + 1);
}
LOG.warn("Test with name '{}' not found.", testName);
return Optional.empty();
}
private EngagementResponse createTest(TestPayload testPayload) {
RestTemplate restTemplate = new RestTemplate();

HttpEntity<TestPayload> payload = new HttpEntity<>(testPayload, getHeaders());

try {
ResponseEntity<EngagementResponse> response = restTemplate.exchange(defectDojoUrl + "/api/v2/tests/", HttpMethod.POST, payload, EngagementResponse.class);
return response.getBody();
} catch (HttpClientErrorException e) {
LOG.warn("Failed to create Test for SecurityTest. {}", e);
LOG.warn("Failure response body. {}", e.getResponseBodyAsString());
throw new DefectDojoPersistenceException("Failed to create Test for SecurityTest", e);
}
}
private long getTestIdOrCreate(long engagementId, TestPayload testPayload, String testType) {
Long testId = getTestIdByEngagementName(engagementId, testPayload.getTitle(), 0).orElseGet(() -> {
testPayload.setEngagement(Long.toString(engagementId));
testPayload.setTargetStart(currentDateTime());
testPayload.setTargetEnd(currentDateTime());
testPayload.setTestType(Integer.toString(TestPayload.getTestTypeIdForName(testType)));
return createTest(testPayload).getId();
});
return testId.longValue();
}

public ImportScanResponse createFindingsReImport(String rawResult, String productName, String engagementName, long lead, String currentDate, String defectDojoScanName, EngagementPayload engagementPayload, TestPayload testPayload, MultiValueMap<String, Object> options) {
long productId = retrieveOrCreateProduct(productName);
long engagementId = getEngagementIdByEngagementNameOrCreate(productId, engagementName, engagementPayload, lead);
long testId = getTestIdOrCreate(engagementId, testPayload, defectDojoScanName);
return createFindingsReImport(rawResult, testId, lead, currentDate, defectDojoScanName, options);
}

public ImportScanResponse createFindingsReImport(String rawResult, long testId, long lead, String currentDate,String defectDojoScanName, MultiValueMap<String, Object> options) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = getHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
restTemplate.setMessageConverters(Arrays.asList(new FormHttpMessageConverter(), new ResourceHttpMessageConverter(), new MappingJackson2HttpMessageConverter()));

MultiValueMap<String, Object> mvn = new LinkedMultiValueMap<>();
mvn.add("test", Long.toString(testId));
mvn.add("lead", Long.toString(lead));
mvn.add("scan_date", currentDate);
mvn.add("scan_type", defectDojoScanName);
mvn.add("close_old_findings", "true");
mvn.add("skip_duplicates", "false");

Iterator<String> it = options.keySet().iterator();
while(it.hasNext()){
String theKey = (String)it.next();
if(mvn.containsKey(theKey)) {
mvn.remove(theKey);
}
}
mvn.addAll(options);

try {
ByteArrayResource contentsAsResource = new ByteArrayResource(rawResult.getBytes(StandardCharsets.UTF_8)) {
@Override
public String getFilename() {
return "this_needs_to_be_here_but_doesnt_really_matter.txt";
}
};

mvn.add("file", contentsAsResource);

HttpEntity<MultiValueMap> payload = new HttpEntity<>(mvn, headers);

return restTemplate.exchange(defectDojoUrl + "/api/v2/reimport-scan/", HttpMethod.POST, payload, ImportScanResponse.class).getBody();
} catch (HttpClientErrorException e) {
LOG.warn("Failed to import findings to DefectDojo. Request failed with status code: '{}'.", e.getStatusCode());
LOG.warn("Failure body: {}", e.getResponseBodyAsString());
throw new DefectDojoPersistenceException("Failed to attach findings to engagement.");
}
}


public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead){
return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, new EngagementPayload(), "");
return getEngagementIdByEngagementNameOrCreate(engagementName, rawResults, defectDojoScanName, productId, lead, new EngagementPayload(), "", new LinkedMultiValueMap<>());
}

public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead, EngagementPayload engagementPayload, String testName){
public ImportScanResponse getEngagementIdByEngagementNameOrCreate(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead, EngagementPayload engagementPayload, String testName, MultiValueMap<String, Object> options){
long engagementId = getEngagementIdByEngagementNameOrCreate(productId, engagementName, engagementPayload, lead);

return createFindings(rawResults, engagementId, lead, currentDate(), defectDojoScanName, testName, options);
}
private long getEngagementIdByEngagementNameOrCreate(long productId, String engagementName, EngagementPayload engagementPayload, long lead) {
Long engagementId = getEngagementIdByEngagementName(engagementName, productId).orElseGet(() -> {
engagementPayload.setName(engagementName);
engagementPayload.setProduct(productId);
Expand All @@ -239,23 +379,12 @@ public ImportScanResponse createFindingsForEngagementName(String engagementName,
engagementPayload.setLead(lead);
return createEngagement(engagementPayload).getId();
});

return createFindings(rawResults, engagementId, lead, currentDate(), defectDojoScanName, testName);
return engagementId.longValue();
}

public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, String productName, long lead, EngagementPayload engagementPayload, String testName){
long productId = 0;
try {
productId = retrieveProductId(productName);
} catch(DefectDojoProductNotFound e) {
LOG.debug("Given product does not exists");
}
if(productId == 0) {
ProductResponse productResponse = createProduct(productName);
productId = productResponse.getId();
}
public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, String productName, long lead, EngagementPayload engagementPayload, String testName, MultiValueMap<String, Object> options){
long productId = retrieveOrCreateProduct(productName);

return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, engagementPayload, testName);
return getEngagementIdByEngagementNameOrCreate(engagementName, rawResults, defectDojoScanName, productId, lead, engagementPayload, testName, options);
}

private Optional<Long> getEngagementIdByEngagementName(String engagementName, String productName){
Expand All @@ -267,7 +396,6 @@ private Optional<Long> getEngagementIdByEngagementName(String engagementName, lo
}

private Optional<Long> getEngagementIdByEngagementName(String engagementName, long productId, long offset){

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(defectDojoUrl + "/api/v2/engagements")
.queryParam("product", Long.toString(productId))
.queryParam("limit", Long.toString(50L))
Expand Down Expand Up @@ -314,21 +442,24 @@ public void deleteUnusedBranches(List<String> existingBranches, String producNam
* Be aware that the branch tag MUST be set, otherwise all engagments will be deleted
*/
public void deleteUnusedBranches(List<String> existingBranches, long productId) {
if(existingBranches == null) {
LOG.error("No existing branches given, this will lead to nullpointer");
}
RestTemplate restTemplate = new RestTemplate();

//get existing branches
List<EngagementResponse> engagementPayloads = getEngagementsForProduct(productId, 0);
for(EngagementResponse engagementPayload : engagementPayloads) {
boolean branchExists = false;
for(String existingBranchName : existingBranches) {
if(existingBranchName.equals(engagementPayload.getBanch())) {
if(existingBranchName.equals(engagementPayload.getBranch())) {
branchExists = true;
break;
continue;
}
}
if(!branchExists) {
deleteEnageament(engagementPayload.getId());
LOG.info("Deleted engagement with id " + engagementPayload.getId() + ", branch " + engagementPayload.getBanch());
LOG.info("Deleted engagement with id " + engagementPayload.getId() + ", branch " + engagementPayload.getBranch());
}
}
}
Expand Down Expand Up @@ -361,7 +492,7 @@ public void deleteEnageament(long engagementId){
String uri = defectDojoUrl + "/api/v2/engagements/" + engagementId + "/?id=" + engagementId;
HttpEntity request = new HttpEntity(getHeaders());
try {
ResponseEntity<DefectDojoResponse> response = restTemplate.exchange(uri, HttpMethod.GET, request, DefectDojoResponse.class);
restTemplate.exchange(uri, HttpMethod.DELETE, request, DefectDojoResponse.class);
} catch (HttpClientErrorException e) {
LOG.warn("Failed to delete engagment {}, engagementId: " + engagementId, e);
LOG.warn("Failure response body. {}", e.getResponseBodyAsString());
Expand Down Expand Up @@ -410,15 +541,9 @@ private UriComponentsBuilder prepareParameters(LinkedMultiValueMap<String, Strin
return builder;
}

public List<Finding> receiveNonHandeldFindings(String productName, String engagementName, String minimumServerity, LinkedMultiValueMap<String, String> options){
public List<Finding> receiveNonHandledFindings(String productName, String engagementName, String minimumServerity, LinkedMultiValueMap<String, String> options){
Long engagementId = getEngagementIdByEngagementName(engagementName, productName).orElse(0L);
//getCurrentFindings
List<Finding> findings = new LinkedList<Finding>();
for (String serverity : Finding.getServeritiesAndHigherServerities(minimumServerity)) {
LinkedMultiValueMap<String, String> optionTemp = options.clone();
optionTemp.add("serverity", serverity);
findings.addAll(getCurrentFindings(engagementId, optionTemp));
}
return findings;
options.add("serverity", minimumServerity);
return getCurrentFindings(engagementId, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void setName(String name) {
this.name = name;
}

public String getBanch() {
public String getBranch() {
return branch;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
*
* SecureCodeBox (SCB)
* Copyright 2015-2018 iteratec GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package io.securecodebox.persistence.models;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.LinkedList;
import java.util.List;


@Data
public class TestPayload {
@JsonProperty
protected String title;

@JsonProperty("target_start")
protected String targetStart;

@JsonProperty("target_end")
protected String targetEnd;
@JsonProperty
protected List<String> tags = new LinkedList<>();

@JsonProperty("test_type")
protected String testType;

@JsonProperty
protected String engagement;

/**
* 1 Development
* 3 Production
*/
@JsonProperty
protected String environment = "1";

/**
*
* @return OWASP DefectDojo test type id, -1 in case it is not found
*/
public static int getTestTypeIdForName(String name) {
switch (name) {
case "Dependency Check Scan":
return 18;
default:
return -1;
}
}
}
Loading