From e9aec4ec3e86a4e10fd6035f67bec8300150386c Mon Sep 17 00:00:00 2001 From: Marcel Haag Date: Mon, 14 Nov 2022 16:12:21 +0100 Subject: [PATCH] feat: As a developer, I want to get findings by pentestId --- .../pentest-content.component.ts | 2 +- .../pentest-findings.component.html | 2 +- .../pentest-findings.component.scss | 9 +++++ .../pentest-findings.component.spec.ts | 1 + .../pentest-findings.component.ts | 3 +- .../src/shared/models/finding.model.ts | 6 +-- .../finding-dialog.component.html | 1 + .../finding-dialog.component.ts | 2 +- .../src/shared/services/pentest.service.ts | 2 - .../security-c4po-api.postman_collection.json | 37 ++++++++++++++++++- .../configuration/error/handler/Errorcode.kt | 2 + .../com/securityc4po/api/finding/Finding.kt | 1 + .../api/finding/FindingRepository.kt | 4 ++ .../api/finding/FindingService.kt | 31 ++++++++++++++++ .../api/pentest/PentestController.kt | 15 +++++++- .../api/pentest/PentestService.kt | 21 ++++++++++- .../api/project/ProjectService.kt | 2 +- 17 files changed, 127 insertions(+), 14 deletions(-) diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts index 307ba0a..fb98cac 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-content.component.ts @@ -64,7 +64,7 @@ export class PentestContentComponent implements OnInit { } }); - this.store.selectOnce(ProjectState.pentest).pipe( + this.store.select(ProjectState.pentest).pipe( untilDestroyed(this) ).subscribe({ next: (selectedPentest: Pentest) => { diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html index 2a10991..72d7498 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.html @@ -19,7 +19,7 @@ {{ 'finding.severity' | translate }} - + diff --git a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss index c4ec461..51ebbfb 100644 --- a/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss +++ b/security-c4po-angular/src/app/pentest/pentest-content/pentest-findings/pentest-findings.component.scss @@ -6,6 +6,8 @@ .finding-cell { // Add style here + height: 4.5rem !important; + max-height: 4.5rem !important; } .finding-cell:hover { @@ -16,6 +18,13 @@ .cell-severity { width: 125px; max-width: 125px; + // border-style: none; + height: 4.5rem !important; + } + + .border-style { + border-top-style: none; + border-left-style: none; } .cell-actions { 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 b538936..68d98fe 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 @@ -91,6 +91,7 @@ describe('PentestFindingsComponent', () => { [PROJECT_STATE_NAME]: DESIRED_PROJECT_STATE_SESSION }); component = fixture.componentInstance; + component.pentestInfo$.next(DESIRED_PROJECT_STATE_SESSION.selectedPentest); fixture.detectChanges(); }); 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 2f8ec49..bedb3b1 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 @@ -95,7 +95,7 @@ export class PentestFindingsComponent implements OnInit { } ).pipe( filter(value => !!value), - tap((value) => console.warn('FindingDialogBody: ', value)), + /* tap((value) => console.warn('FindingDialogBody: ', value))*/ mergeMap((value: FindingDialogBody) => this.pentestService.saveFinding( this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '', @@ -105,6 +105,7 @@ export class PentestFindingsComponent implements OnInit { untilDestroyed(this) ).subscribe({ next: () => { + // ToDo: Parse new Counter to overview / -> dispatch to store maybe already update it this.loadFindingsData(); this.notificationService.showPopup('finding.popup.save.success', PopupType.SUCCESS); }, diff --git a/security-c4po-angular/src/shared/models/finding.model.ts b/security-c4po-angular/src/shared/models/finding.model.ts index f254dd3..ed4716e 100644 --- a/security-c4po-angular/src/shared/models/finding.model.ts +++ b/security-c4po-angular/src/shared/models/finding.model.ts @@ -1,7 +1,5 @@ import {v4 as UUID} from 'uuid'; import {Severity} from '@shared/models/severity.enum'; -import {Category} from '@shared/models/category.model'; -import {Pentest} from '@shared/models/pentest.model'; export class Finding { id?: string; @@ -47,13 +45,13 @@ export function transformFindingsToObjectiveEntries(findings: Finding[]): Findin findings.forEach((value: Finding) => { findingEntries.push({ findingId: value.id, - severity: value.severity, + severity: typeof value.severity !== 'number' ? Severity[value.severity] : value.severity, title: value.title, impact: value.impact, kind: 'cell', childEntries: null, expanded: false - } as FindingEntry); + } as unknown as FindingEntry); }); return findingEntries; } diff --git a/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.html b/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.html index eecf02c..910c9f4 100644 --- a/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.html +++ b/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.html @@ -78,6 +78,7 @@ {{formArray[1].labelKey | translate}} diff --git a/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.ts b/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.ts index ec4a6e1..1dd1006 100644 --- a/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.ts +++ b/security-c4po-angular/src/shared/modules/finding-dialog/finding-dialog.component.ts @@ -58,7 +58,7 @@ export class FindingDialogComponent implements OnInit { onClickSave(value: any): void { this.dialogRef.close({ title: value.findingTitle, - severity: value.findingSeverity, + severity: this.formArray[1].controlsConfig[0].value, description: value.findingDescription, impact: value.findingImpact, affectedUrls: this.affectedUrls ? this.affectedUrls : [], diff --git a/security-c4po-angular/src/shared/services/pentest.service.ts b/security-c4po-angular/src/shared/services/pentest.service.ts index b639b75..52cce89 100644 --- a/security-c4po-angular/src/shared/services/pentest.service.ts +++ b/security-c4po-angular/src/shared/services/pentest.service.ts @@ -85,7 +85,6 @@ export class PentestService { * @param pentestId the id of the project */ public getFindingsByPentestId(pentestId: string): Observable { - console.warn('Findings for:', pentestId); if (pentestId) { return this.http.get(`${this.apiBaseURL}/${pentestId}/findings`); } else { @@ -134,7 +133,6 @@ export class PentestService { * @param finding the information of the finding */ public saveFinding(pentestId: string, finding: Finding): Observable { - console.warn('Finding: ', finding); return this.http.post(`${this.apiBaseURL}/${pentestId}/finding`, finding); } diff --git a/security-c4po-api/security-c4po-api.postman_collection.json b/security-c4po-api/security-c4po-api.postman_collection.json index e0f4490..351d9b1 100644 --- a/security-c4po-api/security-c4po-api.postman_collection.json +++ b/security-c4po-api/security-c4po-api.postman_collection.json @@ -259,7 +259,7 @@ "name": "pentests", "item": [ { - "name": "Finding", + "name": "findings", "item": [ { "name": "saveFinding", @@ -304,6 +304,41 @@ } }, "response": [] + }, + { + "name": "getFindingsForPentesId", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2Njg0MzUzNDAsImlhdCI6MTY2ODQzNTA0MCwianRpIjoiMTRiYjYyNTUtMTc5Zi00MTkyLWFmNGMtYjdiNTc3NTVmNmIxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiIyYmI2NTU5Yi04MThlLTQxNjgtOGE5Yy1lYmNlZjVmN2M4NjUiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImM0cG9fdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjNHBvX2xvY2FsIjp7InJvbGVzIjpbInVzZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InR0dCIsImdpdmVuX25hbWUiOiJ0ZXN0IiwiZmFtaWx5X25hbWUiOiJ1c2VyIn0.DQRSUcwW4Im2wxp9t8Jm4rFsS3ZFydGsNEEZ0-yfoq0B46kgLD_dOfLzVCkhZfZHsbmFIZv704j_dzUDafqtzVilUV5LM5LCqKs0ByRYB9WA-wXKiRsbKfob_OnwlVrXu2ull2_7o4SXgTnF50yyAONkzegfP-I4cJko0yeKDmeYdWrZpwHJcDtZjZl6rZbQk3BLbICcNMO6F57LtU6tHfFIIxrvlbKGqA49PH7S6n5grTNoA9_fzHnn46DJvsRw0RtzFR-QTrCy3HNdPeClgXYJvSudvwUIuaKjbfpUNU3BzGSBOjvlDpWqkbuiUX1COhJbk83PQk8-mPoltiGSFA", + "type": "string" + }, + { + "key": "undefined", + "type": "any" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8443/pentests/11601f51-bc17-47fd-847d-0c53df5405b5/findings", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8443", + "path": [ + "pentests", + "11601f51-bc17-47fd-847d-0c53df5405b5", + "findings" + ] + } + }, + "response": [] } ] }, 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 756d55c..5cf9844 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 @@ -5,6 +5,8 @@ enum class Errorcode(val code: Int) { ProjectsNotFound(1001), ProjectNotFound(1002), PentestNotFound(1003), + FindingsNotFound(1004), + FindingNotFound(1005), // 2XXX Already Changed ProjectAlreadyChanged(2001), 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 b5efd11..3f936ad 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 @@ -29,6 +29,7 @@ data class FindingRequestBody( fun Finding.toFindingResponseBody(): ResponseBody { return mapOf( "id" to id, + "severity" to severity, "title" to title, "description" to description, "impact" to impact, 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 d5dddf4..33e69a1 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 @@ -3,6 +3,7 @@ package com.securityc4po.api.finding import org.springframework.data.mongodb.repository.Query import org.springframework.data.mongodb.repository.ReactiveMongoRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Flux import reactor.core.publisher.Mono @Repository @@ -10,4 +11,7 @@ interface FindingRepository : ReactiveMongoRepository { @Query("{'data._id' : ?0}") fun findFindingById(id: String): Mono + + @Query("{'data._id' :{\$in: ?0 }}") + fun findFindingsByIds(id: List): Flux } 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 c496b7d..398f857 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 @@ -10,6 +10,7 @@ import com.securityc4po.api.pentest.PentestService import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import org.springframework.stereotype.Service import reactor.core.publisher.Mono +import reactor.kotlin.core.publisher.switchIfEmpty @Service @SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION) @@ -55,4 +56,34 @@ class FindingService(private val findingRepository: FindingRepository, private v ) } } + + /** + * Get [Finding] by findingId + * + * @return of [Finding] + */ + fun getFindingById(findingId: String): Mono { + return this.findingRepository.findFindingById(findingId).switchIfEmpty { + logger.warn("Finding with id $findingId not found.") + val msg = "Finding with id $findingId not found." + val ex = EntityNotFoundException(msg, Errorcode.FindingNotFound) + throw ex + }.map { it.toFinding() } + } + + /** + * Get all [Finding]s by findingsId's + * + * @return list of [Finding]s + */ + fun getFindingsByIds(findingIds: List): Mono> { + return findingRepository.findFindingsByIds(findingIds).collectList().map { + it.map { findingEntity -> findingEntity.toFinding() } + }.switchIfEmpty { + val msg = "Findings not found." + val ex = EntityNotFoundException(msg, Errorcode.FindingsNotFound) + logger.warn(msg, ex) + throw ex + } + } } \ 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 50a2123..6d58525 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 @@ -82,4 +82,17 @@ class PentestController(private val pentestService: PentestService, private val ResponseEntity.accepted().body(it.toFindingResponseBody()) } } -} + + // ToDo: Add Documentation & Tests + @GetMapping("/{pentestId}/findings") + fun getFindings(@PathVariable(value = "pentestId") pentestId: String): Mono>> { + return this.pentestService.getFindingIdsByPentestId(pentestId).flatMap { findingIds: List -> + this.findingService.getFindingsByIds(findingIds).map { findingList -> + findingList.map { it.toFindingResponseBody() } + } + }.map { + if (it.isEmpty()) ResponseEntity.noContent().build() + else ResponseEntity.ok(it) + } + } +} \ 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 693f2a5..c4f3c08 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 @@ -25,7 +25,12 @@ class PentestService(private val pentestRepository: PentestRepository, private v * @return list of [Pentest] */ fun getPentestsForCategory(projectId: String, category: PentestCategory): Mono> { - return pentestRepository.findPentestByProjectIdAndCategory(projectId, category).collectList().map { + return pentestRepository.findPentestByProjectIdAndCategory(projectId, category)/*.switchIfEmpty { + logger.warn("Pentests for project id $projectId not found. Collecting pentests not possible.") + val msg = "Pentests for project id $projectId not found." + val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound) + throw ex + }*/.collectList().map { it.map { pentestEntity -> pentestEntity.toPentest() } } } @@ -148,4 +153,18 @@ class PentestService(private val pentestRepository: PentestRepository, private v } } } + + /** + * Get all [Finding]Id's by pentestId + * + * @return list of [String] + */ + fun getFindingIdsByPentestId(pentestId: String): Mono> { + return this.pentestRepository.findPentestById(pentestId).switchIfEmpty { + logger.warn("Pentest with id $pentestId not found. Collecting findings not possible.") + val msg = "Pentest with id $pentestId not found." + val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound) + throw ex + }.map { pentestEntity -> pentestEntity.data.findingIds } + } } \ No newline at end of file diff --git a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectService.kt b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectService.kt index c812bdd..e088c30 100644 --- a/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectService.kt +++ b/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/ProjectService.kt @@ -28,7 +28,7 @@ class ProjectService(private val projectRepository: ProjectRepository) { return projectRepository.findAll().collectList().map { it.map { projectEntity -> projectEntity.toProject() } }.switchIfEmpty { - val msg = "No projects not found." + val msg = "Projects not found." val ex = EntityNotFoundException(msg, Errorcode.ProjectsNotFound) logger.warn(msg, ex) throw ex