feat: As a developer, I want to get findings by pentestId
This commit is contained in:
parent
b2f430f9fd
commit
e9aec4ec3e
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 : [],
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue