From 6a625349c815c9517adb19fecc36c84b2f456fd7 Mon Sep 17 00:00:00 2001 From: Marcel Haag Date: Thu, 8 Dec 2022 12:03:53 +0100 Subject: [PATCH] feat: As a user I want to delete my finding --- .../pentest-findings.component.spec.ts | 3 ++ .../pentest-findings.component.ts | 31 ++++++++++++- .../src/shared/services/pentest.service.ts | 9 ++++ .../security-c4po-api.postman_collection.json | 36 +++++++++++++++ .../src/main/asciidoc/SecurityC4PO.adoc | 22 +++++++++ .../configuration/error/handler/Errorcode.kt | 1 + .../com/securityc4po/api/finding/Finding.kt | 6 +++ .../api/finding/FindingRepository.kt | 4 ++ .../api/finding/FindingService.kt | 28 ++++++++++++ .../api/pentest/PentestController.kt | 14 ++++++ .../api/pentest/PentestService.kt | 34 ++++++++++++++ .../api/project/ProjectRepository.kt | 2 - .../PentestControllerDocumentationTest.kt | 45 +++++++++++++++++-- .../api/pentest/PentestControllerIntTest.kt | 17 +++++++ 14 files changed, 244 insertions(+), 8 deletions(-) diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts index 68d98fe..79c560a 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.spec.ts @@ -20,6 +20,8 @@ import {Category} from '@shared/models/category.model'; import {PentestStatus} from '@shared/models/pentest-status.model'; import {FindingDialogService} from '@shared/modules/finding-dialog/service/finding-dialog.service'; import {FindingDialogServiceMock} from '@shared/modules/finding-dialog/service/finding-dialog.service.mock'; +import {DialogService} from '@shared/services/dialog-service/dialog.service'; +import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock'; const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = { selectedProject: { @@ -77,6 +79,7 @@ describe('PentestFindingsComponent', () => { ], providers: [ {provide: NotificationService, useValue: new NotificationServiceMock()}, + {provide: DialogService, useClass: DialogServiceMock}, {provide: FindingDialogService, useClass: FindingDialogServiceMock}, ] }) diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts index afdc3f6..9ee360e 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.ts @@ -3,7 +3,7 @@ import {PentestService} from '@shared/services/pentest.service'; import {BehaviorSubject, Observable} from 'rxjs'; import {Pentest} from '@shared/models/pentest.model'; import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy'; -import {filter, mergeMap, tap} from 'rxjs/operators'; +import {catchError, filter, mergeMap, switchMap, tap} from 'rxjs/operators'; import {NotificationService, PopupType} from '@shared/services/notification.service'; import { Finding, @@ -21,6 +21,7 @@ import {PentestStatus} from '@shared/models/pentest-status.model'; import {Store} from '@ngxs/store'; import {UpdatePentestFindings} from '@shared/stores/project-state/project-state.actions'; import {ProjectState} from '@shared/stores/project-state/project-state'; +import {DialogService} from '@shared/services/dialog-service/dialog.service'; @UntilDestroy() @Component({ @@ -33,6 +34,7 @@ export class PentestFindingsComponent implements OnInit { constructor(private readonly pentestService: PentestService, private dataSourceBuilder: NbTreeGridDataSourceBuilder, private notificationService: NotificationService, + private dialogService: DialogService, private findingDialogService: FindingDialogService, private store: Store) { this.dataSource = dataSourceBuilder.create(this.data, this.getters); @@ -180,7 +182,32 @@ export class PentestFindingsComponent implements OnInit { } onClickDeleteFinding(findingEntry): void { - console.info('Coming soon..', findingEntry.data.findingId); + const message = { + title: 'finding.delete.title', + key: 'finding.delete.key', + data: {name: findingEntry.data.title}, + }; + this.dialogService.openConfirmDialog( + message + ).onClose.pipe( + filter((confirm) => !!confirm), + switchMap(() => this.pentestService.deleteFindingByPentestAndFindingId( + this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '', + findingEntry.data.findingId) + ), + catchError(() => { + this.notificationService.showPopup('finding.popup.delete.failed', PopupType.FAILURE); + return []; + }), + untilDestroyed(this) + ).subscribe({ + next: () => { + this.loadFindingsData(); + this.notificationService.showPopup('finding.popup.delete.success', PopupType.SUCCESS); + }, error: error => { + console.error(error); + } + }); } isLoading(): Observable { diff --git a/security-c4po-angular/src/shared/services/pentest.service.ts b/security-c4po-angular/src/shared/services/pentest.service.ts index d79bc87..7d45c2f 100644 --- a/security-c4po-angular/src/shared/services/pentest.service.ts +++ b/security-c4po-angular/src/shared/services/pentest.service.ts @@ -113,6 +113,15 @@ export class PentestService { return this.http.patch(`${this.apiBaseURL}/${findingId}/finding`, finding); } + /** + * Delete Finding + * @param pentestId the id of the pentest + * @param findingId the id of the finding + */ + public deleteFindingByPentestAndFindingId(pentestId: string, findingId: string): Observable { + return this.http.delete(`${this.apiBaseURL}/${pentestId}/finding/${findingId}`); + } + /** * Get Comments for Pentest Id * @param pentestId the id of the project diff --git a/security-c4po-api/security-c4po-api.postman_collection.json b/security-c4po-api/security-c4po-api.postman_collection.json index f33af08..344ebe3 100644 --- a/security-c4po-api/security-c4po-api.postman_collection.json +++ b/security-c4po-api/security-c4po-api.postman_collection.json @@ -418,6 +418,42 @@ } }, "response": [] + }, + { + "name": "deleteFinding", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NzA0MTQ3ODYsImlhdCI6MTY3MDQxNDQ4NiwianRpIjoiM2FmOWU5M2MtY2YzNi00MjQwLTkzNWEtNDkxYTJkZTY2MWU4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiI5M2ExNTBlMC03ZWRkLTQxZTgtYWE4Yi0yZWY5YTgzOWU4NDciLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImM0cG9fdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjNHBvX2xvY2FsIjp7InJvbGVzIjpbInVzZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InR0dCIsImdpdmVuX25hbWUiOiJ0ZXN0IiwiZmFtaWx5X25hbWUiOiJ1c2VyIn0.QjUkCInyCJ5Wsz4q56gfsLqERr6pYlGjwNw-VsKNJ_3Jp-8Dazq9UmDGN8AmAkQ0sp0b-FMm3jArKMBpr84gKd65trvQx_qHvXev5x2MWBG4_9v3C9MmjxWcAYRVmfRdURUOhfto-4YfRwMwNRsKJfwMIjfS5VT8bHJWipcCDzaidN8h_LLORbmmQZ2o0l4Jnv5qrrWzUcSTeEeBpHGOjes1-T0gOlDJa34Z9x_xrsTsybKAylrmX03mDSI-f2h5XqqtgnrxtddtHXHatfxB1BHWq-FILDsGf0UG47FEQjqapFvn9bFiNyq0GVrgdK42miEO7ywOtCOKpCfAUnMwdQ", + "type": "string" + }, + { + "key": "undefined", + "type": "any" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:8443/pentests/11601f51-bc17-47fd-847d-0c53df5405b5/finding/cb33fad4-7965-4654-a9f9-f007edaca35c", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8443", + "path": [ + "pentests", + "11601f51-bc17-47fd-847d-0c53df5405b5", + "finding", + "cb33fad4-7965-4654-a9f9-f007edaca35c" + ] + } + }, + "response": [] } ] }, diff --git a/security-c4po-api/src/main/asciidoc/SecurityC4PO.adoc b/security-c4po-api/src/main/asciidoc/SecurityC4PO.adoc index 2b4a567..f24c9d6 100644 --- a/security-c4po-api/src/main/asciidoc/SecurityC4PO.adoc +++ b/security-c4po-api/src/main/asciidoc/SecurityC4PO.adoc @@ -235,11 +235,33 @@ include::{snippets}/updateFindingById/http-response.adoc[] include::{snippets}/updateFindingById/response-fields.adoc[] +=== Delete finding + +To delete a finding, call the PATCH request /pentests/+{pentestId}+/finding/+{findingId}+ + +==== Request example + +include::{snippets}/deleteFindingByPentestAndFindingId/http-request.adoc[] + +==== Request structure + +include::{snippets}/deleteFindingByPentestAndFindingId/path-parameters.adoc[] + +==== Response example + +include::{snippets}/deleteFindingByPentestAndFindingId/http-response.adoc[] + +==== Response structure + +include::{snippets}/deleteFindingByPentestAndFindingId/response-fields.adoc[] + == Change History |=== |Date |Change |2022-12-09 +|Added DELETE endpoint for Finding +|2022-12-08 |Added GET and PATCH endpoint for Finding |2022-12-02 |Added GET and POST endpoint for Findings diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/configuration/error/handler/Errorcode.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/configuration/error/handler/Errorcode.kt index 5cf9844..6833235 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/configuration/error/handler/Errorcode.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/configuration/error/handler/Errorcode.kt @@ -39,4 +39,5 @@ enum class Errorcode(val code: Int) { PentestInsertionFailed(6007), ProjectPentestInsertionFailed(6008), FindingInsertionFailed(6009), + FindingDeletionFailed(6010), } \ No newline at end of file diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/Finding.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/Finding.kt index c940536..268f5f6 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/Finding.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/Finding.kt @@ -52,6 +52,12 @@ fun Finding.toFindingResponseBody(): ResponseBody { ) } +fun Finding.toFindingDeleteResponseBody(): ResponseBody { + return mapOf( + "id" to id + ) +} + /** * Validates if a [FindingRequestBody] is valid * diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingRepository.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingRepository.kt index 33e69a1..c4d5777 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingRepository.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingRepository.kt @@ -1,5 +1,6 @@ package com.securityc4po.api.finding +import org.springframework.data.mongodb.repository.DeleteQuery import org.springframework.data.mongodb.repository.Query import org.springframework.data.mongodb.repository.ReactiveMongoRepository import org.springframework.stereotype.Repository @@ -14,4 +15,7 @@ interface FindingRepository : ReactiveMongoRepository { @Query("{'data._id' :{\$in: ?0 }}") fun findFindingsByIds(id: List): Flux + + @DeleteQuery("{'data._id' : ?0}") + fun deleteFindingById(id: String): Mono } diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingService.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingService.kt index fcc0495..b0fb231 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingService.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/finding/FindingService.kt @@ -124,4 +124,32 @@ class FindingService(private val findingRepository: FindingRepository, private v throw ex } } + + fun deleteFindingByPentestAndFindingId(pentestId: String, findingId: String): Mono { + return findingRepository.findFindingById(findingId).switchIfEmpty { + logger.info("Finding with id $findingId not found. Deletion not necessary.") + Mono.empty() + }.flatMap { findingEntity: FindingEntity -> + val finding = findingEntity.toFinding() + findingRepository.deleteFindingById(findingId).flatMap { + // After successfully deleting finding remove its id from pentest + pentestService.removeFindingFromPentest(pentestId, findingId).onErrorMap { + TransactionInterruptedException( + "Pentest could not be updated in Database.", + Errorcode.PentestInsertionFailed + ) + }.map { + finding + } + }.doOnError { + throw wrappedException( + logging = { logger.warn("Finding could not be deleted from Database. Thrown exception: ", it) }, + mappedException = TransactionInterruptedException( + "Finding could not be deleted.", + Errorcode.FindingDeletionFailed + ) + ) + } + } + } } \ No newline at end of file diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestController.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestController.kt index efaf8d0..e391b06 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestController.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestController.kt @@ -6,11 +6,13 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import com.securityc4po.api.ResponseBody import com.securityc4po.api.finding.FindingRequestBody import com.securityc4po.api.finding.FindingService +import com.securityc4po.api.finding.toFindingDeleteResponseBody import com.securityc4po.api.finding.toFindingResponseBody import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity.noContent import org.springframework.web.bind.annotation.* import reactor.core.publisher.Mono +import reactor.kotlin.core.publisher.switchIfEmpty @RestController @RequestMapping("/pentests") @@ -108,4 +110,16 @@ class PentestController(private val pentestService: PentestService, private val ResponseEntity.accepted().body(it.toFindingResponseBody()) } } + + @DeleteMapping("/{pentestId}/finding/{findingId}") + fun deleteFinding( + @PathVariable(value = "pentestId") pentestId: String, + @PathVariable(value = "findingId") findingId: String + ): Mono> { + return this.findingService.deleteFindingByPentestAndFindingId(pentestId, findingId).map { + ResponseEntity.ok().body(it.toFindingDeleteResponseBody()) + }.switchIfEmpty { + Mono.just(ResponseEntity.noContent().build()) + } + } } \ No newline at end of file diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestService.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestService.kt index c4f3c08..b3f97e7 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestService.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestService.kt @@ -154,6 +154,40 @@ class PentestService(private val pentestRepository: PentestRepository, private v } } + /** + * Update [Pentest] for Finding + * + * @throws [InvalidModelException] if the [Pentest] is invalid + * @throws [TransactionInterruptedException] if the [Pentest] could not be updated + * @return updated [Pentest] + */ + fun removeFindingFromPentest(pentestId: String, findingId: String): Mono { + return pentestRepository.findPentestById(pentestId).switchIfEmpty { + logger.warn("Pentest with id $pentestId not found. Updating not possible.") + val msg = "Pentest with id $pentestId not found." + val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound) + throw ex + }.flatMap { currentPentestEntity: PentestEntity -> + if (currentPentestEntity.data.findingIds.find { pentestData -> pentestData == findingId } != null) { + val findingIds = currentPentestEntity.data.findingIds.toMutableList() + findingIds.remove(findingId) + currentPentestEntity.data.findingIds = findingIds.toList() + } + currentPentestEntity.lastModified = Instant.now() + this.pentestRepository.save(currentPentestEntity).map { + it.toPentest() + }.doOnError { + throw wrappedException( + logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) }, + mappedException = TransactionInterruptedException( + "Pentest could not be updated.", + Errorcode.PentestInsertionFailed + ) + ) + } + } + } + /** * Get all [Finding]Id's by pentestId * diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectRepository.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectRepository.kt index ae1b7e4..b896237 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectRepository.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectRepository.kt @@ -14,6 +14,4 @@ interface ProjectRepository: ReactiveMongoRepository { @DeleteQuery("{'data._id' : ?0}") fun deleteProjectById(id: String): Mono - - } \ No newline at end of file diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt index 63df7fb..a92db86 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerDocumentationTest.kt @@ -261,7 +261,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() { ), PayloadDocumentation.relaxedResponseFields( PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING) - .description("The id of the requested pentest"), + .description("The id of the requested findings"), PayloadDocumentation.fieldWithPath("[].severity").type(JsonFieldType.STRING) .description("The severity of the finding"), PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING) @@ -325,7 +325,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() { ), PayloadDocumentation.relaxedResponseFields( PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING) - .description("The id of the requested pentest"), + .description("The id of the requested finding"), PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING) .description("The severity of the finding"), PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) @@ -386,7 +386,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() { ), PayloadDocumentation.relaxedResponseFields( PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING) - .description("The id of the requested pentest"), + .description("The id of the saved finding"), PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING) .description("The severity of the finding"), PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) @@ -446,7 +446,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() { ), PayloadDocumentation.relaxedResponseFields( PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING) - .description("The id of the requested pentest"), + .description("The id of the updated finding"), PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING) .description("The severity of the finding"), PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) @@ -477,6 +477,43 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() { ) } + @Nested + inner class DeleteFinding { + @Test + fun deleteFindingByPentestAndFindingId() { + val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002" + val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f" + webTestClient.delete() + .uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId) + .header("Authorization", "Bearer $tokenAdmin") + .exchange() + .expectStatus().isOk + .expectHeader().doesNotExist("") + .expectBody() + .consumeWith( + WebTestClientRestDocumentation.document( + "{methodName}", + Preprocessors.preprocessRequest( + Preprocessors.prettyPrint(), + Preprocessors.modifyUris().removePort(), + Preprocessors.removeHeaders("Host", "Content-Length") + ), + Preprocessors.preprocessResponse( + Preprocessors.prettyPrint() + ), + RequestDocumentation.relaxedPathParameters( + RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to remove the finidng from"), + RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to delete") + ), + PayloadDocumentation.relaxedResponseFields( + PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING) + .description("The id of the finding you deleted") + ) + ) + ) + } + } + private fun persistBasicTestScenario() { // setup test data // Project diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt index 80e349a..87ac6d3 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/PentestControllerIntTest.kt @@ -281,6 +281,23 @@ class PentestControllerIntTest : BaseIntTest() { ) } + @Nested + inner class DeleteFinding { + @Test + fun `delete finding successfully`() { + val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002" + val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f" + webTestClient.delete() + .uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId) + .header("Authorization", "Bearer $tokenAdmin") + .exchange() + .expectStatus().isOk + .expectHeader().valueEquals("Application-Name", "SecurityC4PO") + .expectBody() + .jsonPath("$.id").isEqualTo("ab62d365-1b1d-4da1-89bc-5496616e220f") + } + } + private fun persistBasicTestScenario() { // setup test data // project