feat: As a developer, I want to create a pentest
This commit is contained in:
parent
de659e3293
commit
84b7c1a07d
|
@ -3,12 +3,11 @@ import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@neb
|
||||||
import {Pentest, ObjectiveEntry, transformPentestsToObjectiveEntries} from '@shared/models/pentest.model';
|
import {Pentest, ObjectiveEntry, transformPentestsToObjectiveEntries} from '@shared/models/pentest.model';
|
||||||
import {PentestService} from '@shared/services/pentest.service';
|
import {PentestService} from '@shared/services/pentest.service';
|
||||||
import {Store} from '@ngxs/store';
|
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 {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
|
||||||
import {catchError, switchMap, tap} from 'rxjs/operators';
|
import {catchError, switchMap, tap} from 'rxjs/operators';
|
||||||
import {BehaviorSubject, Observable, of} from 'rxjs';
|
import {BehaviorSubject, Observable, of} from 'rxjs';
|
||||||
import {getTitleKeyForRefNumber} from '@shared/functions/categories/get-title-key-for-ref-number.function';
|
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 {Router} from '@angular/router';
|
||||||
import {ChangePentest} from '@shared/stores/project-state/project-state.actions';
|
import {ChangePentest} from '@shared/stores/project-state/project-state.actions';
|
||||||
|
|
||||||
|
@ -25,6 +24,7 @@ export class ObjectiveTableComponent implements OnInit {
|
||||||
dataSource: NbTreeGridDataSource<ObjectiveEntry>;
|
dataSource: NbTreeGridDataSource<ObjectiveEntry>;
|
||||||
|
|
||||||
private data: ObjectiveEntry[] = [];
|
private data: ObjectiveEntry[] = [];
|
||||||
|
private pentests$: BehaviorSubject<Pentest[]> = new BehaviorSubject<Pentest[]>([]);
|
||||||
|
|
||||||
getters: NbGetters<ObjectiveEntry, ObjectiveEntry> = {
|
getters: NbGetters<ObjectiveEntry, ObjectiveEntry> = {
|
||||||
dataGetter: (node: ObjectiveEntry) => node,
|
dataGetter: (node: ObjectiveEntry) => node,
|
||||||
|
@ -53,6 +53,8 @@ export class ObjectiveTableComponent implements OnInit {
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: (pentests: Pentest[]) => {
|
next: (pentests: Pentest[]) => {
|
||||||
|
// ToDo: Change assignement here
|
||||||
|
this.pentests$.next(pentests);
|
||||||
this.data = transformPentestsToObjectiveEntries(pentests);
|
this.data = transformPentestsToObjectiveEntries(pentests);
|
||||||
this.dataSource.setData(this.data, this.getters);
|
this.dataSource.setData(this.data, this.getters);
|
||||||
this.loading$.next(false);
|
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
|
/* ToDo: Include again after fixing pentest route
|
||||||
this.router.navigate([Route.PENTEST])
|
this.router.navigate([Route.PENTEST])
|
||||||
.then(
|
.then(
|
||||||
|
@ -74,7 +76,8 @@ export class ObjectiveTableComponent implements OnInit {
|
||||||
})
|
})
|
||||||
).finally();
|
).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
|
// HTML only
|
||||||
|
|
|
@ -40,8 +40,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
refNumber: 'OTF-001',
|
refNumber: 'OTF-001',
|
||||||
childEntries: [],
|
childEntries: [],
|
||||||
status: PentestStatus.NOT_STARTED,
|
status: PentestStatus.NOT_STARTED,
|
||||||
findingsIds: [],
|
findingIds: [],
|
||||||
commentsIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112']
|
commentIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112']
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {Pentest} from '@shared/models/pentest.model';
|
||||||
import * as FA from '@fortawesome/free-solid-svg-icons';
|
import * as FA from '@fortawesome/free-solid-svg-icons';
|
||||||
import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@nebular/theme';
|
import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@nebular/theme';
|
||||||
import {PentestService} from '@shared/services/pentest.service';
|
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 {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
|
||||||
import {filter, tap} from 'rxjs/operators';
|
import {filter, tap} from 'rxjs/operators';
|
||||||
import {Comment, CommentEntry, transformCommentsToObjectiveEntries} from '@shared/models/comment.model';
|
import {Comment, CommentEntry, transformCommentsToObjectiveEntries} from '@shared/models/comment.model';
|
||||||
|
@ -63,8 +63,9 @@ export class PentestCommentsComponent implements OnInit {
|
||||||
this.loading$.next(false);
|
this.loading$.next(false);
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
this.notificationService.showPopup('comment.popup.not.found', PopupType.FAILURE);
|
// ToDo: Implement again after proper lazy loading and routing
|
||||||
|
// this.notificationService.showPopup('comment.popup.not.found', PopupType.FAILURE);
|
||||||
this.loading$.next(false);
|
this.loading$.next(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,14 +27,27 @@
|
||||||
</nb-option>
|
</nb-option>
|
||||||
</nb-select>
|
</nb-select>
|
||||||
</div>
|
</div>
|
||||||
<button nbButton
|
<div *ngIf="!pentest$.getValue().id; else updatePentest">
|
||||||
class="save-pentest-button"
|
<button nbButton
|
||||||
status="primary"
|
class="save-pentest-button"
|
||||||
[disabled]="!pentestStatusChanged()"
|
status="primary"
|
||||||
title="{{ 'global.action.save' | translate }}"
|
[disabled]="!pentestStatusChanged()"
|
||||||
(click)="onClickSavePentest()">
|
title="{{ 'global.action.save' | translate }}"
|
||||||
<span class="exit-element-text"> {{ 'global.action.save' | translate }} </span>
|
(click)="onClickSavePentest()">
|
||||||
</button>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||||
import {Category} from '@shared/models/category.model';
|
import {Category} from '@shared/models/category.model';
|
||||||
import {PentestStatus} from '@shared/models/pentest-status.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 = {
|
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
selectedProject: {
|
selectedProject: {
|
||||||
|
@ -33,8 +35,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
refNumber: 'OTF-001',
|
refNumber: 'OTF-001',
|
||||||
childEntries: [],
|
childEntries: [],
|
||||||
status: PentestStatus.NOT_STARTED,
|
status: PentestStatus.NOT_STARTED,
|
||||||
findingsIds: [],
|
findingIds: [],
|
||||||
commentsIds: []
|
commentIds: []
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,6 +62,9 @@ describe('PentestContentComponent', () => {
|
||||||
}),
|
}),
|
||||||
RouterTestingModule.withRoutes([]),
|
RouterTestingModule.withRoutes([]),
|
||||||
NgxsModule.forRoot([ProjectState])
|
NgxsModule.forRoot([ProjectState])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{provide: NotificationService, useValue: new NotificationServiceMock()}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import * as FA from '@fortawesome/free-solid-svg-icons';
|
import * as FA from '@fortawesome/free-solid-svg-icons';
|
||||||
import {BehaviorSubject} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {Store} from '@ngxs/store';
|
import {Select, Store} from '@ngxs/store';
|
||||||
import {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 {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 {PentestStatus} from '@shared/models/pentest-status.model';
|
||||||
import {StatusText} from '@shared/widgets/status-tag/status-tag.component';
|
import {StatusText} from '@shared/widgets/status-tag/status-tag.component';
|
||||||
import {PentestService} from '@shared/services/pentest.service';
|
import {PentestService} from '@shared/services/pentest.service';
|
||||||
import {NotificationService, PopupType} from '@shared/services/notification.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()
|
@UntilDestroy()
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -20,6 +23,10 @@ export class PentestContentComponent implements OnInit {
|
||||||
// HTML only
|
// HTML only
|
||||||
readonly fa = FA;
|
readonly fa = FA;
|
||||||
|
|
||||||
|
@Select(ProjectState.project)
|
||||||
|
selectedProject$: Observable<Project>;
|
||||||
|
selectedProjectId: string;
|
||||||
|
|
||||||
pentest$: BehaviorSubject<Pentest> = new BehaviorSubject<Pentest>(null);
|
pentest$: BehaviorSubject<Pentest> = new BehaviorSubject<Pentest>(null);
|
||||||
pentestChanged$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
pentestChanged$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
currentNumberOfFindings$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
currentNumberOfFindings$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
||||||
|
@ -45,16 +52,29 @@ export class PentestContentComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
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(
|
this.store.selectOnce(ProjectState.pentest).pipe(
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: (selectedPentest: Pentest) => {
|
next: (selectedPentest: Pentest) => {
|
||||||
|
console.warn(selectedPentest);
|
||||||
this.pentest$.next(selectedPentest);
|
this.pentest$.next(selectedPentest);
|
||||||
this.currentStatus = selectedPentest.status;
|
this.currentStatus = selectedPentest.status;
|
||||||
this.initialPentestStatus = 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);
|
this.currentNumberOfFindings$.next(findings);
|
||||||
const comments = selectedPentest.commentsIds ? selectedPentest.commentsIds.length : 0;
|
const comments = selectedPentest.commentIds ? selectedPentest.commentIds.length : 0;
|
||||||
this.currentNumberOfComments$.next(comments);
|
this.currentNumberOfComments$.next(comments);
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
|
@ -65,10 +85,11 @@ export class PentestContentComponent implements OnInit {
|
||||||
|
|
||||||
onClickSavePentest(): void {
|
onClickSavePentest(): void {
|
||||||
this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus});
|
this.pentest$.next({...this.pentest$.getValue(), status: this.currentStatus});
|
||||||
console.warn('Updated Pentest: ', this.pentest$.getValue());
|
this.pentestService.savePentest(this.selectedProjectId, transformPentestToRequestBody(this.pentest$.getValue()))
|
||||||
this.pentestService.savePentest(this.pentest$.getValue())
|
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (pentest: Pentest) => {
|
next: (pentest: Pentest) => {
|
||||||
|
this.pentest$.next(pentest);
|
||||||
|
this.initialPentestStatus = pentest.status;
|
||||||
this.notificationService.showPopup('pentest.popup.save.success', PopupType.SUCCESS);
|
this.notificationService.showPopup('pentest.popup.save.success', PopupType.SUCCESS);
|
||||||
},
|
},
|
||||||
error: err => {
|
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
|
* @return true if initial pentest Status is different from current pentest status
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -42,8 +42,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
refNumber: 'OTF-001',
|
refNumber: 'OTF-001',
|
||||||
childEntries: [],
|
childEntries: [],
|
||||||
status: PentestStatus.NOT_STARTED,
|
status: PentestStatus.NOT_STARTED,
|
||||||
findingsIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
|
findingIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
|
||||||
commentsIds: []
|
commentIds: []
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,9 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
this.loading$.next(false);
|
this.loading$.next(false);
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
this.notificationService.showPopup('findings.popup.not.found', PopupType.FAILURE);
|
// ToDo: Implement again after proper lazy loading and routing
|
||||||
|
// this.notificationService.showPopup('findings.popup.not.found', PopupType.FAILURE);
|
||||||
this.loading$.next(false);
|
this.loading$.next(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -94,8 +95,8 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
this.loadFindingsData();
|
this.loadFindingsData();
|
||||||
this.notificationService.showPopup('finding.popup.save.success', PopupType.SUCCESS);
|
this.notificationService.showPopup('finding.popup.save.success', PopupType.SUCCESS);
|
||||||
},
|
},
|
||||||
error: error => {
|
error: err => {
|
||||||
console.error(error);
|
console.error(err);
|
||||||
this.notificationService.showPopup('finding.popup.save.failed', PopupType.FAILURE);
|
this.notificationService.showPopup('finding.popup.save.failed', PopupType.FAILURE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,8 +51,8 @@ describe('PentestInfoComponent', () => {
|
||||||
refNumber: 'OTF-001',
|
refNumber: 'OTF-001',
|
||||||
childEntries: [],
|
childEntries: [],
|
||||||
status: PentestStatus.NOT_STARTED,
|
status: PentestStatus.NOT_STARTED,
|
||||||
findingsIds: [],
|
findingIds: [],
|
||||||
commentsIds: []
|
commentIds: []
|
||||||
});
|
});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,8 +33,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
refNumber: 'OTF-001',
|
refNumber: 'OTF-001',
|
||||||
childEntries: [],
|
childEntries: [],
|
||||||
status: PentestStatus.NOT_STARTED,
|
status: PentestStatus.NOT_STARTED,
|
||||||
findingsIds: [],
|
findingIds: [],
|
||||||
commentsIds: []
|
commentIds: []
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,13 +36,11 @@
|
||||||
<nb-card-footer>
|
<nb-card-footer>
|
||||||
<div fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="start end">
|
<div fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="start end">
|
||||||
<div class="project-progress">
|
<div class="project-progress">
|
||||||
|
|
||||||
<nb-progress-bar *ngIf="project.testingProgress > 0; else altProgressBar"
|
<nb-progress-bar *ngIf="project.testingProgress > 0; else altProgressBar"
|
||||||
status="warning"
|
status="warning"
|
||||||
[value]="project.testingProgress"
|
[value]="project.testingProgress"
|
||||||
[displayValue]="true">
|
[displayValue]="true">
|
||||||
</nb-progress-bar>
|
</nb-progress-bar>
|
||||||
|
|
||||||
<ng-template #altProgressBar>
|
<ng-template #altProgressBar>
|
||||||
{{'popup.info' | translate}} {{'global.no.progress' | translate}}
|
{{'popup.info' | translate}} {{'global.no.progress' | translate}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
import {v4 as UUID} from 'uuid';
|
|
||||||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||||
import {Category} from '@shared/models/category.model';
|
import {Category} from '@shared/models/category.model';
|
||||||
|
import {v4 as UUID} from 'uuid';
|
||||||
|
|
||||||
export class Pentest {
|
export class Pentest {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
projectId?: string;
|
||||||
category: Category;
|
category: Category;
|
||||||
refNumber: string;
|
refNumber: string;
|
||||||
childEntries?: Pentest[];
|
childEntries?: Pentest[];
|
||||||
status: PentestStatus;
|
status: PentestStatus;
|
||||||
findingsIds?: Array<string>;
|
findingIds?: Array<string>;
|
||||||
commentsIds?: Array<string>;
|
commentIds?: Array<string>;
|
||||||
|
|
||||||
constructor(category: Category,
|
constructor(category: Category,
|
||||||
refNumber: string,
|
refNumber: string,
|
||||||
status: PentestStatus,
|
status: PentestStatus,
|
||||||
id?: string,
|
id?: string,
|
||||||
|
projectId?: string,
|
||||||
findingsIds?: Array<string>,
|
findingsIds?: Array<string>,
|
||||||
commentsIds?: Array<string>) {
|
commentsIds?: Array<string>) {
|
||||||
this.id = id ? id : UUID();
|
this.id = id ? id : UUID();
|
||||||
|
this.projectId = projectId ? projectId : '';
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.refNumber = refNumber;
|
this.refNumber = refNumber;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.findingsIds = findingsIds ? findingsIds : [];
|
this.findingIds = findingsIds ? findingsIds : [];
|
||||||
this.commentsIds = commentsIds ? commentsIds : [];
|
this.commentIds = commentsIds ? commentsIds : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,13 +38,31 @@ export interface ObjectiveEntry {
|
||||||
expanded?: boolean;
|
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[] {
|
export function transformPentestsToObjectiveEntries(pentests: Pentest[]): ObjectiveEntry[] {
|
||||||
const objectiveEntries: ObjectiveEntry[] = [];
|
const objectiveEntries: ObjectiveEntry[] = [];
|
||||||
pentests.forEach((value: Pentest) => {
|
pentests.forEach((value: Pentest) => {
|
||||||
objectiveEntries.push({
|
objectiveEntries.push({
|
||||||
refNumber: value.refNumber,
|
refNumber: value.refNumber,
|
||||||
status: value.status,
|
status: value.status,
|
||||||
findings: value.findingsIds ? value.findingsIds.length : 0,
|
findings: value.findingIds ? value.findingIds.length : 0,
|
||||||
kind: value.childEntries ? 'dir' : 'cell',
|
kind: value.childEntries ? 'dir' : 'cell',
|
||||||
childEntries: value.childEntries ? value.childEntries : null,
|
childEntries: value.childEntries ? value.childEntries : null,
|
||||||
expanded: !!value.childEntries
|
expanded: !!value.childEntries
|
||||||
|
|
|
@ -52,8 +52,8 @@ const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
refNumber: 'OTF-001',
|
refNumber: 'OTF-001',
|
||||||
childEntries: [],
|
childEntries: [],
|
||||||
status: PentestStatus.NOT_STARTED,
|
status: PentestStatus.NOT_STARTED,
|
||||||
findingsIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
|
findingIds: ['56c47c56-3bcd-45f1-a05b-c197dbd33112'],
|
||||||
commentsIds: []
|
commentIds: []
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-
|
||||||
import {Finding, FindingDialogBody} from '@shared/models/finding.model';
|
import {Finding, FindingDialogBody} from '@shared/models/finding.model';
|
||||||
import {Severity} from '@shared/models/severity.enum';
|
import {Severity} from '@shared/models/severity.enum';
|
||||||
import {Comment} from '@shared/models/comment.model';
|
import {Comment} from '@shared/models/comment.model';
|
||||||
|
import {v4 as UUID} from 'uuid';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -29,17 +30,26 @@ export class PentestService {
|
||||||
* @param category the categories of which the pentests should be requested
|
* @param category the categories of which the pentests should be requested
|
||||||
*/
|
*/
|
||||||
public loadPentests(category: Category): Observable<Pentest[]> {
|
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)),
|
switchMap(project => this.getPentestByProjectIdAndCategory(project.id, category)),
|
||||||
catchError(_ => of(null)),
|
catchError(_ => of(null)),
|
||||||
map(response => {
|
map((response: Pentest[]) => {
|
||||||
let pentests = response;
|
// ToDo: Improve performance by only loading templates when not all pentests of category got returned
|
||||||
if (!pentests) {
|
// Load template pentest
|
||||||
pentests = getTempPentestsForCategory(category);
|
const templatePentests = getTempPentestsForCategory(category);
|
||||||
// tslint:disable-next-line:no-console
|
// The pentests that get returned to the component
|
||||||
console.info('Initial pentest data loaded.');
|
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
|
* Save Pentest
|
||||||
* @param pentest the information of the Pentest
|
* @param pentest the information of the Pentest
|
||||||
*/
|
*/
|
||||||
public savePentest(pentest: Pentest): Observable<Pentest> {
|
public savePentest(projectId: string, pentest: Pentest): Observable<Pentest> {
|
||||||
return this.http.post<Pentest>(`${this.apiBaseURL}/${pentest.id}`, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -77,7 +77,7 @@ export class ProjectState {
|
||||||
changePentest(ctx: StateContext<ProjectStateModel>, {pentest}: ChangePentest): void {
|
changePentest(ctx: StateContext<ProjectStateModel>, {pentest}: ChangePentest): void {
|
||||||
const state = ctx.getState();
|
const state = ctx.getState();
|
||||||
ctx.patchState({
|
ctx.patchState({
|
||||||
selectedPentest: pentest
|
selectedPentest: {...pentest, projectId: state.selectedProject.id}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"info": {
|
"info": {
|
||||||
"_postman_id": "58adc500-c0c6-47f3-b268-5fcc16e0944d",
|
"_postman_id": "6f244dd9-5264-497a-9ea4-1ae73e172624",
|
||||||
"name": "security-c4po-api",
|
"name": "security-c4po-api",
|
||||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||||
"_exporter_id": "5225213"
|
"_exporter_id": "5225213"
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
"bearer": [
|
"bearer": [
|
||||||
{
|
{
|
||||||
"key": "token",
|
"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"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -300,6 +300,78 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"response": []
|
"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": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,4 +34,5 @@ enum class Errorcode(val code: Int) {
|
||||||
PentestFetchingFailed(6005),
|
PentestFetchingFailed(6005),
|
||||||
ProjectInsertionFailed(6006),
|
ProjectInsertionFailed(6006),
|
||||||
PentestInsertionFailed(6007),
|
PentestInsertionFailed(6007),
|
||||||
|
ProjectPentestInsertionFailed(6008),
|
||||||
}
|
}
|
|
@ -9,22 +9,68 @@ data class Pentest(
|
||||||
val id: String = UUID.randomUUID().toString(),
|
val id: String = UUID.randomUUID().toString(),
|
||||||
val projectId: String,
|
val projectId: String,
|
||||||
val category: PentestCategory,
|
val category: PentestCategory,
|
||||||
val title: String,
|
|
||||||
val refNumber: String,
|
val refNumber: String,
|
||||||
val status: PentestStatus,
|
val status: PentestStatus,
|
||||||
val findingIds: List<String> = emptyList(),
|
val findingIds: List<String> = emptyList(),
|
||||||
val commentIds: 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 {
|
fun Pentest.toPentestResponseBody(): ResponseBody {
|
||||||
return mapOf(
|
return mapOf(
|
||||||
"id" to id,
|
"id" to id,
|
||||||
"projectId" to projectId,
|
"projectId" to projectId,
|
||||||
"category" to category,
|
"category" to category,
|
||||||
"title" to title,
|
|
||||||
"refNumber" to refNumber,
|
"refNumber" to refNumber,
|
||||||
"status" to status,
|
"status" to status,
|
||||||
"findingIds" to findingIds,
|
"findingIds" to findingIds,
|
||||||
"commentIds" to commentIds
|
"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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import reactor.core.publisher.Mono
|
||||||
origins = [],
|
origins = [],
|
||||||
allowCredentials = "false",
|
allowCredentials = "false",
|
||||||
allowedHeaders = ["*"],
|
allowedHeaders = ["*"],
|
||||||
methods = [RequestMethod.GET]
|
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
|
||||||
)
|
)
|
||||||
|
|
||||||
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
||||||
|
@ -28,7 +28,7 @@ class PentestController(private val pentestService: PentestService) {
|
||||||
@RequestParam("projectId") projectId: String,
|
@RequestParam("projectId") projectId: String,
|
||||||
@RequestParam("category") category: String
|
@RequestParam("category") category: String
|
||||||
): Mono<ResponseEntity<List<ResponseBody>>> {
|
): Mono<ResponseEntity<List<ResponseBody>>> {
|
||||||
return pentestService.getPentests(projectId, PentestCategory.valueOf(category)).map { pentestList ->
|
return pentestService.getPentestsForCategory(projectId, PentestCategory.valueOf(category)).map { pentestList ->
|
||||||
pentestList.map {
|
pentestList.map {
|
||||||
it.toPentestResponseBody()
|
it.toPentestResponseBody()
|
||||||
}
|
}
|
||||||
|
@ -37,4 +37,36 @@ class PentestController(private val pentestService: PentestService) {
|
||||||
else ResponseEntity.ok(it)
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ fun PentestEntity.toPentest(): Pentest {
|
||||||
this.data.id,
|
this.data.id,
|
||||||
this.data.projectId,
|
this.data.projectId,
|
||||||
this.data.category,
|
this.data.category,
|
||||||
this.data.title,
|
|
||||||
this.data.refNumber,
|
this.data.refNumber,
|
||||||
this.data.status,
|
this.data.status,
|
||||||
this.data.findingIds,
|
this.data.findingIds,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.springframework.data.mongodb.repository.Query
|
||||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
interface PentestRepository : ReactiveMongoRepository<PentestEntity, String> {
|
interface PentestRepository : ReactiveMongoRepository<PentestEntity, String> {
|
||||||
|
@ -11,4 +12,6 @@ interface PentestRepository : ReactiveMongoRepository<PentestEntity, String> {
|
||||||
@Query("{'data.projectId': ?0, 'data.category': ?1}")
|
@Query("{'data.projectId': ?0, 'data.category': ?1}")
|
||||||
fun findPentestByProjectIdAndCategory(projectId: String, category: PentestCategory): Flux<PentestEntity>
|
fun findPentestByProjectIdAndCategory(projectId: String, category: PentestCategory): Flux<PentestEntity>
|
||||||
|
|
||||||
|
@Query("{'data._id' : ?0}")
|
||||||
|
fun findPentestById(id: String): Mono<PentestEntity>
|
||||||
}
|
}
|
|
@ -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.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
||||||
import com.securityc4po.api.configuration.MESSAGE_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.extensions.getLoggerFor
|
||||||
|
import com.securityc4po.api.project.*
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.kotlin.core.publisher.switchIfEmpty
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
@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>()
|
var logger = getLoggerFor<PentestService>()
|
||||||
|
|
||||||
|
@ -18,9 +24,97 @@ class PentestService(private val pentestRepository: PentestRepository) {
|
||||||
*
|
*
|
||||||
* @return list of [Pentest]
|
* @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 {
|
return pentestRepository.findPentestByProjectIdAndCategory(projectId, category).collectList().map {
|
||||||
it.map { pentestEntity -> pentestEntity.toPentest() }
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package com.securityc4po.api.pentest
|
||||||
|
|
||||||
enum class PentestStatus {
|
enum class PentestStatus {
|
||||||
NOT_STARTED,
|
NOT_STARTED,
|
||||||
|
DISABLED,
|
||||||
OPEN,
|
OPEN,
|
||||||
IN_PROGRESS,
|
IN_PROGRESS,
|
||||||
COMPLETED
|
COMPLETED
|
||||||
|
|
|
@ -3,10 +3,9 @@ package com.securityc4po.api.project
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat
|
import com.fasterxml.jackson.annotation.JsonFormat
|
||||||
import com.securityc4po.api.ResponseBody
|
import com.securityc4po.api.ResponseBody
|
||||||
import com.securityc4po.api.pentest.PentestStatus
|
import com.securityc4po.api.pentest.PentestStatus
|
||||||
import org.springframework.beans.factory.annotation.Value
|
|
||||||
import org.springframework.data.mongodb.core.index.Indexed
|
import org.springframework.data.mongodb.core.index.Indexed
|
||||||
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.text.DecimalFormat
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
@ -18,11 +17,11 @@ data class Project(
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
|
||||||
val createdAt: String = Instant.now().toString(),
|
val createdAt: String = Instant.now().toString(),
|
||||||
val tester: String? = null,
|
val tester: String? = null,
|
||||||
val projectPentests: List<ProjectPentest> = emptyList(),
|
var projectPentests: List<ProjectPentest> = emptyList(),
|
||||||
val createdBy: String
|
val createdBy: String
|
||||||
)
|
)
|
||||||
|
|
||||||
fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Project{
|
fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Project {
|
||||||
return Project(
|
return Project(
|
||||||
id = projectEntity.data.id,
|
id = projectEntity.data.id,
|
||||||
client = body.client,
|
client = body.client,
|
||||||
|
@ -36,14 +35,14 @@ fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Projec
|
||||||
|
|
||||||
fun Project.toProjectResponseBody(): ResponseBody {
|
fun Project.toProjectResponseBody(): ResponseBody {
|
||||||
return mapOf(
|
return mapOf(
|
||||||
"id" to id,
|
"id" to id,
|
||||||
"client" to client,
|
"client" to client,
|
||||||
"title" to title,
|
"title" to title,
|
||||||
"createdAt" to createdAt,
|
"createdAt" to createdAt,
|
||||||
"tester" to tester,
|
"tester" to tester,
|
||||||
/* ToDo: Calculate percentage in BE type: float */
|
/* ToDo: Calculate percentage in BE type: float */
|
||||||
"testingProgress" to calculateProgress(),
|
"testingProgress" to calculateProgress(),
|
||||||
"createdBy" to createdBy
|
"createdBy" to createdBy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,32 +53,32 @@ fun Project.toProjectDeleteResponseBody(): ResponseBody {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Project.calculateProgress(): BigDecimal {
|
||||||
fun Project.calculateProgress(): Float {
|
|
||||||
// Total number of pentests listet in the OWASP testing guide
|
// 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
|
// https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf
|
||||||
// @Value("\${owasp.web.pentests}")
|
// @Value("\${owasp.web.pentests}")
|
||||||
// lateinit var TOTALPENTESTS: Int
|
// lateinit var TOTALPENTESTS: Int
|
||||||
val TOTALPENTESTS = 95
|
val TOTALPENTESTS = 95.0
|
||||||
|
|
||||||
return if (projectPentests.isEmpty())
|
return if (projectPentests.isEmpty())
|
||||||
0F
|
BigDecimal.ZERO
|
||||||
else {
|
else {
|
||||||
var completedPentests = 0
|
var completedPentests = 0.0
|
||||||
projectPentests.forEach { projectPentest ->
|
projectPentests.forEach { projectPentest ->
|
||||||
|
println(projectPentest.toString())
|
||||||
if (projectPentest.status == PentestStatus.COMPLETED) {
|
if (projectPentest.status == PentestStatus.COMPLETED) {
|
||||||
completedPentests++
|
completedPentests += 1.0
|
||||||
|
} else if (projectPentest.status != PentestStatus.NOT_STARTED) {
|
||||||
|
completedPentests += 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val df = DecimalFormat("#.##")
|
val progress = (completedPentests * 100) / TOTALPENTESTS
|
||||||
df.roundingMode = RoundingMode.DOWN
|
BigDecimal(progress).setScale(2, RoundingMode.HALF_UP)
|
||||||
val progress = completedPentests / TOTALPENTESTS
|
|
||||||
df.format(progress).toFloat()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ProjectOverview(
|
data class ProjectOverview(
|
||||||
val projects: List<Project>
|
val projects: List<Project>
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ProjectRequestBody(
|
data class ProjectRequestBody(
|
||||||
|
@ -111,6 +110,5 @@ fun ProjectRequestBody.toProject(): Project {
|
||||||
tester = this.tester,
|
tester = this.tester,
|
||||||
// ToDo: Should be changed to SUB from Token after adding AUTH Header
|
// ToDo: Should be changed to SUB from Token after adding AUTH Header
|
||||||
createdBy = UUID.randomUUID().toString()
|
createdBy = UUID.randomUUID().toString()
|
||||||
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ import com.securityc4po.api.pentest.PentestStatus
|
||||||
|
|
||||||
data class ProjectPentest(
|
data class ProjectPentest(
|
||||||
val pentestId: String,
|
val pentestId: String,
|
||||||
val status: PentestStatus
|
var status: PentestStatus
|
||||||
)
|
)
|
|
@ -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
|
* @throws [TransactionInterruptedException] if the [Project] Pentest could not be updated
|
||||||
* @return updated list of [ProjectPentest]s
|
* @return updated [Project]
|
||||||
*/
|
*/
|
||||||
fun updateProjectTestingProgress(
|
fun updateProjectTestingProgress(
|
||||||
projectId: String,
|
projectId: String,
|
||||||
projectPentests: ProjectPentest
|
projectPentest: ProjectPentest
|
||||||
)/*: Mono<List<ProjectPentest>>*/ {
|
): Mono<Project> {
|
||||||
// ToDo: update Project Entity with progress
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,8 +75,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
.description("The id of the project of the requested pentest"),
|
.description("The id of the project of the requested pentest"),
|
||||||
PayloadDocumentation.fieldWithPath("[].category").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("[].category").type(JsonFieldType.STRING)
|
||||||
.description("The category of the requested pentest"),
|
.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)
|
PayloadDocumentation.fieldWithPath("[].refNumber").type(JsonFieldType.STRING)
|
||||||
.description("The reference number of the requested pentest according to the current OWASP Testing Guide"),
|
.description("The reference number of the requested pentest according to the current OWASP Testing Guide"),
|
||||||
PayloadDocumentation.fieldWithPath("[].status").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("[].status").type(JsonFieldType.STRING)
|
||||||
|
@ -94,7 +92,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Search engine discovery/reconnaissance",
|
|
||||||
refNumber = "OTG-INFO-001",
|
refNumber = "OTG-INFO-001",
|
||||||
status = PentestStatus.NOT_STARTED,
|
status = PentestStatus.NOT_STARTED,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -104,7 +101,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Fingerprint Web Server",
|
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -123,7 +119,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Search engine discovery/reconnaissance",
|
|
||||||
refNumber = "OTG-INFO-001",
|
refNumber = "OTG-INFO-001",
|
||||||
status = PentestStatus.NOT_STARTED,
|
status = PentestStatus.NOT_STARTED,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -133,7 +128,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Fingerprint Web Server",
|
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -143,7 +137,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
id = "74eae112-f62c-11ec-b939-0242ac120002",
|
id = "74eae112-f62c-11ec-b939-0242ac120002",
|
||||||
projectId = "6fad3474-fc29-49f9-bd37-e039e9e60c18",
|
projectId = "6fad3474-fc29-49f9-bd37-e039e9e60c18",
|
||||||
category = PentestCategory.AUTHENTICATION_TESTING,
|
category = PentestCategory.AUTHENTICATION_TESTING,
|
||||||
title = "Testing for Credentials Transported over an Encrypted Channel",
|
|
||||||
refNumber = "OTG-AUTHN-001",
|
refNumber = "OTG-AUTHN-001",
|
||||||
status = PentestStatus.COMPLETED,
|
status = PentestStatus.COMPLETED,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
|
|
@ -69,7 +69,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Search engine discovery/reconnaissance",
|
|
||||||
refNumber = "OTG-INFO-001",
|
refNumber = "OTG-INFO-001",
|
||||||
status = PentestStatus.NOT_STARTED,
|
status = PentestStatus.NOT_STARTED,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -79,7 +78,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Fingerprint Web Server",
|
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -98,7 +96,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Search engine discovery/reconnaissance",
|
|
||||||
refNumber = "OTG-INFO-001",
|
refNumber = "OTG-INFO-001",
|
||||||
status = PentestStatus.NOT_STARTED,
|
status = PentestStatus.NOT_STARTED,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -108,7 +105,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
||||||
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120002",
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
title = "Fingerprint Web Server",
|
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
@ -118,7 +114,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
id = "74eae112-f62c-11ec-b939-0242ac120002",
|
id = "74eae112-f62c-11ec-b939-0242ac120002",
|
||||||
projectId = "6fad3474-fc29-49f9-bd37-e039e9e60c18",
|
projectId = "6fad3474-fc29-49f9-bd37-e039e9e60c18",
|
||||||
category = PentestCategory.AUTHENTICATION_TESTING,
|
category = PentestCategory.AUTHENTICATION_TESTING,
|
||||||
title = "Testing for Credentials Transported over an Encrypted Channel",
|
|
||||||
refNumber = "OTG-AUTHN-001",
|
refNumber = "OTG-AUTHN-001",
|
||||||
status = PentestStatus.COMPLETED,
|
status = PentestStatus.COMPLETED,
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
|
|
|
@ -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"
|
||||||
|
}]
|
|
@ -4,16 +4,21 @@
|
||||||
},
|
},
|
||||||
"lastModified": {
|
"lastModified": {
|
||||||
"$date": {
|
"$date": {
|
||||||
"$numberLong": "1660142860140"
|
"$numberLong": "1668176064717"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"_id": "5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
|
"_id": "5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
|
||||||
"client": "E Corp",
|
"client": "Dio Stonemask Inc.",
|
||||||
"title": "Some Mock API (v1.0) Scanning",
|
"title": "log4jj bizarre adventure",
|
||||||
"createdAt": "2022-08-10T14:47:40.140406Z",
|
"createdAt": "2022-08-10T14:47:40.140406Z",
|
||||||
"tester": "Novatester",
|
"tester": "Jojo",
|
||||||
"projectPentests": [],
|
"projectPentests": [
|
||||||
|
{
|
||||||
|
"pentestId": "11601f51-bc17-47fd-847d-0c53df5405b5",
|
||||||
|
"status": "IN_PROGRESS"
|
||||||
|
}
|
||||||
|
],
|
||||||
"createdBy": "3c4ae87f-0d56-4634-a824-b4883c403c8a"
|
"createdBy": "3c4ae87f-0d56-4634-a824-b4883c403c8a"
|
||||||
},
|
},
|
||||||
"_class": "com.securityc4po.api.project.ProjectEntity"
|
"_class": "com.securityc4po.api.project.ProjectEntity"
|
||||||
|
@ -38,21 +43,21 @@
|
||||||
"_class": "com.securityc4po.api.project.ProjectEntity"
|
"_class": "com.securityc4po.api.project.ProjectEntity"
|
||||||
},{
|
},{
|
||||||
"_id": {
|
"_id": {
|
||||||
"$oid": "62f3c5427acde34f740ba739"
|
"$oid": "62ff7534ac2b4d14d86215c4"
|
||||||
},
|
},
|
||||||
"lastModified": {
|
"lastModified": {
|
||||||
"$date": {
|
"$date": {
|
||||||
"$numberLong": "1660142914204"
|
"$numberLong": "1660908852340"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"_id": "1120bfa1-0d2b-4e42-a209-0289a1256266",
|
"_id": "195809ed-9722-4ad5-a84b-0099a9a01652",
|
||||||
"client": "Novatec",
|
"client": "Novatec",
|
||||||
"title": "Openspace log4J",
|
"title": "log4j pentest",
|
||||||
"createdAt": "2022-08-10T14:48:34.204234Z",
|
"createdAt": "2022-08-19T11:34:12.339990Z",
|
||||||
"tester": "mhg",
|
"tester": "Stipe",
|
||||||
"projectPentests": [],
|
"projectPentests": [],
|
||||||
"createdBy": "5a4a8032-0726-4851-a105-9f079c3989b9"
|
"createdBy": "7fe49c8d-fee3-47e0-9224-94e0ac7436c6"
|
||||||
},
|
},
|
||||||
"_class": "com.securityc4po.api.project.ProjectEntity"
|
"_class": "com.securityc4po.api.project.ProjectEntity"
|
||||||
}]
|
}]
|
Loading…
Reference in New Issue