From e2b4e44af552d6c254cc98d81435bf520714fbf8 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 6 Aug 2019 16:37:30 +0200 Subject: [PATCH 01/10] Remove default tags from api model --- .../persistence/DefectDojoPersistenceProvider.java | 3 +++ .../io/securecodebox/persistence/models/EngagementPayload.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoPersistenceProvider.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoPersistenceProvider.java index f9595cf4..23e9b653 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoPersistenceProvider.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoPersistenceProvider.java @@ -197,6 +197,9 @@ private EngagementResponse createEngagement(SecurityTest securityTest) { engagementPayload.setTargetEnd(currentDate()); engagementPayload.setStatus(EngagementPayload.Status.COMPLETED); + engagementPayload.getTags().add("secureCodeBox"); + engagementPayload.getTags().add("automated"); + return defectDojoService.createEngagement(engagementPayload); } diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java index 2bf1283f..54a84316 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java @@ -22,6 +22,7 @@ import lombok.Data; import java.util.Arrays; +import java.util.Collections; import java.util.List; @@ -49,7 +50,7 @@ public class EngagementPayload { protected Status status = Status.IN_PROGRESS; @JsonProperty - protected List tags = Arrays.asList("secureCodeBox", "automated"); + protected List tags = Collections.emptyList(); @JsonProperty protected String tracker; From 1794823e7534b08845679fb5fd8edc3e9ac06898 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 6 Aug 2019 16:37:51 +0200 Subject: [PATCH 02/10] Add name to to api response --- .../persistence/models/EngagementResponse.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java index b4bd7911..40d0797a 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java @@ -24,6 +24,9 @@ public class EngagementResponse { @JsonProperty protected long id; + @JsonProperty + protected String name; + public long getId() { return id; } @@ -31,4 +34,12 @@ public long getId() { public void setId(long id) { this.id = id; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } From 27e63c627822b74e072c44bf95b8f9c466eba5ed Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 6 Aug 2019 16:38:25 +0200 Subject: [PATCH 03/10] Add method to add defectdojo engagments with the persistenceprovide lib without the scb --- .../persistence/DefectDojoService.java | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java index 05e8be9b..bcd90f3d 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java @@ -35,10 +35,15 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; import java.nio.charset.StandardCharsets; import java.text.MessageFormat; +import java.time.Clock; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Arrays; +import java.util.Optional; @Component @ConditionalOnProperty(name = "securecodebox.persistence.defectdojo.enabled", havingValue = "true") @@ -52,8 +57,15 @@ public class DefectDojoService { @Value("${securecodebox.persistence.defectdojo.auth.name}") protected String defectDojoDefaultUserName; - private static final Logger LOG = LoggerFactory.getLogger(DefectDojoService.class); + protected static final String DATE_FORMAT = "yyyy-MM-dd"; + + Clock clock = Clock.systemDefaultZone(); + private String currentDate() { + return LocalDate.now(clock).format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + } + + private static final Logger LOG = LoggerFactory.getLogger(DefectDojoService.class); private HttpHeaders getHeaders(){ HttpHeaders headers = new HttpHeaders(); @@ -201,5 +213,67 @@ public String getFilename() { throw new DefectDojoPersistenceException("Failed to attach findings to engagement."); } } + public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId){ + createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, new EngagementPayload()); + } + + public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, EngagementPayload engagementPayload){ + Long engagementId = getEngagementIdByEngagementName(engagementName, productId).orElseGet(() -> { + engagementPayload.setName(engagementName); + engagementPayload.setProduct(productId); + engagementPayload.setTargetStart(currentDate()); + engagementPayload.setTargetEnd(currentDate()); + return createEngagement(engagementPayload).getId(); + }); + + return createFindings(rawResults, engagementId, lead, currentDate(), defectDojoScanName); + } + + private Optional getEngagementIdByEngagementName(String engagementName, long productId){ + return getEngagementIdByEngagementName(engagementName, productId, 0L); + } + + private Optional 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)) + .queryParam("offset", Long.toString(offset)); + + RestTemplate restTemplate = new RestTemplate(); + HttpEntity engagementRequest = new HttpEntity(getHeaders()); + + ResponseEntity> engagementResponse = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, engagementRequest, new ParameterizedTypeReference>(){}); + + for(EngagementResponse engagement : engagementResponse.getBody().getResults()){ + if(engagement.getName().equals(engagementName)){ + return Optional.of(engagement.getId()); + } + } + if(engagementResponse.getBody().getNext() != null){ + return getEngagementIdByEngagementName(engagementName, productId, offset + 1); + } + LOG.warn("Engagement with name '{}' not found.", engagementName); + return Optional.empty(); + } + + public static void main(String[] args) { + DefectDojoService dd = new DefectDojoService(); + + dd.defectDojoUrl = "http://defectdojo.default.minikube.local:8080"; + dd.defectDojoDefaultUserName = "admin"; + dd.defectDojoApiKey = "..."; + + EngagementPayload engagement = new EngagementPayload(); + engagement.setBranch("fix/stuff"); + + dd.createFindingsForEngagementName( + "NMAP fix/stuff", + "...", + "Nmap Scan", + 1, + engagement + ); + } } From ffddc7bc2ed6fbf222931b5a294da3112a4c52d0 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 6 Aug 2019 16:56:33 +0200 Subject: [PATCH 04/10] Re add lead --- .../persistence/DefectDojoService.java | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java index bcd90f3d..14b966e1 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java @@ -213,16 +213,17 @@ public String getFilename() { throw new DefectDojoPersistenceException("Failed to attach findings to engagement."); } } - public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId){ - createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, new EngagementPayload()); + public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead){ + return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, new EngagementPayload()); } - public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, EngagementPayload engagementPayload){ + public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead, EngagementPayload engagementPayload){ Long engagementId = getEngagementIdByEngagementName(engagementName, productId).orElseGet(() -> { engagementPayload.setName(engagementName); engagementPayload.setProduct(productId); engagementPayload.setTargetStart(currentDate()); engagementPayload.setTargetEnd(currentDate()); + engagementPayload.setLead(lead); return createEngagement(engagementPayload).getId(); }); @@ -256,24 +257,4 @@ private Optional getEngagementIdByEngagementName(String engagementName, lo LOG.warn("Engagement with name '{}' not found.", engagementName); return Optional.empty(); } - - public static void main(String[] args) { - DefectDojoService dd = new DefectDojoService(); - - dd.defectDojoUrl = "http://defectdojo.default.minikube.local:8080"; - dd.defectDojoDefaultUserName = "admin"; - dd.defectDojoApiKey = "..."; - - EngagementPayload engagement = new EngagementPayload(); - engagement.setBranch("fix/stuff"); - - dd.createFindingsForEngagementName( - "NMAP fix/stuff", - "...", - "Nmap Scan", - 1, - engagement - ); - } - } From df69c78a465133229aeadd2a847c0d2a16f0085b Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 6 Aug 2019 17:23:15 +0200 Subject: [PATCH 05/10] Use empty linked list --- .../io/securecodebox/persistence/models/EngagementPayload.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java index 54a84316..392939bb 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.LinkedList; import java.util.List; @@ -50,7 +51,7 @@ public class EngagementPayload { protected Status status = Status.IN_PROGRESS; @JsonProperty - protected List tags = Collections.emptyList(); + protected List tags = new LinkedList<>(); @JsonProperty protected String tracker; From e811d9ce2b3567c38269ae6ac5f5878b9f8ab092 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 6 Aug 2019 18:11:07 +0200 Subject: [PATCH 06/10] Add missing tags to test --- .../persistence/DefectDojoPersistenceProviderTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java index da045633..49aa94de 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java @@ -140,6 +140,8 @@ public void createsTheEngagement(){ payload.setBuildServer(5l); payload.setScmServer(7l); payload.setOrchestrationEngine(9l); + payload.getTags().add("secureCodeBox"); + payload.getTags().add("automated"); persistenceProvider.persist(securityTest); From 63a425516e511f08396bc4a9b598fc9718a84c1d Mon Sep 17 00:00:00 2001 From: Timo Pagel Date: Sat, 10 Aug 2019 16:57:29 +0200 Subject: [PATCH 07/10] Add createProduct, addEngagmgent --- .../persistence/DefectDojoLoopException.java | 29 +++++ .../persistence/DefectDojoService.java | 117 +++++++++++++++++- .../persistence/models/DefectDojoProduct.java | 7 ++ .../persistence/models/EngagementPayload.java | 5 +- .../models/EngagementResponse.java | 11 ++ .../persistence/models/ProductPayload.java | 34 +++++ .../persistence/models/ProductResponse.java | 45 +++++++ .../DefectDojoPersistenceProviderTest.java | 9 ++ 8 files changed, 250 insertions(+), 7 deletions(-) create mode 100644 scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoLoopException.java create mode 100644 scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductPayload.java create mode 100644 scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductResponse.java diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoLoopException.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoLoopException.java new file mode 100644 index 00000000..6ab330e2 --- /dev/null +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoLoopException.java @@ -0,0 +1,29 @@ +/* + * + * 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; + +public class DefectDojoLoopException extends RuntimeException{ + public DefectDojoLoopException(String message) { + super(message); + } + + public DefectDojoLoopException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java index 14b966e1..44517640 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java @@ -18,7 +18,6 @@ */ package io.securecodebox.persistence; - import io.securecodebox.persistence.models.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,6 +42,8 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; import java.util.Optional; @Component @@ -181,8 +182,13 @@ 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, ""); + } + /** + * Till 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) { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = getHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); @@ -193,7 +199,12 @@ public ImportScanResponse createFindings(String rawResult, long engagementId, lo 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"); + + if(!testName.isEmpty()) + mvn.add("test_type", testName); + try { ByteArrayResource contentsAsResource = new ByteArrayResource(rawResult.getBytes(StandardCharsets.UTF_8)) { @Override @@ -214,10 +225,10 @@ public String getFilename() { } } public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead){ - return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, new EngagementPayload()); + return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, new EngagementPayload(), ""); } - public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead, EngagementPayload engagementPayload){ + public ImportScanResponse createFindingsForEngagementName(String engagementName, String rawResults, String defectDojoScanName, long productId, long lead, EngagementPayload engagementPayload, String testName){ Long engagementId = getEngagementIdByEngagementName(engagementName, productId).orElseGet(() -> { engagementPayload.setName(engagementName); engagementPayload.setProduct(productId); @@ -227,7 +238,22 @@ public ImportScanResponse createFindingsForEngagementName(String engagementName, return createEngagement(engagementPayload).getId(); }); - return createFindings(rawResults, engagementId, lead, currentDate(), defectDojoScanName); + return createFindings(rawResults, engagementId, lead, currentDate(), defectDojoScanName, testName); + } + + 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(); + } + + return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, engagementPayload, testName); } private Optional getEngagementIdByEngagementName(String engagementName, long productId){ @@ -257,4 +283,83 @@ private Optional getEngagementIdByEngagementName(String engagementName, lo LOG.warn("Engagement with name '{}' not found.", engagementName); return Optional.empty(); } + public ProductResponse createProduct(String productName) { + RestTemplate restTemplate = new RestTemplate(); + ProductPayload productPayload = new ProductPayload(productName, "Description missing"); + HttpEntity payload = new HttpEntity<>(productPayload, getHeaders()); + + try { + ResponseEntity response = restTemplate.exchange(defectDojoUrl + "/api/v2/products/", HttpMethod.POST, payload, ProductResponse.class); + return response.getBody(); + } catch (HttpClientErrorException e) { + LOG.warn("Failed to create product {}", e); + LOG.warn("Failure response body. {}", e.getResponseBodyAsString()); + throw new DefectDojoPersistenceException("Failed to create product", e); + } + } + + public void deleteUnusedBranches(List existingBranches, String producName) { + long productId = retrieveProductId(producName); + deleteUnusedBranches(existingBranches, productId); + } + + /** + * Deletes engagements based on branch tag + * Be aware that the branch tag MUST be set, otherwise all engagments will be deleted + */ + public void deleteUnusedBranches(List existingBranches, long productId) { + RestTemplate restTemplate = new RestTemplate(); + + //get existing branches + List engagementPayloads = getEngagementsForProduct(productId, 0); + for(EngagementResponse engagementPayload : engagementPayloads) { + boolean branchExists = false; + for(String existingBranchName : existingBranches) { + if(existingBranchName.equals(engagementPayload.getBanch())) { + branchExists = true; + break; + } + } + if(!branchExists) { + deleteEnageament(engagementPayload.getId()); + LOG.info("Deleted engagement with id " + engagementPayload.getId() + ", branch " + engagementPayload.getBanch()); + } + } + } + + private List getEngagementsForProduct(long productId, long offset) throws DefectDojoLoopException{ + if(offset > 9999) { + throw new DefectDojoLoopException("offset engagement products too much!"); + } + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(defectDojoUrl + "/api/v2/engagements") + .queryParam("product", Long.toString(productId)) + .queryParam("limit", Long.toString(50L)) + .queryParam("offset", Long.toString(offset)); + + RestTemplate restTemplate = new RestTemplate(); + HttpEntity engagementRequest = new HttpEntity(getHeaders()); + + ResponseEntity> engagementResponse = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, engagementRequest, new ParameterizedTypeReference>(){}); + List engagementPayloads = new LinkedList(); + for(EngagementResponse engagement : engagementResponse.getBody().getResults()){ + engagementPayloads.add(engagement); + } + if(engagementResponse.getBody().getNext() != null){ + engagementPayloads.addAll(getEngagementsForProduct(productId, offset + 1));; + } + return engagementPayloads; + } + public void deleteEnageament(long engagementId){ + RestTemplate restTemplate = new RestTemplate(); + + String uri = defectDojoUrl + "/api/v2/engagements/" + engagementId + "/?id=" + engagementId; + HttpEntity request = new HttpEntity(getHeaders()); + try { + ResponseEntity response = 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()); + throw new DefectDojoPersistenceException("Failed to delete product", e); + } + } } diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/DefectDojoProduct.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/DefectDojoProduct.java index ab20285a..6e5a3f04 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/DefectDojoProduct.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/DefectDojoProduct.java @@ -21,4 +21,11 @@ public class DefectDojoProduct { @JsonProperty("authorized_users") List authorizedUsers; + + public DefectDojoProduct() {} + + public DefectDojoProduct(String productName, String productDescription) { + name = productName; + description = productDescription; + } } diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java index 392939bb..25b2dc67 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementPayload.java @@ -63,7 +63,7 @@ public class EngagementPayload { protected String commitHash; @JsonProperty("branch_tag") - protected String branch; + public String branch; @JsonProperty("source_code_management_uri") protected String repo; @@ -80,6 +80,9 @@ public class EngagementPayload { @JsonProperty protected String description; + @JsonProperty("deduplication_on_engagement") + protected boolean deduplicationOnEngagement; + /** * Currently only contains the statuses relevant to us. * If you need others, feel free to add them ;) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java index 40d0797a..f221ae63 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/EngagementResponse.java @@ -27,6 +27,9 @@ public class EngagementResponse { @JsonProperty protected String name; + @JsonProperty("branch_tag") + protected String branch; + public long getId() { return id; } @@ -42,4 +45,12 @@ public String getName() { public void setName(String name) { this.name = name; } + + public String getBanch() { + return branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } } diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductPayload.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductPayload.java new file mode 100644 index 00000000..b7dfee33 --- /dev/null +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductPayload.java @@ -0,0 +1,34 @@ +/* +* 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 java.util.List; +import lombok.Data; + +@Data +public class ProductPayload { + @JsonProperty + String name; + + @JsonProperty + String description; + + public ProductPayload(String productName, String productDescription) { + name = productName; + description = productDescription; + } +} diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductResponse.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductResponse.java new file mode 100644 index 00000000..70ae774c --- /dev/null +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/ProductResponse.java @@ -0,0 +1,45 @@ +/* + * + * 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; + +public class ProductResponse { + @JsonProperty + protected long id; + + @JsonProperty + protected String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java index 49aa94de..1c0bac60 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/test/java/io/securecodebox/persistence/DefectDojoPersistenceProviderTest.java @@ -233,4 +233,13 @@ public void createsFindingsForNonSupportedScanner() { eq("Generic Findings Import") ); } + + @Test + public void createProduct() { + String productName = "mytestproduct"; + defectDojoService.createProduct(productName); + verify(defectDojoService, times(1)).createProduct( + eq(productName) + ); + } } From 6aeec1fda42c0372f2132f28d3cb3cbd9cdbae1a Mon Sep 17 00:00:00 2001 From: Timo Pagel Date: Sun, 11 Aug 2019 09:34:24 +0200 Subject: [PATCH 08/10] Add vuln handling --- .../persistence/DefectDojoService.java | 64 +++++++++++++- .../persistence/models/Finding.java | 85 +++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/Finding.java diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java index 44517640..7a9b523a 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java @@ -19,6 +19,8 @@ package io.securecodebox.persistence; import io.securecodebox.persistence.models.*; + +import org.camunda.bpm.model.bpmn.instance.ReceiveTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -45,6 +47,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; +import java.util.Iterator; @Component @ConditionalOnProperty(name = "securecodebox.persistence.defectdojo.enabled", havingValue = "true") @@ -68,6 +71,8 @@ private String currentDate() { private static final Logger LOG = LoggerFactory.getLogger(DefectDojoService.class); + private LinkedMultiValueMap options; + private HttpHeaders getHeaders(){ HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Token " + defectDojoApiKey); @@ -256,6 +261,10 @@ public ImportScanResponse createFindingsForEngagementName(String engagementName, return createFindingsForEngagementName(engagementName, rawResults, defectDojoScanName, productId, lead, engagementPayload, testName); } + private Optional getEngagementIdByEngagementName(String engagementName, String productName){ + long productId = retrieveProductId(productName); + return getEngagementIdByEngagementName(engagementName, productId, 0L); + } private Optional getEngagementIdByEngagementName(String engagementName, long productId){ return getEngagementIdByEngagementName(engagementName, productId, 0L); } @@ -355,11 +364,64 @@ public void deleteEnageament(long engagementId){ String uri = defectDojoUrl + "/api/v2/engagements/" + engagementId + "/?id=" + engagementId; HttpEntity request = new HttpEntity(getHeaders()); try { - ResponseEntity response = restTemplate.exchange(uri, HttpMethod.DELETE, request, DefectDojoResponse.class); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, request, DefectDojoResponse.class); } catch (HttpClientErrorException e) { LOG.warn("Failed to delete engagment {}, engagementId: " + engagementId, e); LOG.warn("Failure response body. {}", e.getResponseBodyAsString()); throw new DefectDojoPersistenceException("Failed to delete product", e); } + } + + /* options is created as follows: + MultiValueMap mvn = new LinkedMultiValueMap<>(); + mvn.add("engagement", Long.toString(engagementId)); + */ + private List getCurrentFindings(long engagementId, LinkedMultiValueMap options){ + RestTemplate restTemplate = new RestTemplate(); + + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(defectDojoUrl + "/api/v2/findings/") + .queryParam("active", "true") + .queryParam("false_p", "false") + .queryParam("duplicate", "false") + .queryParam("test__engagement", Long.toString(engagementId)); + + if(options != null) { + builder = prepareParameters(options, builder); + } + + HttpEntity request = new HttpEntity(getHeaders()); + try { + ResponseEntity> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, request, new ParameterizedTypeReference>(){}); + List findings = new LinkedList(); + for(Finding finding : response.getBody().getResults()){ + findings.add(finding); + } + return findings; + } catch (HttpClientErrorException e) { + LOG.warn("Failed to get findings {}, engagementId: " + engagementId, e); + LOG.warn("Failure response body. {}", e.getResponseBodyAsString()); + throw new DefectDojoPersistenceException("Failed to get findings", e); + } + } + private UriComponentsBuilder prepareParameters(LinkedMultiValueMap queryParameters, UriComponentsBuilder builder) { + Iterator it = queryParameters.keySet().iterator(); + + while(it.hasNext()){ + String theKey = (String)it.next(); + builder.replaceQueryParam(theKey, queryParameters.getFirst(theKey)); + } + return builder; } + + public List receiveNonHandeldFindings(String productName, String engagementName, String minimumServerity, LinkedMultiValueMap options){ + Long engagementId = getEngagementIdByEngagementName(engagementName, productName).orElse(0L); + //getCurrentFindings + List findings = new LinkedList(); + for (String serverity : Finding.getServeritiesAndHigherServerities(minimumServerity)) { + LinkedMultiValueMap optionTemp = options.clone(); + optionTemp.add("serverity", serverity); + findings.addAll(getCurrentFindings(engagementId, optionTemp)); + } + return findings; + } } diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/Finding.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/Finding.java new file mode 100644 index 00000000..8b35a728 --- /dev/null +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/models/Finding.java @@ -0,0 +1,85 @@ +/* + * + * SecureCodeBox (SCB) + * + * 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.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + + +@Data +public class Finding { + @JsonProperty + protected long id; + + @JsonProperty + protected String title; + + @JsonProperty + protected long cwe; + + @JsonProperty + protected String cve; + @JsonProperty + + protected String severity; + + @JsonProperty + protected String description; + + @JsonProperty + protected boolean active = true; + + @JsonProperty + protected boolean verified = true; + + @JsonProperty("false_p") + protected boolean falsePostive = false; + + @JsonProperty + protected boolean duplicate = false; + + @JsonProperty("is_Mitigated") + protected boolean isMitigated = false; + + enum FindingSeverities { + + } + public static final LinkedList findingServerities = new LinkedList(){{ + add("Low"); + add("Medium"); + add("High"); + add("Critical"); + }}; + public static LinkedList getServeritiesAndHigherServerities(String minimumServerity){ + LinkedList severities = new LinkedList(); + boolean minimumFound = false; + for(String serverity : findingServerities) { + if(minimumFound || minimumServerity.equals(serverity)) { + minimumFound = true; + severities.add(serverity); + } + } + + return severities; + } +} \ No newline at end of file From ef7b1452f318b65b010c446ace983f6ba065424d Mon Sep 17 00:00:00 2001 From: Timo Pagel Date: Sun, 11 Aug 2019 09:36:02 +0200 Subject: [PATCH 09/10] Cleanup --- .../java/io/securecodebox/persistence/DefectDojoService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java index 7a9b523a..1033a78d 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java @@ -20,7 +20,6 @@ import io.securecodebox.persistence.models.*; -import org.camunda.bpm.model.bpmn.instance.ReceiveTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; From 537d66922705774f434afc982ad1ef20b701307a Mon Sep 17 00:00:00 2001 From: Timo Pagel Date: Sun, 11 Aug 2019 09:37:24 +0200 Subject: [PATCH 10/10] Cleanup --- .../java/io/securecodebox/persistence/DefectDojoService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java index 1033a78d..55ffda9b 100644 --- a/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java +++ b/scb-persistenceproviders/defectdojo-persistenceprovider/src/main/java/io/securecodebox/persistence/DefectDojoService.java @@ -70,8 +70,6 @@ private String currentDate() { private static final Logger LOG = LoggerFactory.getLogger(DefectDojoService.class); - private LinkedMultiValueMap options; - private HttpHeaders getHeaders(){ HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Token " + defectDojoApiKey);