1818 */
1919package io .securecodebox .persistence ;
2020
21-
2221import io .securecodebox .persistence .models .*;
22+
2323import org .slf4j .Logger ;
2424import org .slf4j .LoggerFactory ;
2525import org .springframework .beans .factory .annotation .Value ;
4343import java .time .LocalDate ;
4444import java .time .format .DateTimeFormatter ;
4545import java .util .Arrays ;
46+ import java .util .LinkedList ;
47+ import java .util .List ;
4648import java .util .Optional ;
49+ import java .util .Iterator ;
4750
4851@ Component
4952@ ConditionalOnProperty (name = "securecodebox.persistence.defectdojo.enabled" , havingValue = "true" )
@@ -181,8 +184,13 @@ public EngagementResponse createEngagement(EngagementPayload engagementPayload)
181184 throw new DefectDojoPersistenceException ("Failed to create Engagement for SecurityTest" , e );
182185 }
183186 }
184-
185187 public ImportScanResponse createFindings (String rawResult , long engagementId , long lead , String currentDate ,String defectDojoScanName ) {
188+ return createFindings (rawResult , engagementId , lead , currentDate ,defectDojoScanName , "" );
189+ }
190+ /**
191+ * Till version 1.5.4. testName (in defectdojo _test_type_) must be defectDojoScanName, afterwards, you can have somethings else
192+ */
193+ public ImportScanResponse createFindings (String rawResult , long engagementId , long lead , String currentDate ,String defectDojoScanName , String testName ) {
186194 RestTemplate restTemplate = new RestTemplate ();
187195 HttpHeaders headers = getHeaders ();
188196 headers .setContentType (MediaType .MULTIPART_FORM_DATA );
@@ -193,7 +201,12 @@ public ImportScanResponse createFindings(String rawResult, long engagementId, lo
193201 mvn .add ("lead" , Long .toString (lead ));
194202 mvn .add ("scan_date" , currentDate );
195203 mvn .add ("scan_type" , defectDojoScanName );
196-
204+ mvn .add ("close_old_findings" , "true" );
205+ mvn .add ("skip_duplicates" , "false" );
206+
207+ if (!testName .isEmpty ())
208+ mvn .add ("test_type" , testName );
209+
197210 try {
198211 ByteArrayResource contentsAsResource = new ByteArrayResource (rawResult .getBytes (StandardCharsets .UTF_8 )) {
199212 @ Override
@@ -214,10 +227,10 @@ public String getFilename() {
214227 }
215228 }
216229 public ImportScanResponse createFindingsForEngagementName (String engagementName , String rawResults , String defectDojoScanName , long productId , long lead ){
217- return createFindingsForEngagementName (engagementName , rawResults , defectDojoScanName , productId , lead , new EngagementPayload ());
230+ return createFindingsForEngagementName (engagementName , rawResults , defectDojoScanName , productId , lead , new EngagementPayload (), "" );
218231 }
219232
220- public ImportScanResponse createFindingsForEngagementName (String engagementName , String rawResults , String defectDojoScanName , long productId , long lead , EngagementPayload engagementPayload ){
233+ public ImportScanResponse createFindingsForEngagementName (String engagementName , String rawResults , String defectDojoScanName , long productId , long lead , EngagementPayload engagementPayload , String testName ){
221234 Long engagementId = getEngagementIdByEngagementName (engagementName , productId ).orElseGet (() -> {
222235 engagementPayload .setName (engagementName );
223236 engagementPayload .setProduct (productId );
@@ -227,9 +240,28 @@ public ImportScanResponse createFindingsForEngagementName(String engagementName,
227240 return createEngagement (engagementPayload ).getId ();
228241 });
229242
230- return createFindings (rawResults , engagementId , lead , currentDate (), defectDojoScanName );
243+ return createFindings (rawResults , engagementId , lead , currentDate (), defectDojoScanName , testName );
231244 }
232245
246+ public ImportScanResponse createFindingsForEngagementName (String engagementName , String rawResults , String defectDojoScanName , String productName , long lead , EngagementPayload engagementPayload , String testName ){
247+ long productId = 0 ;
248+ try {
249+ productId = retrieveProductId (productName );
250+ } catch (DefectDojoProductNotFound e ) {
251+ LOG .debug ("Given product does not exists" );
252+ }
253+ if (productId == 0 ) {
254+ ProductResponse productResponse = createProduct (productName );
255+ productId = productResponse .getId ();
256+ }
257+
258+ return createFindingsForEngagementName (engagementName , rawResults , defectDojoScanName , productId , lead , engagementPayload , testName );
259+ }
260+
261+ private Optional <Long > getEngagementIdByEngagementName (String engagementName , String productName ){
262+ long productId = retrieveProductId (productName );
263+ return getEngagementIdByEngagementName (engagementName , productId , 0L );
264+ }
233265 private Optional <Long > getEngagementIdByEngagementName (String engagementName , long productId ){
234266 return getEngagementIdByEngagementName (engagementName , productId , 0L );
235267 }
@@ -257,4 +289,136 @@ private Optional<Long> getEngagementIdByEngagementName(String engagementName, lo
257289 LOG .warn ("Engagement with name '{}' not found." , engagementName );
258290 return Optional .empty ();
259291 }
292+ public ProductResponse createProduct (String productName ) {
293+ RestTemplate restTemplate = new RestTemplate ();
294+ ProductPayload productPayload = new ProductPayload (productName , "Description missing" );
295+ HttpEntity <ProductPayload > payload = new HttpEntity <>(productPayload , getHeaders ());
296+
297+ try {
298+ ResponseEntity <ProductResponse > response = restTemplate .exchange (defectDojoUrl + "/api/v2/products/" , HttpMethod .POST , payload , ProductResponse .class );
299+ return response .getBody ();
300+ } catch (HttpClientErrorException e ) {
301+ LOG .warn ("Failed to create product {}" , e );
302+ LOG .warn ("Failure response body. {}" , e .getResponseBodyAsString ());
303+ throw new DefectDojoPersistenceException ("Failed to create product" , e );
304+ }
305+ }
306+
307+ public void deleteUnusedBranches (List <String > existingBranches , String producName ) {
308+ long productId = retrieveProductId (producName );
309+ deleteUnusedBranches (existingBranches , productId );
310+ }
311+
312+ /**
313+ * Deletes engagements based on branch tag
314+ * Be aware that the branch tag MUST be set, otherwise all engagments will be deleted
315+ */
316+ public void deleteUnusedBranches (List <String > existingBranches , long productId ) {
317+ RestTemplate restTemplate = new RestTemplate ();
318+
319+ //get existing branches
320+ List <EngagementResponse > engagementPayloads = getEngagementsForProduct (productId , 0 );
321+ for (EngagementResponse engagementPayload : engagementPayloads ) {
322+ boolean branchExists = false ;
323+ for (String existingBranchName : existingBranches ) {
324+ if (existingBranchName .equals (engagementPayload .getBanch ())) {
325+ branchExists = true ;
326+ break ;
327+ }
328+ }
329+ if (!branchExists ) {
330+ deleteEnageament (engagementPayload .getId ());
331+ LOG .info ("Deleted engagement with id " + engagementPayload .getId () + ", branch " + engagementPayload .getBanch ());
332+ }
333+ }
334+ }
335+
336+ private List <EngagementResponse > getEngagementsForProduct (long productId , long offset ) throws DefectDojoLoopException {
337+ if (offset > 9999 ) {
338+ throw new DefectDojoLoopException ("offset engagement products too much!" );
339+ }
340+ UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl (defectDojoUrl + "/api/v2/engagements" )
341+ .queryParam ("product" , Long .toString (productId ))
342+ .queryParam ("limit" , Long .toString (50L ))
343+ .queryParam ("offset" , Long .toString (offset ));
344+
345+ RestTemplate restTemplate = new RestTemplate ();
346+ HttpEntity engagementRequest = new HttpEntity (getHeaders ());
347+
348+ ResponseEntity <DefectDojoResponse <EngagementResponse >> engagementResponse = restTemplate .exchange (builder .toUriString (), HttpMethod .GET , engagementRequest , new ParameterizedTypeReference <DefectDojoResponse <EngagementResponse >>(){});
349+ List <EngagementResponse > engagementPayloads = new LinkedList <EngagementResponse >();
350+ for (EngagementResponse engagement : engagementResponse .getBody ().getResults ()){
351+ engagementPayloads .add (engagement );
352+ }
353+ if (engagementResponse .getBody ().getNext () != null ){
354+ engagementPayloads .addAll (getEngagementsForProduct (productId , offset + 1 ));;
355+ }
356+ return engagementPayloads ;
357+ }
358+ public void deleteEnageament (long engagementId ){
359+ RestTemplate restTemplate = new RestTemplate ();
360+
361+ String uri = defectDojoUrl + "/api/v2/engagements/" + engagementId + "/?id=" + engagementId ;
362+ HttpEntity request = new HttpEntity (getHeaders ());
363+ try {
364+ ResponseEntity <DefectDojoResponse > response = restTemplate .exchange (uri , HttpMethod .GET , request , DefectDojoResponse .class );
365+ } catch (HttpClientErrorException e ) {
366+ LOG .warn ("Failed to delete engagment {}, engagementId: " + engagementId , e );
367+ LOG .warn ("Failure response body. {}" , e .getResponseBodyAsString ());
368+ throw new DefectDojoPersistenceException ("Failed to delete product" , e );
369+ }
370+ }
371+
372+ /* options is created as follows:
373+ MultiValueMap<String, String> mvn = new LinkedMultiValueMap<>();
374+ mvn.add("engagement", Long.toString(engagementId));
375+ */
376+ private List <Finding > getCurrentFindings (long engagementId , LinkedMultiValueMap <String , String > options ){
377+ RestTemplate restTemplate = new RestTemplate ();
378+
379+ UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl (defectDojoUrl + "/api/v2/findings/" )
380+ .queryParam ("active" , "true" )
381+ .queryParam ("false_p" , "false" )
382+ .queryParam ("duplicate" , "false" )
383+ .queryParam ("test__engagement" , Long .toString (engagementId ));
384+
385+ if (options != null ) {
386+ builder = prepareParameters (options , builder );
387+ }
388+
389+ HttpEntity request = new HttpEntity (getHeaders ());
390+ try {
391+ ResponseEntity <DefectDojoResponse <Finding >> response = restTemplate .exchange (builder .toUriString (), HttpMethod .GET , request , new ParameterizedTypeReference <DefectDojoResponse <Finding >>(){});
392+ List <Finding > findings = new LinkedList <Finding >();
393+ for (Finding finding : response .getBody ().getResults ()){
394+ findings .add (finding );
395+ }
396+ return findings ;
397+ } catch (HttpClientErrorException e ) {
398+ LOG .warn ("Failed to get findings {}, engagementId: " + engagementId , e );
399+ LOG .warn ("Failure response body. {}" , e .getResponseBodyAsString ());
400+ throw new DefectDojoPersistenceException ("Failed to get findings" , e );
401+ }
402+ }
403+ private UriComponentsBuilder prepareParameters (LinkedMultiValueMap <String , String > queryParameters , UriComponentsBuilder builder ) {
404+ Iterator <String > it = queryParameters .keySet ().iterator ();
405+
406+ while (it .hasNext ()){
407+ String theKey = (String )it .next ();
408+ builder .replaceQueryParam (theKey , queryParameters .getFirst (theKey ));
409+ }
410+ return builder ;
411+ }
412+
413+ public List <Finding > receiveNonHandeldFindings (String productName , String engagementName , String minimumServerity , LinkedMultiValueMap <String , String > options ){
414+ Long engagementId = getEngagementIdByEngagementName (engagementName , productName ).orElse (0L );
415+ //getCurrentFindings
416+ List <Finding > findings = new LinkedList <Finding >();
417+ for (String serverity : Finding .getServeritiesAndHigherServerities (minimumServerity )) {
418+ LinkedMultiValueMap <String , String > optionTemp = options .clone ();
419+ optionTemp .add ("serverity" , serverity );
420+ findings .addAll (getCurrentFindings (engagementId , optionTemp ));
421+ }
422+ return findings ;
423+ }
260424}
0 commit comments