feat: As a developer, I want to get findings by pentestId

This commit is contained in:
Marcel Haag 2022-11-14 16:12:21 +01:00 committed by Cel
parent b2f430f9fd
commit e9aec4ec3e
17 changed files with 127 additions and 14 deletions

View File

@ -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) => {

View File

@ -19,7 +19,7 @@
<th nbTreeGridHeaderCell *nbTreeGridHeaderCellDef class="cell-severity">
{{ 'finding.severity' | translate }}
</th>
<td nbTreeGridCell *nbTreeGridCellDef="let finding" class="cell-severity" fxFill fxLayoutAlign="center none">
<td nbTreeGridCell *nbTreeGridCellDef="let finding" class="cell-severity border-style" fxFill fxLayoutAlign="center center">
<app-severity-tag [currentSeverity]="finding.data['severity']"></app-severity-tag>
</td>
</ng-container>

View File

@ -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 {

View File

@ -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();
});

View File

@ -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);
},

View File

@ -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;
}

View File

@ -78,6 +78,7 @@
{{formArray[1].labelKey | translate}}
</label>
<nb-select class="severities" placeholder="{{formArray[1].placeholder | translate}} *"
type="severity-select"
[(selected)]="formArray[1].controlsConfig[0].value"
shape="round" status="{{getSeverityFillStatus(formArray[1].controlsConfig[0].value)}}" filled>
<nb-option *ngFor="let severity of severityTexts" [value]="severity.value">

View File

@ -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 : [],

View File

@ -85,7 +85,6 @@ export class PentestService {
* @param pentestId the id of the project
*/
public getFindingsByPentestId(pentestId: string): Observable<Finding[]> {
console.warn('Findings for:', pentestId);
if (pentestId) {
return this.http.get<Finding[]>(`${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<Finding> {
console.warn('Finding: ', finding);
return this.http.post<Finding>(`${this.apiBaseURL}/${pentestId}/finding`, finding);
}

View File

@ -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": []
}
]
},

View File

@ -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),

View File

@ -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,

View File

@ -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<FindingEntity, String> {
@Query("{'data._id' : ?0}")
fun findFindingById(id: String): Mono<FindingEntity>
@Query("{'data._id' :{\$in: ?0 }}")
fun findFindingsByIds(id: List<String>): Flux<FindingEntity>
}

View File

@ -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<Finding> {
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<String>): Mono<List<Finding>> {
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
}
}
}

View File

@ -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<ResponseEntity<List<ResponseBody>>> {
return this.pentestService.getFindingIdsByPentestId(pentestId).flatMap { findingIds: List<String> ->
this.findingService.getFindingsByIds(findingIds).map { findingList ->
findingList.map { it.toFindingResponseBody() }
}
}.map {
if (it.isEmpty()) ResponseEntity.noContent().build()
else ResponseEntity.ok(it)
}
}
}

View File

@ -25,7 +25,12 @@ class PentestService(private val pentestRepository: PentestRepository, private v
* @return list of [Pentest]
*/
fun getPentestsForCategory(projectId: String, category: PentestCategory): Mono<List<Pentest>> {
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<List<String>> {
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 }
}
}

View File

@ -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