feat: As a developer, I want to create a pentest

This commit is contained in:
Marcel Haag 2022-11-09 08:20:02 +01:00
parent de659e3293
commit e7bcf53347
30 changed files with 502 additions and 122 deletions

View File

@ -3,12 +3,11 @@ import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@neb
import {Pentest, ObjectiveEntry, transformPentestsToObjectiveEntries} from '@shared/models/pentest.model';
import {PentestService} from '@shared/services/pentest.service';
import {Store} from '@ngxs/store';
import {PROJECT_STATE_NAME, ProjectState} from '@shared/stores/project-state/project-state';
import {ProjectState} from '@shared/stores/project-state/project-state';
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
import {catchError, switchMap, tap} from 'rxjs/operators';
import {BehaviorSubject, Observable, of} from 'rxjs';
import {getTitleKeyForRefNumber} from '@shared/functions/categories/get-title-key-for-ref-number.function';
import {Route} from '@shared/models/route.enum';
import {Router} from '@angular/router';
import {ChangePentest} from '@shared/stores/project-state/project-state.actions';
@ -25,6 +24,7 @@ export class ObjectiveTableComponent implements OnInit {
dataSource: NbTreeGridDataSource<ObjectiveEntry>;
private data: ObjectiveEntry[] = [];
private pentests$: BehaviorSubject<Pentest[]> = new BehaviorSubject<Pentest[]>([]);
getters: NbGetters<ObjectiveEntry, ObjectiveEntry> = {
dataGetter: (node: ObjectiveEntry) => node,
@ -53,6 +53,8 @@ export class ObjectiveTableComponent implements OnInit {
untilDestroyed(this)
).subscribe({
next: (pentests: Pentest[]) => {
// ToDo: Change assignement here
this.pentests$.next(pentests);
this.data = transformPentestsToObjectiveEntries(pentests);
this.dataSource.setData(this.data, this.getters);
this.loading$.next(false);
@ -64,7 +66,7 @@ export class ObjectiveTableComponent implements OnInit {
});
}
selectPentest(pentest: Pentest): void {
selectPentest(selectedPentest: Pentest): void {
/* ToDo: Include again after fixing pentest route
this.router.navigate([Route.PENTEST])
.then(
@ -74,7 +76,8 @@ export class ObjectiveTableComponent implements OnInit {
})
).finally();
*/
this.store.dispatch(new ChangePentest(pentest));
const statePentest = this.pentests$.getValue().find(pentest => pentest.refNumber === selectedPentest.refNumber);
this.store.dispatch(new ChangePentest(statePentest));
}
// HTML only

View File

@ -40,8 +40,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
refNumber: 'OTF-001',
childEntries: [],
status: PentestStatus.NOT_STARTED,
findingsIds: [],
commentsIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112']
findingIds: [],
commentIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112']
},
};

View File

@ -4,7 +4,7 @@ import {Pentest} from '@shared/models/pentest.model';
import * as FA from '@fortawesome/free-solid-svg-icons';
import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@nebular/theme';
import {PentestService} from '@shared/services/pentest.service';
import {NotificationService, PopupType} from '@shared/services/notification.service';
import {NotificationService} from '@shared/services/notification.service';
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
import {filter, tap} from 'rxjs/operators';
import {Comment, CommentEntry, transformCommentsToObjectiveEntries} from '@shared/models/comment.model';
@ -63,8 +63,9 @@ export class PentestCommentsComponent implements OnInit {
this.loading$.next(false);
},
error: err => {
console.log(err);
this.notificationService.showPopup('comment.popup.not.found', PopupType.FAILURE);
console.error(err);
// ToDo: Implement again after proper lazy loading and routing
// this.notificationService.showPopup('comment.popup.not.found', PopupType.FAILURE);
this.loading$.next(false);
}
});

View File

@ -27,14 +27,27 @@
</nb-option>
</nb-select>
</div>
<button nbButton
class="save-pentest-button"
status="primary"
[disabled]="!pentestStatusChanged()"
title="{{ 'global.action.save' | translate }}"
(click)="onClickSavePentest()">
<span class="exit-element-text"> {{ 'global.action.save' | translate }} </span>
</button>
<div *ngIf="!pentest$.getValue().id; else updatePentest">
<button nbButton
class="save-pentest-button"
status="primary"
[disabled]="!pentestStatusChanged()"
title="{{ 'global.action.save' | translate }}"
(click)="onClickSavePentest()">
<span class="exit-element-text"> {{ 'global.action.save' | translate }} </span>
</button>
</div>
<ng-template #updatePentest>
<button nbButton
class="save-pentest-button"
status="primary"
[disabled]="!pentestStatusChanged()"
title="{{ 'global.action.update' | translate }}"
(click)="onClickUpdatePentest()">
<span class="exit-element-text"> {{ 'global.action.update' | translate }} </span>
</button>
</ng-template>
</div>
</div>

View File

@ -11,6 +11,8 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {Category} from '@shared/models/category.model';
import {PentestStatus} from '@shared/models/pentest-status.model';
import {NotificationService} from '@shared/services/notification.service';
import {NotificationServiceMock} from '@shared/services/notification.service.mock';
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
selectedProject: {
@ -33,8 +35,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
refNumber: 'OTF-001',
childEntries: [],
status: PentestStatus.NOT_STARTED,
findingsIds: [],
commentsIds: []
findingIds: [],
commentIds: []
},
};
@ -60,6 +62,9 @@ describe('PentestContentComponent', () => {
}),
RouterTestingModule.withRoutes([]),
NgxsModule.forRoot([ProjectState])
],
providers: [
{provide: NotificationService, useValue: new NotificationServiceMock()}
]
})
.compileComponents();

View File

@ -1,14 +1,17 @@
import {Component, OnInit} from '@angular/core';
import * as FA from '@fortawesome/free-solid-svg-icons';
import {BehaviorSubject} from 'rxjs';
import {Store} from '@ngxs/store';
import {BehaviorSubject, Observable} from 'rxjs';
import {Select, Store} from '@ngxs/store';
import {ProjectState} from '@shared/stores/project-state/project-state';
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
import {Pentest} from '@shared/models/pentest.model';
import {Pentest, transformPentestToRequestBody} from '@shared/models/pentest.model';
import {PentestStatus} from '@shared/models/pentest-status.model';
import {StatusText} from '@shared/widgets/status-tag/status-tag.component';
import {PentestService} from '@shared/services/pentest.service';
import {NotificationService, PopupType} from '@shared/services/notification.service';
import {Project} from '@shared/models/project.model';
import {isNotNullOrUndefined} from 'codelyzer/util/isNotNullOrUndefined';
import {filter} from 'rxjs/operators';
@UntilDestroy()
@Component({
@ -20,6 +23,10 @@ export class PentestContentComponent implements OnInit {
// HTML only
readonly fa = FA;
@Select(ProjectState.project)
selectedProject$: Observable<Project>;
selectedProjectId: string;
pentest$: BehaviorSubject<Pentest> = new BehaviorSubject<Pentest>(null);
pentestChanged$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
currentNumberOfFindings$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
@ -45,16 +52,29 @@ export class PentestContentComponent implements OnInit {
}
ngOnInit(): void {
this.selectedProject$.pipe(
filter(isNotNullOrUndefined),
untilDestroyed(this)
).subscribe({
next: (project) => {
this.selectedProjectId = project.id;
},
error: (err) => {
console.error(err);
}
});
this.store.selectOnce(ProjectState.pentest).pipe(
untilDestroyed(this)
).subscribe({
next: (selectedPentest: Pentest) => {
console.warn(selectedPentest);
this.pentest$.next(selectedPentest);
this.currentStatus = selectedPentest.status;
this.initialPentestStatus = selectedPentest.status;
const findings = selectedPentest.findingsIds ? selectedPentest.findingsIds.length : 0;
const findings = selectedPentest.findingIds ? selectedPentest.findingIds.length : 0;
this.currentNumberOfFindings$.next(findings);
const comments = selectedPentest.commentsIds ? selectedPentest.commentsIds.length : 0;
const comments = selectedPentest.commentIds ? selectedPentest.commentIds.length : 0;
this.currentNumberOfComments$.next(comments);
},
error: err => {
@ -65,10 +85,11 @@ export class PentestContentComponent implements OnInit {
onClickSavePentest(): void {
this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus});
console.warn('Updated Pentest: ', this.pentest$.getValue());
this.pentestService.savePentest(this.pentest$.getValue())
this.pentestService.savePentest(this.selectedProjectId, transformPentestToRequestBody(this.pentest$.getValue()))
.subscribe({
next: (pentest: Pentest) => {
this.pentest$.next(pentest);
this.initialPentestStatus = pentest.status;
this.notificationService.showPopup('pentest.popup.save.success', PopupType.SUCCESS);
},
error: err => {
@ -78,6 +99,22 @@ export class PentestContentComponent implements OnInit {
});
}
onClickUpdatePentest(): void {
this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus});
this.pentestService.updatePentest(transformPentestToRequestBody(this.pentest$.getValue()))
.subscribe({
next: (pentest: Pentest) => {
this.pentest$.next(pentest);
this.initialPentestStatus = pentest.status;
this.notificationService.showPopup('pentest.popup.update.success', PopupType.SUCCESS);
},
error: err => {
console.log(err);
this.notificationService.showPopup('pentest.popup.update.failed', PopupType.FAILURE);
}
});
}
/**
* @return true if initial pentest Status is different from current pentest status
*/

View File

@ -42,8 +42,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
refNumber: 'OTF-001',
childEntries: [],
status: PentestStatus.NOT_STARTED,
findingsIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
commentsIds: []
findingIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
commentIds: []
},
};

View File

@ -65,8 +65,9 @@ export class PentestFindingsComponent implements OnInit {
this.loading$.next(false);
},
error: err => {
console.log(err);
this.notificationService.showPopup('findings.popup.not.found', PopupType.FAILURE);
console.error(err);
// ToDo: Implement again after proper lazy loading and routing
// this.notificationService.showPopup('findings.popup.not.found', PopupType.FAILURE);
this.loading$.next(false);
}
});
@ -94,8 +95,8 @@ export class PentestFindingsComponent implements OnInit {
this.loadFindingsData();
this.notificationService.showPopup('finding.popup.save.success', PopupType.SUCCESS);
},
error: error => {
console.error(error);
error: err => {
console.error(err);
this.notificationService.showPopup('finding.popup.save.failed', PopupType.FAILURE);
}
});

View File

@ -51,8 +51,8 @@ describe('PentestInfoComponent', () => {
refNumber: 'OTF-001',
childEntries: [],
status: PentestStatus.NOT_STARTED,
findingsIds: [],
commentsIds: []
findingIds: [],
commentIds: []
});
fixture.detectChanges();
});

View File

@ -33,8 +33,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
refNumber: 'OTF-001',
childEntries: [],
status: PentestStatus.NOT_STARTED,
findingsIds: [],
commentsIds: []
findingIds: [],
commentIds: []
},
};

View File

@ -36,13 +36,11 @@
<nb-card-footer>
<div fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="start end">
<div class="project-progress">
<nb-progress-bar *ngIf="project.testingProgress > 0; else altProgressBar"
status="warning"
[value]="project.testingProgress"
[displayValue]="true">
</nb-progress-bar>
<ng-template #altProgressBar>
{{'popup.info' | translate}} {{'global.no.progress' | translate}}
</ng-template>

View File

@ -1,28 +1,31 @@
import {v4 as UUID} from 'uuid';
import {PentestStatus} from '@shared/models/pentest-status.model';
import {Category} from '@shared/models/category.model';
import {v4 as UUID} from 'uuid';
export class Pentest {
id?: string;
projectId?: string;
category: Category;
refNumber: string;
childEntries?: Pentest[];
status: PentestStatus;
findingsIds?: Array<string>;
commentsIds?: Array<string>;
findingIds?: Array<string>;
commentIds?: Array<string>;
constructor(category: Category,
refNumber: string,
status: PentestStatus,
id?: string,
projectId?: string,
findingsIds?: Array<string>,
commentsIds?: Array<string>) {
this.id = id ? id : UUID();
this.projectId = projectId ? projectId : '';
this.category = category;
this.refNumber = refNumber;
this.status = status;
this.findingsIds = findingsIds ? findingsIds : [];
this.commentsIds = commentsIds ? commentsIds : [];
this.findingIds = findingsIds ? findingsIds : [];
this.commentIds = commentsIds ? commentsIds : [];
}
}
@ -35,13 +38,31 @@ export interface ObjectiveEntry {
expanded?: boolean;
}
export function transformPentestToRequestBody(pentest: Pentest): Pentest {
const transformedPentest = {
...pentest,
projectId: pentest.projectId,
category: typeof pentest.category === 'number' ? Category[pentest.category] : pentest.category,
refNumber: pentest.refNumber,
status: pentest.status,
findingIds: pentest.findingIds ? pentest.findingIds : [],
commentIds: pentest.commentIds ? pentest.commentIds : [],
/* Remove Table Entry Object Properties */
childEntries: undefined,
kind: undefined,
findings: undefined,
expanded: undefined,
} as unknown as Pentest;
return transformedPentest;
}
export function transformPentestsToObjectiveEntries(pentests: Pentest[]): ObjectiveEntry[] {
const objectiveEntries: ObjectiveEntry[] = [];
pentests.forEach((value: Pentest) => {
objectiveEntries.push({
refNumber: value.refNumber,
status: value.status,
findings: value.findingsIds ? value.findingsIds.length : 0,
findings: value.findingIds ? value.findingIds.length : 0,
kind: value.childEntries ? 'dir' : 'cell',
childEntries: value.childEntries ? value.childEntries : null,
expanded: !!value.childEntries

View File

@ -52,8 +52,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
refNumber: 'OTF-001',
childEntries: [],
status: PentestStatus.NOT_STARTED,
findingsIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
commentsIds: []
findingIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
commentIds: []
},
};

View File

@ -11,6 +11,7 @@ import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-
import {Finding, FindingDialogBody} from '@shared/models/finding.model';
import {Severity} from '@shared/models/severity.enum';
import {Comment} from '@shared/models/comment.model';
import {v4 as UUID} from 'uuid';
@Injectable({
providedIn: 'root'
@ -29,17 +30,26 @@ export class PentestService {
* @param category the categories of which the pentests should be requested
*/
public loadPentests(category: Category): Observable<Pentest[]> {
return this.store.selectOnce(ProjectState.project).pipe(
return this.store.select(ProjectState.project).pipe(
switchMap(project => this.getPentestByProjectIdAndCategory(project.id, category)),
catchError(_ => of(null)),
map(response => {
let pentests = response;
if (!pentests) {
pentests = getTempPentestsForCategory(category);
// tslint:disable-next-line:no-console
console.info('Initial pentest data loaded.');
map((response: Pentest[]) => {
// ToDo: Improve performance by only loading templates when not all pentests of category got returned
// Load template pentest
const templatePentests = getTempPentestsForCategory(category);
// The pentests that get returned to the component
let completePentests: Pentest[] = response;
// Add pentest template to complete pentests if not included in request
if (completePentests) {
templatePentests.forEach((templatePentest: Pentest) => {
if (!completePentests.map(it => it.refNumber).includes(templatePentest.refNumber)) {
completePentests.push(templatePentest);
}
});
} else {
completePentests = templatePentests;
}
return pentests;
return completePentests;
})
);
}
@ -58,8 +68,16 @@ export class PentestService {
* Save Pentest
* @param pentest the information of the Pentest
*/
public savePentest(pentest: Pentest): Observable<Pentest> {
return this.http.post<Pentest>(`${this.apiBaseURL}/${pentest.id}`, pentest);
public savePentest(projectId: string, pentest: Pentest): Observable<Pentest> {
return this.http.post<Pentest>(`${this.apiBaseURL}/${projectId}`, pentest);
}
/**
* Update Pentest
* @param pentest the information of the Pentest
*/
public updatePentest(pentest: Pentest): Observable<Pentest> {
return this.http.patch<Pentest>(`${this.apiBaseURL}/${pentest.id}`, pentest);
}
/**

View File

@ -77,7 +77,7 @@ export class ProjectState {
changePentest(ctx: StateContext<ProjectStateModel>, {pentest}: ChangePentest): void {
const state = ctx.getState();
ctx.patchState({
selectedPentest: pentest
selectedPentest: {...pentest, projectId: state.selectedProject.id}
});
}
}

View File

@ -1,6 +1,6 @@
{
"info": {
"_postman_id": "58adc500-c0c6-47f3-b268-5fcc16e0944d",
"_postman_id": "6f244dd9-5264-497a-9ea4-1ae73e172624",
"name": "security-c4po-api",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "5225213"
@ -266,7 +266,7 @@
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NjAxNDI5NjMsImlhdCI6MTY2MDE0MjY2MywianRpIjoiNzk2YzY5NzYtZjBlYS00ZTM0LTk2MTItMjI5ZmE0ODgzOTM0IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiIyYWU1MmQyYy01MjA5LTQzMjEtOWY5OS0wMTQ2YjRkMmNkY2YiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImM0cG9fdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjNHBvX2xvY2FsIjp7InJvbGVzIjpbInVzZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InR0dCIsImdpdmVuX25hbWUiOiJ0ZXN0IiwiZmFtaWx5X25hbWUiOiJ1c2VyIn0.EO5CC1VXZzybIx-lndq3b61TZpWOnYDI4F2CUFuxj5ECxrlIfm_tlv0TbErDTX311YsA_nhzNHYSaffRzx0OkmmUKSyyH8k9aPRKXUTUmY7Y9PLv3UCKEmAFHAnJkr5kZV08g3UMYG2blpryYBg82abEVMxeMUbh-T4M-Z9dcgQyiZ4nyNMUs1bbfH_2kAtqfEXmP_9eZ42Kwa2ixFWFZDcvOp775bjkYcGvwSnHqmyBXivONzTxyPN6Ug7uFCvMTbeo10ctgOFfXJUZfoxRt-hCspTPJR8C4TzIK41fiy19uRpGjeezG5Ghwy9upXsomunwB4knTAn1otmj4afIxw",
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2Njc5OTM4NzYsImlhdCI6MTY2Nzk5MzU3NiwianRpIjoiNTdhOWRiYTYtYzExYy00NGQzLWIzNzItNTQ1MmZjYTk5OTc3IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiI5NDY5OTJmNy03MDJhLTQ1NzYtYWI5Yi03MGM5Yzk1MzkwOTIiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImM0cG9fdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjNHBvX2xvY2FsIjp7InJvbGVzIjpbInVzZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InR0dCIsImdpdmVuX25hbWUiOiJ0ZXN0IiwiZmFtaWx5X25hbWUiOiJ1c2VyIn0.r9EHKziADP6FYV2dfUszNB3Mrm6HwJc5pFWzx-bZ83HGGZ7NRCMkmHhLjAZUdnLcQYDikxzg88KXOM5H9i_0RXtQTgwhYfhuAiOelJTQ8a4YHq0t5vNbG9XmIymtGU5wdiTMM0Z8Dz85sxB9dAl5uKDCh5Eo3gA3r42kJ4reFzU_ldjYGZf7J0yskgGv_JCn9MXYWW7Zp0StegE_XMF1Fl3yWE67uxHOd_fOQExbmGohP9fSmzjAaMfvCt3XtqP2oi9BXuV04zbvqP7-9r2yt58vpyQbRPy-xRgxTIU0wwmnDavKVoji2e8rNaSEuvr_Tu_PJ69uUzBu36vpA4aMxQ",
"type": "string"
},
{
@ -300,6 +300,78 @@
}
},
"response": []
},
{
"name": "savePentest",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NjgxNzYyNDYsImlhdCI6MTY2ODE3NTk0NiwianRpIjoiNTFmZWE5YjYtMGY3OS00M2QxLWI1YmItNmEyOTRhMjQyZDIxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiI1NjU5ZDIyMS0yODdiLTQ1ZjktODUzMS00M2I3ZGNhOTExMmUiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImM0cG9fdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjNHBvX2xvY2FsIjp7InJvbGVzIjpbInVzZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InR0dCIsImdpdmVuX25hbWUiOiJ0ZXN0IiwiZmFtaWx5X25hbWUiOiJ1c2VyIn0.D30yLd7T0Qu4GgEYFbaVQXXqNrC_xJeMqaoBZxh4O0KR_hjn7Udsgdkrb8cs4kQinDasOiLaFBABKSF5pQNXJS_yeRsVW-D4_pBY1yD52_rKwHCjNFRkj1ads0CF4h8tHrJhXcBLQKYB9T0F5hu6q5dsP33q1ej25vJm6yoOu2U33TpqLlOOufNLyGJrtdDzpD9BYsAECLboO3X-KneNfYH_Xl7ECXT3hSMnagFchkQ_sDUuurnyBqg-2-sBGFhBgVgb-ku_aiSeZvRvRY9vPPRIyze6r-bgRM28cgjZvjFtMTjiJeFtwnjcEbAOquX2CRqDo7H3GfJGXrqGHrg8tw",
"type": "string"
},
{
"key": "undefined",
"type": "any"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"category\": \"INFORMATION_GATHERING\",\n \"refNumber\": \"OTG-INFO-001\",\n \"status\": \"IN_PROGRESS\",\n \"projectId\": \"5a4f126c-9471-43b8-80b9-6eb02b7c35d0\",\n \"findingIds\": [],\n \"commentIds\": []\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8443/pentests/5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
"protocol": "http",
"host": [
"localhost"
],
"port": "8443",
"path": [
"pentests",
"5a4f126c-9471-43b8-80b9-6eb02b7c35d0"
]
}
},
"response": []
},
{
"name": "updatePentest",
"request": {
"method": "PATCH",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"id\": \"11601f51-bc17-47fd-847d-0c53df5405b5\",\n \"category\": \"INFORMATION_GATHERING\",\n \"refNumber\": \"OTG-INFO-001\",\n \"status\": \"COMPLETED\",\n \"projectId\": \"5a4f126c-9471-43b8-80b9-6eb02b7c35d0\",\n \"findingIds\": [],\n \"commentIds\": []\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8443/pentests/pentestId",
"protocol": "http",
"host": [
"localhost"
],
"port": "8443",
"path": [
"pentests",
"pentestId"
]
}
},
"response": []
}
]
},

View File

@ -34,4 +34,5 @@ enum class Errorcode(val code: Int) {
PentestFetchingFailed(6005),
ProjectInsertionFailed(6006),
PentestInsertionFailed(6007),
ProjectPentestInsertionFailed(6008),
}

View File

@ -9,22 +9,68 @@ data class Pentest(
val id: String = UUID.randomUUID().toString(),
val projectId: String,
val category: PentestCategory,
val title: String,
val refNumber: String,
val status: PentestStatus,
val findingIds: List<String> = emptyList(),
val commentIds: List<String> = emptyList()
)
fun buildPentest(body: PentestRequestBody, pentestEntity: PentestEntity): Pentest {
return Pentest(
id = pentestEntity.data.id,
projectId = body.projectId,
category = PentestCategory.valueOf(body.category),
refNumber = body.refNumber,
status = PentestStatus.valueOf(body.status),
findingIds = body.findingIds,
commentIds = body.commentIds
)
}
fun Pentest.toPentestResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"projectId" to projectId,
"category" to category,
"title" to title,
"refNumber" to refNumber,
"status" to status,
"findingIds" to findingIds,
"commentIds" to commentIds
)
}
data class PentestRequestBody(
val projectId: String,
val refNumber: String,
val category: String,
val status: String,
val findingIds: List<String>,
val commentIds: List<String>
)
/**
* Validates if a [PentestRequestBody] is valid
*
* @return Boolean describing if the body is valid
*/
fun PentestRequestBody.isValid(): Boolean {
return when {
this.projectId.isBlank() -> false
this.refNumber.isBlank() -> false
this.category.isBlank() -> false
this.status.isBlank() -> false
else -> true
}
}
fun PentestRequestBody.toPentest(): Pentest {
return Pentest(
id = UUID.randomUUID().toString(),
projectId = this.projectId,
category = PentestCategory.valueOf(this.category),
refNumber = this.refNumber,
status = PentestStatus.valueOf(this.status),
findingIds = this.findingIds,
commentIds = this.commentIds
)
}

View File

@ -15,7 +15,7 @@ import reactor.core.publisher.Mono
origins = [],
allowCredentials = "false",
allowedHeaders = ["*"],
methods = [RequestMethod.GET]
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
)
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
@ -28,7 +28,7 @@ class PentestController(private val pentestService: PentestService) {
@RequestParam("projectId") projectId: String,
@RequestParam("category") category: String
): Mono<ResponseEntity<List<ResponseBody>>> {
return pentestService.getPentests(projectId, PentestCategory.valueOf(category)).map { pentestList ->
return pentestService.getPentestsForCategory(projectId, PentestCategory.valueOf(category)).map { pentestList ->
pentestList.map {
it.toPentestResponseBody()
}
@ -37,4 +37,36 @@ class PentestController(private val pentestService: PentestService) {
else ResponseEntity.ok(it)
}
}
/* Todo: Add API
@GetMapping
fun getPentestById(
@RequestParam("pentestId") pentestId: String
): Mono<ResponseEntity<List<ResponseBody>>> {
return pentestService.getPentest(pentestId).map {
ResponseEntity.ok(it)
}
}*/
// ToDo: Add Documentation & Tests
@PostMapping("/{projectId}")
fun savePentest(
@PathVariable(value = "projectId") projectId: String,
@RequestBody body: PentestRequestBody
): Mono<ResponseEntity<ResponseBody>> {
return this.pentestService.savePentest(projectId, body).map {
ResponseEntity.accepted().body(it.toPentestResponseBody())
}
}
// ToDo: Add Documentation & Tests
@PatchMapping("/{pentestId}")
fun updatePentest(
@PathVariable(value = "pentestId") pentestId: String,
@RequestBody body: PentestRequestBody
): Mono<ResponseEntity<ResponseBody>> {
return this.pentestService.updatePentest(pentestId, body).map {
ResponseEntity.accepted().body(it.toPentestResponseBody())
}
}
}

View File

@ -16,7 +16,6 @@ fun PentestEntity.toPentest(): Pentest {
this.data.id,
this.data.projectId,
this.data.category,
this.data.title,
this.data.refNumber,
this.data.status,
this.data.findingIds,

View File

@ -4,6 +4,7 @@ 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
interface PentestRepository : ReactiveMongoRepository<PentestEntity, String> {
@ -11,4 +12,6 @@ interface PentestRepository : ReactiveMongoRepository<PentestEntity, String> {
@Query("{'data.projectId': ?0, 'data.category': ?1}")
fun findPentestByProjectIdAndCategory(projectId: String, category: PentestCategory): Flux<PentestEntity>
@Query("{'data._id' : ?0}")
fun findPentestById(id: String): Mono<PentestEntity>
}

View File

@ -2,14 +2,20 @@ package com.securityc4po.api.pentest
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.error.handler.*
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.project.*
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty
import java.time.Instant
@Service
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestService(private val pentestRepository: PentestRepository) {
class PentestService(private val pentestRepository: PentestRepository, private val projectService: ProjectService) {
var logger = getLoggerFor<PentestService>()
@ -18,9 +24,97 @@ class PentestService(private val pentestRepository: PentestRepository) {
*
* @return list of [Pentest]
*/
fun getPentests(projectId: String, category: PentestCategory): Mono<List<Pentest>> {
fun getPentestsForCategory(projectId: String, category: PentestCategory): Mono<List<Pentest>> {
return pentestRepository.findPentestByProjectIdAndCategory(projectId, category).collectList().map {
it.map { pentestEntity -> pentestEntity.toPentest() }
}
}
/**
* Save [Pentest]
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be stored
* @return saved [Pentest]
*/
fun savePentest(projectId: String, body: PentestRequestBody): Mono<Pentest> {
validate(
require = body.isValid(),
logging = { logger.warn("Pentest not valid.") },
mappedException = InvalidModelException(
"Pentest not valid.", Errorcode.PentestInvalid
)
)
val pentest = body.toPentest()
val pentestEntity = PentestEntity(pentest)
return pentestRepository.insert(pentestEntity).flatMap { newPentestEntity: PentestEntity ->
val pentest = newPentestEntity.toPentest()
// After successfully saving pentest add id and status to project
val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status)
projectService.updateProjectTestingProgress(projectId, projectPentest).onErrorMap {
TransactionInterruptedException(
"Project Pentests could not be updated in Database.",
Errorcode.ProjectPentestInsertionFailed
)
}.map {
pentest
}
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be stored in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be stored.",
Errorcode.PentestInsertionFailed
)
)
}
}
/**
* Update [Pentest]
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
* @return updated [Pentest]
*/
fun updatePentest(pentestId: String, body: PentestRequestBody): Mono<Pentest> {
validate(
require = body.isValid(),
logging = { logger.warn("Pentest not valid.") },
mappedException = InvalidModelException(
"Pentest not valid.", Errorcode.PentestInvalid
)
)
return pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Updating not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.flatMap { currentPentestEntity: PentestEntity ->
currentPentestEntity.lastModified = Instant.now()
currentPentestEntity.data = buildPentest(body, currentPentestEntity)
pentestRepository.save(currentPentestEntity).flatMap {newPentestEntity: PentestEntity ->
val pentest = newPentestEntity.toPentest()
// After successfully saving pentest add id and status to project
val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status)
projectService.updateProjectTestingProgress(body.projectId, projectPentest).onErrorMap {
TransactionInterruptedException(
"Project Pentest could not be updated in Database.",
Errorcode.ProjectPentestInsertionFailed
)
}.map {
return@map newPentestEntity.toPentest()
}
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be updated.",
Errorcode.PentestInsertionFailed
)
)
}
}
}
}

View File

@ -2,6 +2,7 @@ package com.securityc4po.api.pentest
enum class PentestStatus {
NOT_STARTED,
DISABLED,
OPEN,
IN_PROGRESS,
COMPLETED

View File

@ -3,10 +3,9 @@ package com.securityc4po.api.project
import com.fasterxml.jackson.annotation.JsonFormat
import com.securityc4po.api.ResponseBody
import com.securityc4po.api.pentest.PentestStatus
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.mongodb.core.index.Indexed
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.time.Instant
import java.util.UUID
@ -18,11 +17,11 @@ data class Project(
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
val createdAt: String = Instant.now().toString(),
val tester: String? = null,
val projectPentests: List<ProjectPentest> = emptyList(),
var projectPentests: List<ProjectPentest> = emptyList(),
val createdBy: String
)
fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Project{
fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Project {
return Project(
id = projectEntity.data.id,
client = body.client,
@ -36,14 +35,14 @@ fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Projec
fun Project.toProjectResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt,
"tester" to tester,
/* ToDo: Calculate percentage in BE type: float */
"testingProgress" to calculateProgress(),
"createdBy" to createdBy
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt,
"tester" to tester,
/* ToDo: Calculate percentage in BE type: float */
"testingProgress" to calculateProgress(),
"createdBy" to createdBy
)
}
@ -54,32 +53,32 @@ fun Project.toProjectDeleteResponseBody(): ResponseBody {
}
fun Project.calculateProgress(): Float {
fun Project.calculateProgress(): BigDecimal {
// Total number of pentests listet in the OWASP testing guide
// https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf
// @Value("\${owasp.web.pentests}")
// lateinit var TOTALPENTESTS: Int
val TOTALPENTESTS = 95
val TOTALPENTESTS = 95.0
return if (projectPentests.isEmpty())
0F
BigDecimal.ZERO
else {
var completedPentests = 0
var completedPentests = 0.0
projectPentests.forEach { projectPentest ->
println(projectPentest.toString())
if (projectPentest.status == PentestStatus.COMPLETED) {
completedPentests++
completedPentests += 1.0
} else if (projectPentest.status != PentestStatus.NOT_STARTED) {
completedPentests += 0.5
}
}
val df = DecimalFormat("#.##")
df.roundingMode = RoundingMode.DOWN
val progress = completedPentests / TOTALPENTESTS
df.format(progress).toFloat()
val progress = (completedPentests * 100) / TOTALPENTESTS
BigDecimal(progress).setScale(2, RoundingMode.HALF_UP)
}
}
data class ProjectOverview(
val projects: List<Project>
val projects: List<Project>
)
data class ProjectRequestBody(
@ -111,6 +110,5 @@ fun ProjectRequestBody.toProject(): Project {
tester = this.tester,
// ToDo: Should be changed to SUB from Token after adding AUTH Header
createdBy = UUID.randomUUID().toString()
)
)
}

View File

@ -4,5 +4,5 @@ import com.securityc4po.api.pentest.PentestStatus
data class ProjectPentest(
val pentestId: String,
val status: PentestStatus
var status: PentestStatus
)

View File

@ -125,15 +125,39 @@ class ProjectService(private val projectRepository: ProjectRepository) {
/**
* Update testing progress of [Project]
* Update testing progress for specific Pentest of [Project]
*
* @throws [TransactionInterruptedException] if the [Project] could not be updated
* @return updated list of [ProjectPentest]s
* @throws [TransactionInterruptedException] if the [Project] Pentest could not be updated
* @return updated [Project]
*/
fun updateProjectTestingProgress(
projectId: String,
projectPentests: ProjectPentest
)/*: Mono<List<ProjectPentest>>*/ {
// ToDo: update Project Entity with progress
projectPentest: ProjectPentest
): Mono<Project> {
return this.projectRepository.findProjectById(projectId).switchIfEmpty {
logger.warn("Project with id $projectId not found. Updating not possible.")
val msg = "Project with id $projectId not found."
val ex = EntityNotFoundException(msg, Errorcode.ProjectNotFound)
throw ex
}.flatMap {projectEntity: ProjectEntity ->
val currentProjectPentestStatus = projectEntity.data.projectPentests.find { projectPentestData -> projectPentestData.pentestId == projectPentest.pentestId }
if (currentProjectPentestStatus !== null) {
projectEntity.data.projectPentests.find { data -> data.pentestId == projectPentest.pentestId }!!.status = projectPentest.status
} else {
projectEntity.data.projectPentests += projectPentest
}
projectEntity.lastModified = Instant.now()
this.projectRepository.save(projectEntity).map {
it.toProject()
}.doOnError {
throw wrappedException(
logging = { logger.warn("Project Pentests could not be updated in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Project could not be updated.",
Errorcode.ProjectInsertionFailed
)
)
}
}
}
}

View File

@ -75,8 +75,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
.description("The id of the project of the requested pentest"),
PayloadDocumentation.fieldWithPath("[].category").type(JsonFieldType.STRING)
.description("The category of the requested pentest"),
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
.description("The title of the requested pentest"),
PayloadDocumentation.fieldWithPath("[].refNumber").type(JsonFieldType.STRING)
.description("The reference number of the requested pentest according to the current OWASP Testing Guide"),
PayloadDocumentation.fieldWithPath("[].status").type(JsonFieldType.STRING)
@ -94,7 +92,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
id = "9c8af320-f608-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Search engine discovery/reconnaissance",
refNumber = "OTG-INFO-001",
status = PentestStatus.NOT_STARTED,
findingIds = emptyList(),
@ -104,7 +101,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
id = "43fbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Fingerprint Web Server",
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = emptyList(),
@ -123,7 +119,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
id = "9c8af320-f608-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Search engine discovery/reconnaissance",
refNumber = "OTG-INFO-001",
status = PentestStatus.NOT_STARTED,
findingIds = emptyList(),
@ -133,7 +128,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
id = "43fbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Fingerprint Web Server",
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = emptyList(),
@ -143,7 +137,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
id = "74eae112-f62c-11ec-b939-0242ac120002",
projectId = "6fad3474-fc29-49f9-bd37-e039e9e60c18",
category = PentestCategory.AUTHENTICATION_TESTING,
title = "Testing for Credentials Transported over an Encrypted Channel",
refNumber = "OTG-AUTHN-001",
status = PentestStatus.COMPLETED,
findingIds = emptyList(),

View File

@ -69,7 +69,6 @@ class PentestControllerIntTest : BaseIntTest() {
id = "9c8af320-f608-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Search engine discovery/reconnaissance",
refNumber = "OTG-INFO-001",
status = PentestStatus.NOT_STARTED,
findingIds = emptyList(),
@ -79,7 +78,6 @@ class PentestControllerIntTest : BaseIntTest() {
id = "43fbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Fingerprint Web Server",
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = emptyList(),
@ -98,7 +96,6 @@ class PentestControllerIntTest : BaseIntTest() {
id = "9c8af320-f608-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Search engine discovery/reconnaissance",
refNumber = "OTG-INFO-001",
status = PentestStatus.NOT_STARTED,
findingIds = emptyList(),
@ -108,7 +105,6 @@ class PentestControllerIntTest : BaseIntTest() {
id = "43fbc63c-f624-11ec-b939-0242ac120002",
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
category = PentestCategory.INFORMATION_GATHERING,
title = "Fingerprint Web Server",
refNumber = "OTG-INFO-002",
status = PentestStatus.IN_PROGRESS,
findingIds = emptyList(),
@ -118,7 +114,6 @@ class PentestControllerIntTest : BaseIntTest() {
id = "74eae112-f62c-11ec-b939-0242ac120002",
projectId = "6fad3474-fc29-49f9-bd37-e039e9e60c18",
category = PentestCategory.AUTHENTICATION_TESTING,
title = "Testing for Credentials Transported over an Encrypted Channel",
refNumber = "OTG-AUTHN-001",
status = PentestStatus.COMPLETED,
findingIds = emptyList(),

View File

@ -0,0 +1,20 @@
[{
"_id": {
"$oid": "636e332392b4c57eb1693c4f"
},
"lastModified": {
"$date": {
"$numberLong": "1668176064712"
}
},
"data": {
"_id": "11601f51-bc17-47fd-847d-0c53df5405b5",
"projectId": "5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
"category": "INFORMATION_GATHERING",
"refNumber": "OTG-INFO-001",
"status": "IN_PROGRESS",
"findingIds": [],
"commentIds": []
},
"_class": "com.securityc4po.api.pentest.PentestEntity"
}]

View File

@ -4,16 +4,21 @@
},
"lastModified": {
"$date": {
"$numberLong": "1660142860140"
"$numberLong": "1668176064717"
}
},
"data": {
"_id": "5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
"client": "E Corp",
"title": "Some Mock API (v1.0) Scanning",
"client": "Dio Stonemask Inc.",
"title": "log4jj bizarre adventure",
"createdAt": "2022-08-10T14:47:40.140406Z",
"tester": "Novatester",
"projectPentests": [],
"tester": "Jojo",
"projectPentests": [
{
"pentestId": "11601f51-bc17-47fd-847d-0c53df5405b5",
"status": "IN_PROGRESS"
}
],
"createdBy": "3c4ae87f-0d56-4634-a824-b4883c403c8a"
},
"_class": "com.securityc4po.api.project.ProjectEntity"
@ -38,21 +43,21 @@
"_class": "com.securityc4po.api.project.ProjectEntity"
},{
"_id": {
"$oid": "62f3c5427acde34f740ba739"
"$oid": "62ff7534ac2b4d14d86215c4"
},
"lastModified": {
"$date": {
"$numberLong": "1660142914204"
"$numberLong": "1660908852340"
}
},
"data": {
"_id": "1120bfa1-0d2b-4e42-a209-0289a1256266",
"_id": "195809ed-9722-4ad5-a84b-0099a9a01652",
"client": "Novatec",
"title": "Openspace log4J",
"createdAt": "2022-08-10T14:48:34.204234Z",
"tester": "mhg",
"title": "log4j pentest",
"createdAt": "2022-08-19T11:34:12.339990Z",
"tester": "Stipe",
"projectPentests": [],
"createdBy": "5a4a8032-0726-4851-a105-9f079c3989b9"
"createdBy": "7fe49c8d-fee3-47e0-9224-94e0ac7436c6"
},
"_class": "com.securityc4po.api.project.ProjectEntity"
}]