fix: design pattern and QoL Improvements

This commit is contained in:
Marcel Haag 2022-12-23 10:17:06 +01:00 committed by Cel
parent 46f79dcf89
commit 16bc1a5d4f
35 changed files with 972 additions and 627 deletions

View File

@ -2997,6 +2997,21 @@
"tslib": "^2.0.0"
}
},
"@ngxs/storage-plugin": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/@ngxs/storage-plugin/-/storage-plugin-3.7.6.tgz",
"integrity": "sha512-jeeoUgsMdb4M8kIHdnO0X6I73okmRJhMW9znaqhYF/aZ+PiQrogdJ5xvSXmoQ7Gg57YfQEhVErM+zf/HD7fnMA==",
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@ngxs/store": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.4.tgz",
@ -11338,9 +11353,9 @@
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"moment-timezone": {
"version": "0.5.38",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.38.tgz",
"integrity": "sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==",
"version": "0.5.40",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz",
"integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==",
"requires": {
"moment": ">= 2.9.0"
}

View File

@ -32,6 +32,7 @@
"@ngneat/until-destroy": "~8.0.4",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"@ngxs/storage-plugin": "^3.7.3",
"@ngxs/store": "^3.7.3",
"eva-icons": "^1.1.3",
"i18n-iso-countries": "^6.8.0",

View File

@ -38,7 +38,12 @@
{{ 'comment.relatedFindings' | translate }}
</th>
<td nbTreeGridCell *nbTreeGridCellDef="let comment" class="related-finding-cell">
{{ comment.data['relatedFindings'].length ? comment.data['relatedFindings'] : 'comment.no.relatedFindings' | translate }}
<ng-container *ngIf="comment.data['relatedFindings'].length > 0; else NoRelatedFindings">
<app-findig-widget [numberOfFindigs]="comment.data['relatedFindings'].length"></app-findig-widget>
</ng-container>
<ng-template #NoRelatedFindings>
{{ 'comment.no.relatedFindings' | translate }}
</ng-template>
</td>
</ng-container>
<!-- Actions -->

View File

@ -7,7 +7,8 @@
.comment-cell {
// Add style here
height: 4.5rem !important;
max-height: 4.5rem !important;
// max-height: 4.5rem !important;
overflow: hidden;
}
.comment-cell:hover {
@ -15,10 +16,17 @@
background-color: nb-theme(color-basic-transparent-focus);
}
.cell {
height: 4.5rem !important;
max-height: 4.5rem !important;
}
.related-finding-cell {
height: 4.5rem !important;
max-height: 4.5rem !important;
// cursor: pointer;
font-family: Courier, serif;
color: nb-theme(color-info-default);
color: nb-theme(color-danger-default);
}
.cell-actions {

View File

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

View File

@ -22,6 +22,7 @@ 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';
import {FindingService} from '@shared/services/finding.service';
@UntilDestroy()
@Component({
@ -31,7 +32,7 @@ import {DialogService} from '@shared/services/dialog-service/dialog.service';
})
export class PentestFindingsComponent implements OnInit {
constructor(private readonly pentestService: PentestService,
constructor(private readonly findingService: FindingService,
private dataSourceBuilder: NbTreeGridDataSourceBuilder<FindingEntry>,
private notificationService: NotificationService,
private dialogService: DialogService,
@ -75,7 +76,7 @@ export class PentestFindingsComponent implements OnInit {
}
loadFindingsData(): void {
this.pentestService.getFindingsByPentestId(this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '')
this.findingService.getFindingsByPentestId(this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '')
.pipe(
untilDestroyed(this),
/*filter(isNotNullOrUndefined),*/
@ -115,7 +116,7 @@ export class PentestFindingsComponent implements OnInit {
filter(value => !!value),
/*tap((value) => console.warn('FindingDialogBody: ', value)),*/
mergeMap((value: FindingDialogBody) =>
this.pentestService.saveFinding(
this.findingService.saveFinding(
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
transformFindingToRequestBody(value)
)
@ -135,7 +136,7 @@ export class PentestFindingsComponent implements OnInit {
}
onClickEditFinding(findingEntry): void {
this.pentestService.getFindingById(findingEntry.data.findingId).pipe(
this.findingService.getFindingById(findingEntry.data.findingId).pipe(
filter(isNotNullOrUndefined),
untilDestroyed(this)
).subscribe({
@ -154,7 +155,7 @@ export class PentestFindingsComponent implements OnInit {
filter(value => !!value),
/*tap((value) => console.warn('FindingDialogBody: ', value)),*/
mergeMap((value: FindingDialogBody) =>
this.pentestService.updateFinding(
this.findingService.updateFinding(
findingEntry.data.findingId,
transformFindingToRequestBody(value)
)
@ -191,7 +192,7 @@ export class PentestFindingsComponent implements OnInit {
message
).onClose.pipe(
filter((confirm) => !!confirm),
switchMap(() => this.pentestService.deleteFindingByPentestAndFindingId(
switchMap(() => this.findingService.deleteFindingByPentestAndFindingId(
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
findingEntry.data.findingId)
),
@ -201,7 +202,8 @@ export class PentestFindingsComponent implements OnInit {
}),
untilDestroyed(this)
).subscribe({
next: () => {
next: (deletedFinding: any) => {
this.store.dispatch(new UpdatePentestFindings(deletedFinding.id));
this.loadFindingsData();
this.notificationService.showPopup('finding.popup.delete.success', PopupType.SUCCESS);
}, error: error => {

View File

@ -16,6 +16,7 @@ import {CommonAppModule} from '../common-app.module';
import {SeverityTagModule} from '@shared/widgets/severity-tag/severity-tag.module';
import {FindingDialogModule} from '@shared/modules/finding-dialog/finding-dialog.module';
import {CommentDialogModule} from '@shared/modules/comment-dialog/comment-dialog.module';
import {FindigWidgetModule} from '@shared/widgets/findig-widget/findig-widget.module';
@NgModule({
declarations: [
@ -47,6 +48,7 @@ import {CommentDialogModule} from '@shared/modules/comment-dialog/comment-dialog
// Dialog Modules
FindingDialogModule,
CommentDialogModule,
FindigWidgetModule,
]
})
export class PentestModule {

View File

@ -153,7 +153,7 @@
"add": "Kommentar hinzufügen",
"add.finding": "Fund hinzufügen",
"no.comments": "Keine Kommentare verfügbar",
"no.relatedFindings": "Nicht verbunden mit Fund",
"no.relatedFindings": "Nicht verbunden mit einem Fund",
"relatedFindingsPlaceholder": "Fund auswählen",
"noFindingsInObjectivePlaceholder": "Objective hat keine Befunde, auf die es sich beziehen könnte.",
"create": {

View File

@ -153,7 +153,7 @@
"add": "Add Comment",
"add.finding": "Add related finding",
"no.comments": "No comments available",
"no.relatedFindings": "Not related to finding",
"no.relatedFindings": "Not related to any finding",
"relatedFindingsPlaceholder": "Select a related finding",
"noFindingsInObjectivePlaceholder": "Objective doesn't have any findings to relate to.",
"create": {

View File

@ -12,6 +12,7 @@ import {Store} from '@ngxs/store';
import {Finding} from '@shared/models/finding.model';
import {RelatedFindingOption} from '@shared/models/comment.model';
import {BehaviorSubject} from 'rxjs';
import {FindingService} from '@shared/services/finding.service';
@Component({
selector: 'app-comment-dialog',
@ -39,7 +40,7 @@ export class CommentDialogComponent implements OnInit {
@Inject(NB_DIALOG_CONFIG) private data: GenericDialogData,
private fb: FormBuilder,
protected dialogRef: NbDialogRef<CommentDialogComponent>,
private readonly pentestService: PentestService,
private readonly findingService: FindingService,
private store: Store
) {
}
@ -95,7 +96,7 @@ export class CommentDialogComponent implements OnInit {
}
requestRelatedFindingsData(pentestId: string, relatedFindings: any): void {
this.pentestService.getFindingsByPentestId(pentestId).pipe(
this.findingService.getFindingsByPentestId(pentestId).pipe(
untilDestroyed(this)
).subscribe({
next: (findings: Finding[]) => {

View File

@ -0,0 +1,26 @@
import { TestBed } from '@angular/core/testing';
import { FindingService } from './finding.service';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NgxsModule} from '@ngxs/store';
import {ProjectState} from '@shared/stores/project-state/project-state';
describe('FindingService', () => {
let service: FindingService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
BrowserAnimationsModule,
NgxsModule.forRoot([ProjectState])
]
});
service = TestBed.inject(FindingService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,62 @@
import { Injectable } from '@angular/core';
import {environment} from '../../environments/environment';
import {HttpClient} from '@angular/common/http';
import {Store} from '@ngxs/store';
import {Observable} from 'rxjs';
import {Finding} from '@shared/models/finding.model';
@Injectable({
providedIn: 'root'
})
export class FindingService {
private apiBaseURL = `${environment.apiEndpoint}/pentests`;
constructor(
private http: HttpClient,
private readonly store: Store) {
}
/**
* Get Findings for Pentest Id
* @param pentestId the id of the project
*/
public getFindingsByPentestId(pentestId: string): Observable<Finding[]> {
return this.http.get<Finding[]>(`${this.apiBaseURL}/${pentestId}/findings`);
}
/**
* Get Finding by Id
* @param findingId the id of the finding
*/
public getFindingById(findingId: string): Observable<Finding> {
return this.http.get<Finding>(`${this.apiBaseURL}/${findingId}/finding`);
}
/**
* Save Finding
* @param pentestId the id of the pentest
* @param finding the information of the finding
*/
public saveFinding(pentestId: string, finding: Finding): Observable<Finding> {
return this.http.post<Finding>(`${this.apiBaseURL}/${pentestId}/finding`, finding);
}
/**
* Update Finding
* @param findingId the id of the finding
* @param finding the information of the finding
*/
public updateFinding(findingId: string, finding: Finding): Observable<Finding> {
return this.http.patch<Finding>(`${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<string> {
return this.http.delete<string>(`${this.apiBaseURL}/${pentestId}/finding/${findingId}`);
}
}

View File

@ -9,8 +9,6 @@ import {ProjectState} from '@shared/stores/project-state/project-state';
import {catchError, map, switchMap} from 'rxjs/operators';
import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-pentests-for-category.function';
import {Finding} from '@shared/models/finding.model';
import {Comment} from '@shared/models/comment.model';
import {v4 as UUID} from 'uuid';
@Injectable({
providedIn: 'root'
@ -78,47 +76,4 @@ export class PentestService {
public updatePentest(pentest: Pentest): Observable<Pentest> {
return this.http.patch<Pentest>(`${this.apiBaseURL}/${pentest.id}`, pentest);
}
/**
* Get Findings for Pentest Id
* @param pentestId the id of the project
*/
public getFindingsByPentestId(pentestId: string): Observable<Finding[]> {
return this.http.get<Finding[]>(`${this.apiBaseURL}/${pentestId}/findings`);
}
/**
* Get Finding by Id
* @param findingId the id of the finding
*/
public getFindingById(findingId: string): Observable<Finding> {
return this.http.get<Finding>(`${this.apiBaseURL}/${findingId}/finding`);
}
/**
* Save Finding
* @param pentestId the id of the pentest
* @param finding the information of the finding
*/
public saveFinding(pentestId: string, finding: Finding): Observable<Finding> {
return this.http.post<Finding>(`${this.apiBaseURL}/${pentestId}/finding`, finding);
}
/**
* Update Finding
* @param findingId the id of the finding
* @param finding the information of the finding
*/
public updateFinding(findingId: string, finding: Finding): Observable<Finding> {
return this.http.patch<Finding>(`${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<string> {
return this.http.delete<string>(`${this.apiBaseURL}/${pentestId}/finding/${findingId}`);
}
}

View File

@ -23,6 +23,10 @@ export interface ProjectStateModel {
selectedPentest: Pentest;
}
export interface FindingMap {
[key: string]: string;
}
@State<ProjectStateModel>({
name: PROJECT_STATE_NAME,
defaults: {
@ -91,12 +95,14 @@ export class ProjectState {
updatePentestFindings(ctx: StateContext<ProjectStateModel>, {findingId}: UpdatePentestFindings): void {
const state = ctx.getState();
let stateSelectedPentest: Pentest = state.selectedPentest;
// State objects
const stateFindingIds: Array<string> = stateSelectedPentest.findingIds || [];
let updatedFindingIds: Array<string> = [];
if (!stateFindingIds.includes(findingId)) {
updatedFindingIds = [...stateFindingIds, findingId];
} else {
// ToDo: Add logic to remove findingId from array
const findingIndex = stateFindingIds.indexOf(findingId);
updatedFindingIds = [...stateFindingIds.slice(0, findingIndex)];
}
// overwrites only findingIds
stateSelectedPentest = {
@ -113,12 +119,14 @@ export class ProjectState {
updatePentestComments(ctx: StateContext<ProjectStateModel>, {commentId}: UpdatePentestComments): void {
const state = ctx.getState();
let stateSelectedPentest: Pentest = state.selectedPentest;
// State objects
const stateCommentIds: Array<string> = stateSelectedPentest.commentIds || [];
let updatedCommentIds: Array<string> = [];
if (!stateCommentIds.includes(commentId)) {
updatedCommentIds = [...stateCommentIds, commentId];
} else {
// ToDo: Add logic to remove commentId from array
const commentIndex = stateCommentIds.indexOf(commentId);
updatedCommentIds = [...stateCommentIds.slice(0, commentIndex)];
}
// overwrites only findingIds
stateSelectedPentest = {

View File

@ -13,11 +13,8 @@
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
],
"module": "esNext",
"lib": ["es2019", "es2018", "dom"],
"paths": {
"@shared/*": ["./src/shared/*"],
"@assets/*": ["./src/assets/*"]

View File

@ -4,15 +4,10 @@ import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.extensions.getLoggerFor
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")
@ -23,7 +18,7 @@ import reactor.kotlin.core.publisher.switchIfEmpty
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
)
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestController(private val pentestService: PentestService, private val findingService: FindingService) {
class PentestController(private val pentestService: PentestService) {
var logger = getLoggerFor<PentestController>()
@ -71,55 +66,4 @@ class PentestController(private val pentestService: PentestService, private val
ResponseEntity.accepted().body(it.toPentestResponseBody())
}
}
@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()) noContent().build()
else ResponseEntity.ok(it)
}
}
@GetMapping("/{findingId}/finding")
fun getFinding(@PathVariable(value = "findingId") findingId: String):Mono<ResponseEntity<ResponseBody>> {
return this.findingService.getFindingById(findingId).map {
ResponseEntity.ok().body(it.toFindingResponseBody())
}
}
@PostMapping("/{pentestId}/finding")
fun saveFinding(
@PathVariable(value = "pentestId") pentestId: String,
@RequestBody body: FindingRequestBody
): Mono<ResponseEntity<ResponseBody>> {
return this.findingService.saveFinding(pentestId, body).map {
ResponseEntity.accepted().body(it.toFindingResponseBody())
}
}
@PatchMapping("/{findingId}/finding")
fun updateFinding(
@PathVariable(value = "findingId") findingId: String,
@RequestBody body: FindingRequestBody
): Mono<ResponseEntity<ResponseBody>> {
return this.findingService.updateFinding(findingId, body).map {
ResponseEntity.accepted().body(it.toFindingResponseBody())
}
}
@DeleteMapping("/{pentestId}/finding/{findingId}")
fun deleteFinding(
@PathVariable(value = "pentestId") pentestId: String,
@PathVariable(value = "findingId") findingId: String
): Mono<ResponseEntity<ResponseBody>> {
return this.findingService.deleteFindingByPentestAndFindingId(pentestId, findingId).map {
ResponseEntity.ok().body(it.toFindingDeleteResponseBody())
}.switchIfEmpty {
Mono.just(noContent().build<ResponseBody>())
}
}
}

View File

@ -53,16 +53,16 @@ class PentestService(private val pentestRepository: PentestRepository, private v
val pentest = body.toPentest()
val pentestEntity = PentestEntity(pentest)
return pentestRepository.insert(pentestEntity).flatMap { newPentestEntity: PentestEntity ->
val pentest = newPentestEntity.toPentest()
val newPentest = newPentestEntity.toPentest()
// After successfully saving pentest add id and status to project
val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status)
val projectPentest = ProjectPentest(pentestId = newPentest.id, status = newPentest.status)
projectService.updateProjectTestingProgress(projectId, projectPentest).onErrorMap {
TransactionInterruptedException(
"Project Pentests could not be updated in Database.",
Errorcode.ProjectPentestInsertionFailed
)
}.map {
pentest
newPentest
}
}.doOnError {
throw wrappedException(

View File

@ -1,7 +1,7 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import com.securityc4po.api.ResponseBody
import com.securityc4po.api.finding.FindingRequestBody
import com.securityc4po.api.pentest.finding.FindingRequestBody
import org.springframework.data.mongodb.core.index.Indexed
import java.util.*

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.extensions.getLoggerFor
@ -6,7 +6,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import com.securityc4po.api.ResponseBody
import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
import com.securityc4po.api.configuration.error.handler.Errorcode
import com.securityc4po.api.finding.toFindingResponseBody
import com.securityc4po.api.pentest.finding.toFindingResponseBody
import com.securityc4po.api.pentest.PentestService
import org.springframework.http.ResponseEntity
import org.springframework.http.ResponseEntity.noContent

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import com.securityc4po.api.BaseEntity
import org.springframework.data.mongodb.core.mapping.Document

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import org.springframework.data.mongodb.repository.DeleteQuery
import org.springframework.data.mongodb.repository.Query

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
@ -7,7 +7,6 @@ import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
import com.securityc4po.api.configuration.error.handler.InvalidModelException
import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException
import com.securityc4po.api.extensions.getLoggerFor
import com.securityc4po.api.finding.*
import com.securityc4po.api.pentest.PentestService
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.finding
package com.securityc4po.api.pentest.finding
import com.securityc4po.api.ResponseBody
import org.springframework.data.mongodb.core.index.Indexed

View File

@ -0,0 +1,85 @@
package com.securityc4po.api.pentest.finding
import com.securityc4po.api.pentest.PentestCategory
import com.securityc4po.api.pentest.PentestRequestBody
import com.securityc4po.api.pentest.PentestService
import com.securityc4po.api.pentest.toPentestResponseBody
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.extensions.getLoggerFor
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import com.securityc4po.api.ResponseBody
import com.securityc4po.api.pentest.finding.FindingRequestBody
import com.securityc4po.api.pentest.finding.FindingService
import com.securityc4po.api.pentest.finding.toFindingDeleteResponseBody
import com.securityc4po.api.pentest.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")
@CrossOrigin(
origins = [],
allowCredentials = "false",
allowedHeaders = ["*"],
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
)
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
class FindingController(private val pentestService: PentestService, private val findingService: FindingService) {
var logger = getLoggerFor<FindingController>()
@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()) noContent().build()
else ResponseEntity.ok(it)
}
}
@GetMapping("/{findingId}/finding")
fun getFinding(@PathVariable(value = "findingId") findingId: String):Mono<ResponseEntity<ResponseBody>> {
return this.findingService.getFindingById(findingId).map {
ResponseEntity.ok().body(it.toFindingResponseBody())
}
}
@PostMapping("/{pentestId}/finding")
fun saveFinding(
@PathVariable(value = "pentestId") pentestId: String,
@RequestBody body: FindingRequestBody
): Mono<ResponseEntity<ResponseBody>> {
return this.findingService.saveFinding(pentestId, body).map {
ResponseEntity.accepted().body(it.toFindingResponseBody())
}
}
@PatchMapping("/{findingId}/finding")
fun updateFinding(
@PathVariable(value = "findingId") findingId: String,
@RequestBody body: FindingRequestBody
): Mono<ResponseEntity<ResponseBody>> {
return this.findingService.updateFinding(findingId, body).map {
ResponseEntity.accepted().body(it.toFindingResponseBody())
}
}
@DeleteMapping("/{pentestId}/finding/{findingId}")
fun deleteFinding(
@PathVariable(value = "pentestId") pentestId: String,
@PathVariable(value = "findingId") findingId: String
): Mono<ResponseEntity<ResponseBody>> {
// ToDo: Add remove finding from comment as well
return this.findingService.deleteFindingByPentestAndFindingId(pentestId, findingId).map {
ResponseEntity.ok().body(it.toFindingDeleteResponseBody())
}.switchIfEmpty {
Mono.just(noContent().build<ResponseBody>())
}
}
}

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.finding
package com.securityc4po.api.pentest.finding
import com.securityc4po.api.BaseEntity
import org.springframework.data.mongodb.core.mapping.Document

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.finding
package com.securityc4po.api.pentest.finding
import org.springframework.data.mongodb.repository.DeleteQuery
import org.springframework.data.mongodb.repository.Query

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.finding
package com.securityc4po.api.pentest.finding
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
@ -37,15 +37,15 @@ class FindingService(private val findingRepository: FindingRepository, private v
val finding = body.toFinding()
val findingEntity = FindingEntity(finding)
return findingRepository.insert(findingEntity).flatMap { newFindingEntity: FindingEntity ->
val finding = newFindingEntity.toFinding()
val newFinding = newFindingEntity.toFinding()
// After successfully saving finding add id to pentest
pentestService.updatePentestFinding(pentestId, finding.id).onErrorMap {
pentestService.updatePentestFinding(pentestId, newFinding.id).onErrorMap {
TransactionInterruptedException(
"Pentest could not be updated in Database.",
Errorcode.PentestInsertionFailed
)
}.map {
finding
newFinding
}
}.doOnError {
throw wrappedException(

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.finding
package com.securityc4po.api.pentest.finding
enum class Severity {
LOW,

View File

@ -6,7 +6,6 @@ import com.securityc4po.api.BaseDocumentationIntTest
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
import com.securityc4po.api.finding.*
import com.securityc4po.api.project.Project
import com.securityc4po.api.project.ProjectEntity
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
@ -109,7 +108,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
findingIds = emptyList(),
commentIds = emptyList()
)
@ -233,287 +232,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
)
}
@Nested
inner class GetFindings {
@Test
fun getFindingsByPentestId() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.get()
.uri("/pentests/{pentestId}/findings", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(getFindingsResponse()))
.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 get the findings for")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
.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)
.description("The title of the requested finding"),
PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("[].impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("[].affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("[].reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("[].mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
private fun getFindingsResponse() = listOf(
findingOne.toFindingResponseBody()
)
}
@Nested
inner class GetFinding {
@Test
fun getFindingById() {
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
webTestClient.get()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
.consumeWith(
WebTestClientRestDocumentation.document(
"{methodName}",
Preprocessors.preprocessRequest(
Preprocessors.prettyPrint(),
Preprocessors.modifyUris().removePort(),
Preprocessors.removeHeaders("Host", "Content-Length")
),
Preprocessors.preprocessResponse(
Preprocessors.prettyPrint()
),
RequestDocumentation.relaxedPathParameters(
RequestDocumentation.parameterWithName("findingId").description("The id of the feinidng you want to get")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
.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)
.description("The title of the requested finding"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
}
@Nested
inner class SaveFinding {
@Test
fun saveFindingByPentestId() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.post()
.uri("/pentests/{pentestId}/finding", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(findingBody))
.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 save the finding for")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
.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)
.description("The title of the finding"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingBody = FindingRequestBody(
severity = "MEDIUM",
title = "Found another Bug",
description = "Another OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack more",
mitigation = "Another None"
)
}
@Nested
inner class UpdateFinding {
@Test
fun updateFindingById() {
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
webTestClient.patch()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(findingBody))
.consumeWith(
WebTestClientRestDocumentation.document(
"{methodName}",
Preprocessors.preprocessRequest(
Preprocessors.prettyPrint(),
Preprocessors.modifyUris().removePort(),
Preprocessors.removeHeaders("Host", "Content-Length")
),
Preprocessors.preprocessResponse(
Preprocessors.prettyPrint()
),
RequestDocumentation.relaxedPathParameters(
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to update")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
.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)
.description("The title of the requested finding"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingBody = FindingRequestBody(
severity = "HIGH",
title = "Updated Bug",
description = "Updated OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "Still None"
)
}
@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
@ -542,7 +260,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
findingIds = emptyList(),
commentIds = emptyList()
)
val pentestThree = Pentest(
@ -554,23 +272,11 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
findingIds = emptyList(),
commentIds = emptyList()
)
// Finding
val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
// persist test data in database
mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(PentestEntity(pentestOne))
mongoTemplate.save(PentestEntity(pentestTwo))
mongoTemplate.save(PentestEntity(pentestThree))
mongoTemplate.save(FindingEntity(findingOne))
}
private fun configureAdminToken() {
@ -580,7 +286,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
private fun cleanUp() {
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
tokenAdmin = "n/a"
}

View File

@ -5,7 +5,6 @@ import com.securityc4po.api.BaseIntTest
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
import com.securityc4po.api.finding.*
import com.securityc4po.api.project.Project
import com.securityc4po.api.project.ProjectEntity
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
@ -86,7 +85,7 @@ class PentestControllerIntTest : BaseIntTest() {
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
findingIds = emptyList(),
commentIds = emptyList()
)
@ -159,145 +158,6 @@ class PentestControllerIntTest : BaseIntTest() {
)
}
@Nested
inner class GetFindings {
@Test
fun `requesting findings by pentestId successfully`() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.get()
.uri("/pentests/{pentestId}/findings", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody().json(Json.write(getFindings()))
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
private fun getFindings() = listOf(
findingOne.toFindingResponseBody()
)
}
@Nested
inner class GetFinding {
@Test
fun `requesting finding by findingId successfully`() {
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
webTestClient.get()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
}
@Nested
inner class SaveFinding {
@Test
fun `save finding successfully`() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.post()
.uri("/pentests/{pentestId}/finding", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody()
.jsonPath("$.severity").isEqualTo("MEDIUM")
.jsonPath("$.title").isEqualTo("Found another Bug")
.jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug")
.jsonPath("$.impact").isEqualTo("Service")
.jsonPath("$.affectedUrls").isEmpty
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack more")
.jsonPath("$.mitigation").isEqualTo("Another None")
}
private val findingBody = FindingRequestBody(
severity = "MEDIUM",
title = "Found another Bug",
description = "Another OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack more",
mitigation = "Another None"
)
}
@Nested
inner class UpdateFinding {
@Test
fun `update finding successfully`() {
val findingId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.post()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody()
.jsonPath("$.severity").isEqualTo("HIGH")
.jsonPath("$.title").isEqualTo("Updated Bug")
.jsonPath("$.description").isEqualTo("Updated OTG-INFO-002 Bug")
.jsonPath("$.impact").isEqualTo("Service")
.jsonPath("$.affectedUrls").isEmpty
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack")
.jsonPath("$.mitigation").isEqualTo("Still None")
}
private val findingBody = FindingRequestBody(
severity = "HIGH",
title = "Updated Bug",
description = "Updated OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "Still None"
)
}
@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
@ -325,7 +185,7 @@ class PentestControllerIntTest : BaseIntTest() {
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
findingIds = emptyList(),
commentIds = emptyList()
)
val pentestThree = Pentest(
@ -337,23 +197,11 @@ class PentestControllerIntTest : BaseIntTest() {
findingIds = emptyList(),
commentIds = emptyList()
)
// Finding
val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
// persist test data in database
mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(PentestEntity(pentestOne))
mongoTemplate.save(PentestEntity(pentestTwo))
mongoTemplate.save(PentestEntity(pentestThree))
mongoTemplate.save(FindingEntity(findingOne))
}
private fun configureAdminToken() {
@ -363,7 +211,6 @@ class PentestControllerIntTest : BaseIntTest() {
private fun cleanUp() {
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
tokenAdmin = "n/a"
}

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.tomakehurst.wiremock.common.Json
@ -10,6 +10,10 @@ import com.securityc4po.api.pentest.Pentest
import com.securityc4po.api.pentest.PentestCategory
import com.securityc4po.api.pentest.PentestEntity
import com.securityc4po.api.pentest.PentestStatus
import com.securityc4po.api.pentest.comment.Comment
import com.securityc4po.api.pentest.comment.CommentEntity
import com.securityc4po.api.pentest.comment.CommentRequestBody
import com.securityc4po.api.pentest.comment.toCommentResponseBody
import com.securityc4po.api.project.Project
import com.securityc4po.api.project.ProjectEntity
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings

View File

@ -1,4 +1,4 @@
package com.securityc4po.api.comment
package com.securityc4po.api.pentest.comment
import com.github.tomakehurst.wiremock.common.Json
import com.securityc4po.api.BaseIntTest
@ -9,6 +9,10 @@ import com.securityc4po.api.pentest.Pentest
import com.securityc4po.api.pentest.PentestCategory
import com.securityc4po.api.pentest.PentestEntity
import com.securityc4po.api.pentest.PentestStatus
import com.securityc4po.api.pentest.comment.Comment
import com.securityc4po.api.pentest.comment.CommentEntity
import com.securityc4po.api.pentest.comment.CommentRequestBody
import com.securityc4po.api.pentest.comment.toCommentResponseBody
import com.securityc4po.api.project.Project
import com.securityc4po.api.project.ProjectEntity
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings

View File

@ -0,0 +1,403 @@
package com.securityc4po.api.pentest.finding
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.tomakehurst.wiremock.common.Json
import com.securityc4po.api.BaseDocumentationIntTest
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
import com.securityc4po.api.pentest.Pentest
import com.securityc4po.api.pentest.PentestCategory
import com.securityc4po.api.pentest.PentestEntity
import com.securityc4po.api.pentest.PentestStatus
import com.securityc4po.api.project.Project
import com.securityc4po.api.project.ProjectEntity
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Query
import org.springframework.restdocs.operation.preprocess.Preprocessors
import org.springframework.restdocs.payload.JsonFieldType
import org.springframework.restdocs.payload.PayloadDocumentation
import org.springframework.restdocs.request.RequestDocumentation
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import reactor.core.publisher.Mono
@SuppressFBWarnings(
SIC_INNER_SHOULD_BE_STATIC,
NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,
RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
)
class FindingControllerDocumentationTest: BaseDocumentationIntTest() {
@Autowired
lateinit var mongoTemplate: MongoTemplate
var mapper = ObjectMapper()
@BeforeEach
fun init() {
configureAdminToken()
persistBasicTestScenario()
}
@AfterEach
fun destroy() {
cleanUp()
}
@Nested
inner class GetFindings {
@Test
fun getFindingsByPentestId() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.get()
.uri("/pentests/{pentestId}/findings", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(getFindingsResponse()))
.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 get the findings for")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
.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)
.description("The title of the requested finding"),
PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("[].impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("[].affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("[].reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("[].mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
private fun getFindingsResponse() = listOf(
findingOne.toFindingResponseBody()
)
}
@Nested
inner class GetFinding {
@Test
fun getFindingById() {
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
webTestClient.get()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
.consumeWith(
WebTestClientRestDocumentation.document(
"{methodName}",
Preprocessors.preprocessRequest(
Preprocessors.prettyPrint(),
Preprocessors.modifyUris().removePort(),
Preprocessors.removeHeaders("Host", "Content-Length")
),
Preprocessors.preprocessResponse(
Preprocessors.prettyPrint()
),
RequestDocumentation.relaxedPathParameters(
RequestDocumentation.parameterWithName("findingId").description("The id of the feinidng you want to get")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
.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)
.description("The title of the requested finding"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
}
@Nested
inner class SaveFinding {
@Test
fun saveFindingByPentestId() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.post()
.uri("/pentests/{pentestId}/finding", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(findingBody))
.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 save the finding for")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
.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)
.description("The title of the finding"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingBody = FindingRequestBody(
severity = "MEDIUM",
title = "Found another Bug",
description = "Another OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack more",
mitigation = "Another None"
)
}
@Nested
inner class UpdateFinding {
@Test
fun updateFindingById() {
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
webTestClient.patch()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(findingBody))
.consumeWith(
WebTestClientRestDocumentation.document(
"{methodName}",
Preprocessors.preprocessRequest(
Preprocessors.prettyPrint(),
Preprocessors.modifyUris().removePort(),
Preprocessors.removeHeaders("Host", "Content-Length")
),
Preprocessors.preprocessResponse(
Preprocessors.prettyPrint()
),
RequestDocumentation.relaxedPathParameters(
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to update")
),
PayloadDocumentation.relaxedResponseFields(
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
.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)
.description("The title of the requested finding"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the finding"),
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
.description("The impact of the finding"),
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
.description("List of affected Urls of the finding"),
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
.description("The reproduction steps of the finding"),
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
.description("The example mitigation for the finding")
)
)
)
}
private val findingBody = FindingRequestBody(
severity = "HIGH",
title = "Updated Bug",
description = "Updated OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "Still None"
)
}
@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
val projectOne = Project(
id = "d2e126ba-f608-11ec-b939-0242ac120025",
client = "E Corp",
title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester",
projectPentests = emptyList(),
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
)
// Pentests
val pentestOne = Pentest(
id = "9c8af320-f608-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-001",
status = PentestStatus.NOT_STARTED,
findingIds = emptyList(),
commentIds = emptyList()
)
val pentestTwo = Pentest(
id = "43fbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
commentIds = emptyList()
)
val pentestThree = Pentest(
id = "74eae112-f62c-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
category = PentestCategory.AUTHENTICATION_TESTING,
refNumber = "OTG-AUTHN-001",
status = PentestStatus.COMPLETED,
findingIds = emptyList(),
commentIds = emptyList()
)
// Finding
val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
// persist test data in database
mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(PentestEntity(pentestOne))
mongoTemplate.save(PentestEntity(pentestTwo))
mongoTemplate.save(PentestEntity(pentestThree))
mongoTemplate.save(FindingEntity(findingOne))
}
private fun configureAdminToken() {
tokenAdmin = getAccessToken("test_admin", "test", "c4po_local", "c4po_realm_local")
}
private fun cleanUp() {
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
tokenAdmin = "n/a"
}
}

View File

@ -0,0 +1,270 @@
package com.securityc4po.api.pentest.finding
import com.github.tomakehurst.wiremock.common.Json
import com.securityc4po.api.BaseIntTest
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
import com.securityc4po.api.pentest.Pentest
import com.securityc4po.api.pentest.PentestCategory
import com.securityc4po.api.pentest.PentestEntity
import com.securityc4po.api.pentest.PentestStatus
import com.securityc4po.api.project.Project
import com.securityc4po.api.project.ProjectEntity
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.web.server.LocalServerPort
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Query
import org.springframework.test.web.reactive.server.WebTestClient
import reactor.core.publisher.Mono
import java.time.Duration
@SuppressFBWarnings(
SIC_INNER_SHOULD_BE_STATIC,
NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,
RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
)
class FindingControllerIntTest: BaseIntTest() {
@LocalServerPort
private var port = 0
@Autowired
lateinit var mongoTemplate: MongoTemplate
@Autowired
private lateinit var webTestClient: WebTestClient
@BeforeEach
fun setupWebClient() {
webTestClient = WebTestClient.bindToServer()
.baseUrl("http://localhost:$port")
.responseTimeout(Duration.ofMillis(10000))
.build()
}
@BeforeEach
fun init() {
configureAdminToken()
persistBasicTestScenario()
}
@AfterEach
fun destroy() {
cleanUp()
}
@Nested
inner class GetFindings {
@Test
fun `requesting findings by pentestId successfully`() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.get()
.uri("/pentests/{pentestId}/findings", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody().json(Json.write(getFindings()))
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
private fun getFindings() = listOf(
findingOne.toFindingResponseBody()
)
}
@Nested
inner class GetFinding {
@Test
fun `requesting finding by findingId successfully`() {
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
webTestClient.get()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
}
private val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
}
@Nested
inner class SaveFinding {
@Test
fun `save finding successfully`() {
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.post()
.uri("/pentests/{pentestId}/finding", pentestTwoId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody()
.jsonPath("$.severity").isEqualTo("MEDIUM")
.jsonPath("$.title").isEqualTo("Found another Bug")
.jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug")
.jsonPath("$.impact").isEqualTo("Service")
.jsonPath("$.affectedUrls").isEmpty
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack more")
.jsonPath("$.mitigation").isEqualTo("Another None")
}
private val findingBody = FindingRequestBody(
severity = "MEDIUM",
title = "Found another Bug",
description = "Another OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack more",
mitigation = "Another None"
)
}
@Nested
inner class UpdateFinding {
@Test
fun `update finding successfully`() {
val findingId = "43fbc63c-f624-11ec-b939-0242ac120002"
webTestClient.post()
.uri("/pentests/{findingId}/finding", findingId)
.header("Authorization", "Bearer $tokenAdmin")
.body(Mono.just(findingBody), FindingRequestBody::class.java)
.exchange()
.expectStatus().isAccepted
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
.expectBody()
.jsonPath("$.severity").isEqualTo("HIGH")
.jsonPath("$.title").isEqualTo("Updated Bug")
.jsonPath("$.description").isEqualTo("Updated OTG-INFO-002 Bug")
.jsonPath("$.impact").isEqualTo("Service")
.jsonPath("$.affectedUrls").isEmpty
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack")
.jsonPath("$.mitigation").isEqualTo("Still None")
}
private val findingBody = FindingRequestBody(
severity = "HIGH",
title = "Updated Bug",
description = "Updated OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "Still None"
)
}
@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
val projectOne = Project(
id = "d2e126ba-f608-11ec-b939-0242ac120025",
client = "E Corp",
title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester",
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
)
// pentests
val pentestOne = Pentest(
id = "9c8af320-f608-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-001",
status = PentestStatus.NOT_STARTED,
findingIds = emptyList(),
commentIds = emptyList()
)
val pentestTwo = Pentest(
id = "43fbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
category = PentestCategory.INFORMATION_GATHERING,
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
commentIds = emptyList()
)
val pentestThree = Pentest(
id = "16vbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
category = PentestCategory.AUTHENTICATION_TESTING,
refNumber = "OTG-AUTHN-001",
status = PentestStatus.COMPLETED,
findingIds = emptyList(),
commentIds = emptyList()
)
// Finding
val findingOne = Finding(
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
severity = Severity.LOW,
title = "Found Bug",
description = "OTG-INFO-002 Bug",
impact = "Service",
affectedUrls = emptyList(),
reproduction = "Step 1: Hack",
mitigation = "None"
)
// persist test data in database
mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(PentestEntity(pentestOne))
mongoTemplate.save(PentestEntity(pentestTwo))
mongoTemplate.save(PentestEntity(pentestThree))
mongoTemplate.save(FindingEntity(findingOne))
}
private fun configureAdminToken() {
tokenAdmin = getAccessToken("test_admin", "test", "c4po_local", "c4po_realm_local")
}
private fun cleanUp() {
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
tokenAdmin = "n/a"
}
}

View File

@ -10,7 +10,7 @@
"data": {
"_id": "89703b19-16c7-49e5-8e33-0c706313e5fe",
"title": "Test Comment",
"description": "Test Comment Description",
"description": "No related findings",
"relatedFindings": []
},
"_class": "com.securityc4po.api.comment.CommentEntity"
@ -25,8 +25,8 @@
},
"data": {
"_id": "df516de6-ca5e-44a6-ac50-db89bb17aac3",
"title": "New Test",
"description": "New Test",
"title": "New Test Comment",
"description": "Two related findings",
"relatedFindings": [
"0bda8950-94fa-4ec6-8fa7-e09f5a8cd3e8",
"4ddb84f6-068c-4319-a8ee-1000008bb75a"
@ -44,8 +44,8 @@
},
"data": {
"_id": "e55e943b-6a48-4d84-8d72-b48d7d9de5b7",
"title": "Wow another one?",
"description": "Epic!",
"title": "Another Test Comment",
"description": "One related findings",
"relatedFindings": [
"5e22d38f-a4f6-4809-84ea-a803b5f1f9fc"
]