fix: design pattern and QoL Improvements
This commit is contained in:
parent
46f79dcf89
commit
2ab6940673
|
@ -2997,6 +2997,21 @@
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@ngxs/storage-plugin": {
|
||||||
|
"version": "3.7.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ngxs/storage-plugin/-/storage-plugin-3.7.6.tgz",
|
||||||
|
"integrity": "sha512-jeeoUgsMdb4M8kIHdnO0X6I73okmRJhMW9znaqhYF/aZ+PiQrogdJ5xvSXmoQ7Gg57YfQEhVErM+zf/HD7fnMA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^1.9.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@ngxs/store": {
|
"@ngxs/store": {
|
||||||
"version": "3.7.4",
|
"version": "3.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.4.tgz",
|
||||||
|
@ -11338,9 +11353,9 @@
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||||
},
|
},
|
||||||
"moment-timezone": {
|
"moment-timezone": {
|
||||||
"version": "0.5.38",
|
"version": "0.5.40",
|
||||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.38.tgz",
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz",
|
||||||
"integrity": "sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==",
|
"integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"moment": ">= 2.9.0"
|
"moment": ">= 2.9.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
"@ngneat/until-destroy": "~8.0.4",
|
"@ngneat/until-destroy": "~8.0.4",
|
||||||
"@ngx-translate/core": "^13.0.0",
|
"@ngx-translate/core": "^13.0.0",
|
||||||
"@ngx-translate/http-loader": "^6.0.0",
|
"@ngx-translate/http-loader": "^6.0.0",
|
||||||
|
"@ngxs/storage-plugin": "^3.7.3",
|
||||||
"@ngxs/store": "^3.7.3",
|
"@ngxs/store": "^3.7.3",
|
||||||
"eva-icons": "^1.1.3",
|
"eva-icons": "^1.1.3",
|
||||||
"i18n-iso-countries": "^6.8.0",
|
"i18n-iso-countries": "^6.8.0",
|
||||||
|
|
|
@ -38,7 +38,12 @@
|
||||||
{{ 'comment.relatedFindings' | translate }}
|
{{ 'comment.relatedFindings' | translate }}
|
||||||
</th>
|
</th>
|
||||||
<td nbTreeGridCell *nbTreeGridCellDef="let comment" class="related-finding-cell">
|
<td nbTreeGridCell *nbTreeGridCellDef="let comment" class="related-finding-cell">
|
||||||
{{ comment.data['relatedFindings'].length ? comment.data['relatedFindings'] : 'comment.no.relatedFindings' | translate }}
|
<ng-container *ngIf="comment.data['relatedFindings'].length > 0; else NoRelatedFindings">
|
||||||
|
<app-findig-widget [numberOfFindigs]="comment.data['relatedFindings'].length"></app-findig-widget>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #NoRelatedFindings>
|
||||||
|
{{ 'comment.no.relatedFindings' | translate }}
|
||||||
|
</ng-template>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
.comment-cell {
|
.comment-cell {
|
||||||
// Add style here
|
// Add style here
|
||||||
height: 4.5rem !important;
|
height: 4.5rem !important;
|
||||||
max-height: 4.5rem !important;
|
// max-height: 4.5rem !important;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-cell:hover {
|
.comment-cell:hover {
|
||||||
|
@ -15,10 +16,17 @@
|
||||||
background-color: nb-theme(color-basic-transparent-focus);
|
background-color: nb-theme(color-basic-transparent-focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
height: 4.5rem !important;
|
||||||
|
max-height: 4.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.related-finding-cell {
|
.related-finding-cell {
|
||||||
|
height: 4.5rem !important;
|
||||||
|
max-height: 4.5rem !important;
|
||||||
// cursor: pointer;
|
// cursor: pointer;
|
||||||
font-family: Courier, serif;
|
font-family: Courier, serif;
|
||||||
color: nb-theme(color-info-default);
|
color: nb-theme(color-danger-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell-actions {
|
.cell-actions {
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
{{ 'finding.severity' | translate }}
|
{{ 'finding.severity' | translate }}
|
||||||
</th>
|
</th>
|
||||||
<td nbTreeGridCell *nbTreeGridCellDef="let finding" class="cell-severity border-style" fxLayoutAlign="center center">
|
<td nbTreeGridCell *nbTreeGridCellDef="let finding" class="cell-severity border-style" fxLayoutAlign="center center">
|
||||||
<app-severity-tag [currentSeverity]="finding.data['severity']"></app-severity-tag>
|
<ng-container>
|
||||||
|
<app-severity-tag [currentSeverity]="finding.data['severity']"></app-severity-tag>
|
||||||
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {Store} from '@ngxs/store';
|
||||||
import {UpdatePentestFindings} from '@shared/stores/project-state/project-state.actions';
|
import {UpdatePentestFindings} from '@shared/stores/project-state/project-state.actions';
|
||||||
import {ProjectState} from '@shared/stores/project-state/project-state';
|
import {ProjectState} from '@shared/stores/project-state/project-state';
|
||||||
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {FindingService} from '@shared/services/finding.service';
|
||||||
|
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -31,7 +32,7 @@ import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
})
|
})
|
||||||
export class PentestFindingsComponent implements OnInit {
|
export class PentestFindingsComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private readonly pentestService: PentestService,
|
constructor(private readonly findingService: FindingService,
|
||||||
private dataSourceBuilder: NbTreeGridDataSourceBuilder<FindingEntry>,
|
private dataSourceBuilder: NbTreeGridDataSourceBuilder<FindingEntry>,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
|
@ -75,7 +76,7 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFindingsData(): void {
|
loadFindingsData(): void {
|
||||||
this.pentestService.getFindingsByPentestId(this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '')
|
this.findingService.getFindingsByPentestId(this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '')
|
||||||
.pipe(
|
.pipe(
|
||||||
untilDestroyed(this),
|
untilDestroyed(this),
|
||||||
/*filter(isNotNullOrUndefined),*/
|
/*filter(isNotNullOrUndefined),*/
|
||||||
|
@ -115,7 +116,7 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
filter(value => !!value),
|
filter(value => !!value),
|
||||||
/*tap((value) => console.warn('FindingDialogBody: ', value)),*/
|
/*tap((value) => console.warn('FindingDialogBody: ', value)),*/
|
||||||
mergeMap((value: FindingDialogBody) =>
|
mergeMap((value: FindingDialogBody) =>
|
||||||
this.pentestService.saveFinding(
|
this.findingService.saveFinding(
|
||||||
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
|
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
|
||||||
transformFindingToRequestBody(value)
|
transformFindingToRequestBody(value)
|
||||||
)
|
)
|
||||||
|
@ -135,7 +136,7 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickEditFinding(findingEntry): void {
|
onClickEditFinding(findingEntry): void {
|
||||||
this.pentestService.getFindingById(findingEntry.data.findingId).pipe(
|
this.findingService.getFindingById(findingEntry.data.findingId).pipe(
|
||||||
filter(isNotNullOrUndefined),
|
filter(isNotNullOrUndefined),
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
).subscribe({
|
).subscribe({
|
||||||
|
@ -154,7 +155,7 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
filter(value => !!value),
|
filter(value => !!value),
|
||||||
/*tap((value) => console.warn('FindingDialogBody: ', value)),*/
|
/*tap((value) => console.warn('FindingDialogBody: ', value)),*/
|
||||||
mergeMap((value: FindingDialogBody) =>
|
mergeMap((value: FindingDialogBody) =>
|
||||||
this.pentestService.updateFinding(
|
this.findingService.updateFinding(
|
||||||
findingEntry.data.findingId,
|
findingEntry.data.findingId,
|
||||||
transformFindingToRequestBody(value)
|
transformFindingToRequestBody(value)
|
||||||
)
|
)
|
||||||
|
@ -191,7 +192,7 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
message
|
message
|
||||||
).onClose.pipe(
|
).onClose.pipe(
|
||||||
filter((confirm) => !!confirm),
|
filter((confirm) => !!confirm),
|
||||||
switchMap(() => this.pentestService.deleteFindingByPentestAndFindingId(
|
switchMap(() => this.findingService.deleteFindingByPentestAndFindingId(
|
||||||
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
|
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
|
||||||
findingEntry.data.findingId)
|
findingEntry.data.findingId)
|
||||||
),
|
),
|
||||||
|
@ -201,7 +202,8 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
}),
|
}),
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: () => {
|
next: (deletedFinding: any) => {
|
||||||
|
this.store.dispatch(new UpdatePentestFindings(deletedFinding.id));
|
||||||
this.loadFindingsData();
|
this.loadFindingsData();
|
||||||
this.notificationService.showPopup('finding.popup.delete.success', PopupType.SUCCESS);
|
this.notificationService.showPopup('finding.popup.delete.success', PopupType.SUCCESS);
|
||||||
}, error: error => {
|
}, error: error => {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {CommonAppModule} from '../common-app.module';
|
||||||
import {SeverityTagModule} from '@shared/widgets/severity-tag/severity-tag.module';
|
import {SeverityTagModule} from '@shared/widgets/severity-tag/severity-tag.module';
|
||||||
import {FindingDialogModule} from '@shared/modules/finding-dialog/finding-dialog.module';
|
import {FindingDialogModule} from '@shared/modules/finding-dialog/finding-dialog.module';
|
||||||
import {CommentDialogModule} from '@shared/modules/comment-dialog/comment-dialog.module';
|
import {CommentDialogModule} from '@shared/modules/comment-dialog/comment-dialog.module';
|
||||||
|
import {FindigWidgetModule} from '@shared/widgets/findig-widget/findig-widget.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -26,28 +27,29 @@ import {CommentDialogModule} from '@shared/modules/comment-dialog/comment-dialog
|
||||||
PentestFindingsComponent,
|
PentestFindingsComponent,
|
||||||
PentestCommentsComponent
|
PentestCommentsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
CommonAppModule,
|
CommonAppModule,
|
||||||
RouterModule.forChild([{
|
RouterModule.forChild([{
|
||||||
path: '',
|
path: '',
|
||||||
component: PentestComponent
|
component: PentestComponent
|
||||||
}]),
|
}]),
|
||||||
NbLayoutModule,
|
NbLayoutModule,
|
||||||
NbCardModule,
|
NbCardModule,
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
NbButtonModule,
|
NbButtonModule,
|
||||||
StatusTagModule,
|
StatusTagModule,
|
||||||
NbTabsetModule,
|
NbTabsetModule,
|
||||||
NbTreeGridModule,
|
NbTreeGridModule,
|
||||||
SeverityTagModule,
|
SeverityTagModule,
|
||||||
NbSelectModule,
|
NbSelectModule,
|
||||||
// Dialog Modules
|
// Dialog Modules
|
||||||
FindingDialogModule,
|
FindingDialogModule,
|
||||||
CommentDialogModule,
|
CommentDialogModule,
|
||||||
]
|
FindigWidgetModule,
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class PentestModule {
|
export class PentestModule {
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@
|
||||||
"add": "Kommentar hinzufügen",
|
"add": "Kommentar hinzufügen",
|
||||||
"add.finding": "Fund hinzufügen",
|
"add.finding": "Fund hinzufügen",
|
||||||
"no.comments": "Keine Kommentare verfügbar",
|
"no.comments": "Keine Kommentare verfügbar",
|
||||||
"no.relatedFindings": "Nicht verbunden mit Fund",
|
"no.relatedFindings": "Nicht verbunden mit einem Fund",
|
||||||
"relatedFindingsPlaceholder": "Fund auswählen",
|
"relatedFindingsPlaceholder": "Fund auswählen",
|
||||||
"noFindingsInObjectivePlaceholder": "Objective hat keine Befunde, auf die es sich beziehen könnte.",
|
"noFindingsInObjectivePlaceholder": "Objective hat keine Befunde, auf die es sich beziehen könnte.",
|
||||||
"create": {
|
"create": {
|
||||||
|
|
|
@ -153,7 +153,7 @@
|
||||||
"add": "Add Comment",
|
"add": "Add Comment",
|
||||||
"add.finding": "Add related finding",
|
"add.finding": "Add related finding",
|
||||||
"no.comments": "No comments available",
|
"no.comments": "No comments available",
|
||||||
"no.relatedFindings": "Not related to finding",
|
"no.relatedFindings": "Not related to any finding",
|
||||||
"relatedFindingsPlaceholder": "Select a related finding",
|
"relatedFindingsPlaceholder": "Select a related finding",
|
||||||
"noFindingsInObjectivePlaceholder": "Objective doesn't have any findings to relate to.",
|
"noFindingsInObjectivePlaceholder": "Objective doesn't have any findings to relate to.",
|
||||||
"create": {
|
"create": {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {Store} from '@ngxs/store';
|
||||||
import {Finding} from '@shared/models/finding.model';
|
import {Finding} from '@shared/models/finding.model';
|
||||||
import {RelatedFindingOption} from '@shared/models/comment.model';
|
import {RelatedFindingOption} from '@shared/models/comment.model';
|
||||||
import {BehaviorSubject} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
|
import {FindingService} from '@shared/services/finding.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-comment-dialog',
|
selector: 'app-comment-dialog',
|
||||||
|
@ -39,7 +40,7 @@ export class CommentDialogComponent implements OnInit {
|
||||||
@Inject(NB_DIALOG_CONFIG) private data: GenericDialogData,
|
@Inject(NB_DIALOG_CONFIG) private data: GenericDialogData,
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
protected dialogRef: NbDialogRef<CommentDialogComponent>,
|
protected dialogRef: NbDialogRef<CommentDialogComponent>,
|
||||||
private readonly pentestService: PentestService,
|
private readonly findingService: FindingService,
|
||||||
private store: Store
|
private store: Store
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -95,7 +96,7 @@ export class CommentDialogComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
requestRelatedFindingsData(pentestId: string, relatedFindings: any): void {
|
requestRelatedFindingsData(pentestId: string, relatedFindings: any): void {
|
||||||
this.pentestService.getFindingsByPentestId(pentestId).pipe(
|
this.findingService.getFindingsByPentestId(pentestId).pipe(
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: (findings: Finding[]) => {
|
next: (findings: Finding[]) => {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { FindingService } from './finding.service';
|
||||||
|
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||||
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {NgxsModule} from '@ngxs/store';
|
||||||
|
import {ProjectState} from '@shared/stores/project-state/project-state';
|
||||||
|
|
||||||
|
describe('FindingService', () => {
|
||||||
|
let service: FindingService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
HttpClientTestingModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
NgxsModule.forRoot([ProjectState])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
service = TestBed.inject(FindingService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {environment} from '../../environments/environment';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
import {Store} from '@ngxs/store';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {Finding} from '@shared/models/finding.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FindingService {
|
||||||
|
|
||||||
|
private apiBaseURL = `${environment.apiEndpoint}/pentests`;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private readonly store: Store) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Findings for Pentest Id
|
||||||
|
* @param pentestId the id of the project
|
||||||
|
*/
|
||||||
|
public getFindingsByPentestId(pentestId: string): Observable<Finding[]> {
|
||||||
|
return this.http.get<Finding[]>(`${this.apiBaseURL}/${pentestId}/findings`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Finding by Id
|
||||||
|
* @param findingId the id of the finding
|
||||||
|
*/
|
||||||
|
public getFindingById(findingId: string): Observable<Finding> {
|
||||||
|
return this.http.get<Finding>(`${this.apiBaseURL}/${findingId}/finding`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save Finding
|
||||||
|
* @param pentestId the id of the pentest
|
||||||
|
* @param finding the information of the finding
|
||||||
|
*/
|
||||||
|
public saveFinding(pentestId: string, finding: Finding): Observable<Finding> {
|
||||||
|
return this.http.post<Finding>(`${this.apiBaseURL}/${pentestId}/finding`, finding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Finding
|
||||||
|
* @param findingId the id of the finding
|
||||||
|
* @param finding the information of the finding
|
||||||
|
*/
|
||||||
|
public updateFinding(findingId: string, finding: Finding): Observable<Finding> {
|
||||||
|
return this.http.patch<Finding>(`${this.apiBaseURL}/${findingId}/finding`, finding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Finding
|
||||||
|
* @param pentestId the id of the pentest
|
||||||
|
* @param findingId the id of the finding
|
||||||
|
*/
|
||||||
|
public deleteFindingByPentestAndFindingId(pentestId: string, findingId: string): Observable<string> {
|
||||||
|
return this.http.delete<string>(`${this.apiBaseURL}/${pentestId}/finding/${findingId}`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,6 @@ import {ProjectState} from '@shared/stores/project-state/project-state';
|
||||||
import {catchError, map, switchMap} from 'rxjs/operators';
|
import {catchError, map, switchMap} from 'rxjs/operators';
|
||||||
import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-pentests-for-category.function';
|
import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-pentests-for-category.function';
|
||||||
import {Finding} from '@shared/models/finding.model';
|
import {Finding} from '@shared/models/finding.model';
|
||||||
import {Comment} from '@shared/models/comment.model';
|
|
||||||
import {v4 as UUID} from 'uuid';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -78,47 +76,4 @@ export class PentestService {
|
||||||
public updatePentest(pentest: Pentest): Observable<Pentest> {
|
public updatePentest(pentest: Pentest): Observable<Pentest> {
|
||||||
return this.http.patch<Pentest>(`${this.apiBaseURL}/${pentest.id}`, pentest);
|
return this.http.patch<Pentest>(`${this.apiBaseURL}/${pentest.id}`, pentest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Findings for Pentest Id
|
|
||||||
* @param pentestId the id of the project
|
|
||||||
*/
|
|
||||||
public getFindingsByPentestId(pentestId: string): Observable<Finding[]> {
|
|
||||||
return this.http.get<Finding[]>(`${this.apiBaseURL}/${pentestId}/findings`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Finding by Id
|
|
||||||
* @param findingId the id of the finding
|
|
||||||
*/
|
|
||||||
public getFindingById(findingId: string): Observable<Finding> {
|
|
||||||
return this.http.get<Finding>(`${this.apiBaseURL}/${findingId}/finding`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save Finding
|
|
||||||
* @param pentestId the id of the pentest
|
|
||||||
* @param finding the information of the finding
|
|
||||||
*/
|
|
||||||
public saveFinding(pentestId: string, finding: Finding): Observable<Finding> {
|
|
||||||
return this.http.post<Finding>(`${this.apiBaseURL}/${pentestId}/finding`, finding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update Finding
|
|
||||||
* @param findingId the id of the finding
|
|
||||||
* @param finding the information of the finding
|
|
||||||
*/
|
|
||||||
public updateFinding(findingId: string, finding: Finding): Observable<Finding> {
|
|
||||||
return this.http.patch<Finding>(`${this.apiBaseURL}/${findingId}/finding`, finding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete Finding
|
|
||||||
* @param pentestId the id of the pentest
|
|
||||||
* @param findingId the id of the finding
|
|
||||||
*/
|
|
||||||
public deleteFindingByPentestAndFindingId(pentestId: string, findingId: string): Observable<string> {
|
|
||||||
return this.http.delete<string>(`${this.apiBaseURL}/${pentestId}/finding/${findingId}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@ export interface ProjectStateModel {
|
||||||
selectedPentest: Pentest;
|
selectedPentest: Pentest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FindingMap {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
@State<ProjectStateModel>({
|
@State<ProjectStateModel>({
|
||||||
name: PROJECT_STATE_NAME,
|
name: PROJECT_STATE_NAME,
|
||||||
defaults: {
|
defaults: {
|
||||||
|
@ -91,12 +95,14 @@ export class ProjectState {
|
||||||
updatePentestFindings(ctx: StateContext<ProjectStateModel>, {findingId}: UpdatePentestFindings): void {
|
updatePentestFindings(ctx: StateContext<ProjectStateModel>, {findingId}: UpdatePentestFindings): void {
|
||||||
const state = ctx.getState();
|
const state = ctx.getState();
|
||||||
let stateSelectedPentest: Pentest = state.selectedPentest;
|
let stateSelectedPentest: Pentest = state.selectedPentest;
|
||||||
|
// State objects
|
||||||
const stateFindingIds: Array<string> = stateSelectedPentest.findingIds || [];
|
const stateFindingIds: Array<string> = stateSelectedPentest.findingIds || [];
|
||||||
let updatedFindingIds: Array<string> = [];
|
let updatedFindingIds: Array<string> = [];
|
||||||
if (!stateFindingIds.includes(findingId)) {
|
if (!stateFindingIds.includes(findingId)) {
|
||||||
updatedFindingIds = [...stateFindingIds, findingId];
|
updatedFindingIds = [...stateFindingIds, findingId];
|
||||||
} else {
|
} else {
|
||||||
// ToDo: Add logic to remove findingId from array
|
const findingIndex = stateFindingIds.indexOf(findingId);
|
||||||
|
updatedFindingIds = [...stateFindingIds.slice(0, findingIndex)];
|
||||||
}
|
}
|
||||||
// overwrites only findingIds
|
// overwrites only findingIds
|
||||||
stateSelectedPentest = {
|
stateSelectedPentest = {
|
||||||
|
@ -113,12 +119,14 @@ export class ProjectState {
|
||||||
updatePentestComments(ctx: StateContext<ProjectStateModel>, {commentId}: UpdatePentestComments): void {
|
updatePentestComments(ctx: StateContext<ProjectStateModel>, {commentId}: UpdatePentestComments): void {
|
||||||
const state = ctx.getState();
|
const state = ctx.getState();
|
||||||
let stateSelectedPentest: Pentest = state.selectedPentest;
|
let stateSelectedPentest: Pentest = state.selectedPentest;
|
||||||
|
// State objects
|
||||||
const stateCommentIds: Array<string> = stateSelectedPentest.commentIds || [];
|
const stateCommentIds: Array<string> = stateSelectedPentest.commentIds || [];
|
||||||
let updatedCommentIds: Array<string> = [];
|
let updatedCommentIds: Array<string> = [];
|
||||||
if (!stateCommentIds.includes(commentId)) {
|
if (!stateCommentIds.includes(commentId)) {
|
||||||
updatedCommentIds = [...stateCommentIds, commentId];
|
updatedCommentIds = [...stateCommentIds, commentId];
|
||||||
} else {
|
} else {
|
||||||
// ToDo: Add logic to remove commentId from array
|
const commentIndex = stateCommentIds.indexOf(commentId);
|
||||||
|
updatedCommentIds = [...stateCommentIds.slice(0, commentIndex)];
|
||||||
}
|
}
|
||||||
// overwrites only findingIds
|
// overwrites only findingIds
|
||||||
stateSelectedPentest = {
|
stateSelectedPentest = {
|
||||||
|
|
|
@ -13,11 +13,8 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"target": "es2015",
|
"target": "es2015",
|
||||||
"module": "es2020",
|
"module": "esNext",
|
||||||
"lib": [
|
"lib": ["es2019", "es2018", "dom"],
|
||||||
"es2018",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@shared/*": ["./src/shared/*"],
|
"@shared/*": ["./src/shared/*"],
|
||||||
"@assets/*": ["./src/assets/*"]
|
"@assets/*": ["./src/assets/*"]
|
||||||
|
|
|
@ -4,15 +4,10 @@ import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
||||||
import com.securityc4po.api.extensions.getLoggerFor
|
import com.securityc4po.api.extensions.getLoggerFor
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
import com.securityc4po.api.ResponseBody
|
import com.securityc4po.api.ResponseBody
|
||||||
import com.securityc4po.api.finding.FindingRequestBody
|
|
||||||
import com.securityc4po.api.finding.FindingService
|
|
||||||
import com.securityc4po.api.finding.toFindingDeleteResponseBody
|
|
||||||
import com.securityc4po.api.finding.toFindingResponseBody
|
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.http.ResponseEntity.noContent
|
import org.springframework.http.ResponseEntity.noContent
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import reactor.kotlin.core.publisher.switchIfEmpty
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/pentests")
|
@RequestMapping("/pentests")
|
||||||
|
@ -23,7 +18,7 @@ import reactor.kotlin.core.publisher.switchIfEmpty
|
||||||
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
|
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
|
||||||
)
|
)
|
||||||
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
||||||
class PentestController(private val pentestService: PentestService, private val findingService: FindingService) {
|
class PentestController(private val pentestService: PentestService) {
|
||||||
|
|
||||||
var logger = getLoggerFor<PentestController>()
|
var logger = getLoggerFor<PentestController>()
|
||||||
|
|
||||||
|
@ -71,55 +66,4 @@ class PentestController(private val pentestService: PentestService, private val
|
||||||
ResponseEntity.accepted().body(it.toPentestResponseBody())
|
ResponseEntity.accepted().body(it.toPentestResponseBody())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{pentestId}/findings")
|
|
||||||
fun getFindings(@PathVariable(value = "pentestId") pentestId: String): Mono<ResponseEntity<List<ResponseBody>>> {
|
|
||||||
return this.pentestService.getFindingIdsByPentestId(pentestId).flatMap { findingIds: List<String> ->
|
|
||||||
this.findingService.getFindingsByIds(findingIds).map { findingList ->
|
|
||||||
findingList.map { it.toFindingResponseBody() }
|
|
||||||
}
|
|
||||||
}.map {
|
|
||||||
if (it.isEmpty()) noContent().build()
|
|
||||||
else ResponseEntity.ok(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{findingId}/finding")
|
|
||||||
fun getFinding(@PathVariable(value = "findingId") findingId: String):Mono<ResponseEntity<ResponseBody>> {
|
|
||||||
return this.findingService.getFindingById(findingId).map {
|
|
||||||
ResponseEntity.ok().body(it.toFindingResponseBody())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/{pentestId}/finding")
|
|
||||||
fun saveFinding(
|
|
||||||
@PathVariable(value = "pentestId") pentestId: String,
|
|
||||||
@RequestBody body: FindingRequestBody
|
|
||||||
): Mono<ResponseEntity<ResponseBody>> {
|
|
||||||
return this.findingService.saveFinding(pentestId, body).map {
|
|
||||||
ResponseEntity.accepted().body(it.toFindingResponseBody())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PatchMapping("/{findingId}/finding")
|
|
||||||
fun updateFinding(
|
|
||||||
@PathVariable(value = "findingId") findingId: String,
|
|
||||||
@RequestBody body: FindingRequestBody
|
|
||||||
): Mono<ResponseEntity<ResponseBody>> {
|
|
||||||
return this.findingService.updateFinding(findingId, body).map {
|
|
||||||
ResponseEntity.accepted().body(it.toFindingResponseBody())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/{pentestId}/finding/{findingId}")
|
|
||||||
fun deleteFinding(
|
|
||||||
@PathVariable(value = "pentestId") pentestId: String,
|
|
||||||
@PathVariable(value = "findingId") findingId: String
|
|
||||||
): Mono<ResponseEntity<ResponseBody>> {
|
|
||||||
return this.findingService.deleteFindingByPentestAndFindingId(pentestId, findingId).map {
|
|
||||||
ResponseEntity.ok().body(it.toFindingDeleteResponseBody())
|
|
||||||
}.switchIfEmpty {
|
|
||||||
Mono.just(noContent().build<ResponseBody>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -53,16 +53,16 @@ class PentestService(private val pentestRepository: PentestRepository, private v
|
||||||
val pentest = body.toPentest()
|
val pentest = body.toPentest()
|
||||||
val pentestEntity = PentestEntity(pentest)
|
val pentestEntity = PentestEntity(pentest)
|
||||||
return pentestRepository.insert(pentestEntity).flatMap { newPentestEntity: PentestEntity ->
|
return pentestRepository.insert(pentestEntity).flatMap { newPentestEntity: PentestEntity ->
|
||||||
val pentest = newPentestEntity.toPentest()
|
val newPentest = newPentestEntity.toPentest()
|
||||||
// After successfully saving pentest add id and status to project
|
// After successfully saving pentest add id and status to project
|
||||||
val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status)
|
val projectPentest = ProjectPentest(pentestId = newPentest.id, status = newPentest.status)
|
||||||
projectService.updateProjectTestingProgress(projectId, projectPentest).onErrorMap {
|
projectService.updateProjectTestingProgress(projectId, projectPentest).onErrorMap {
|
||||||
TransactionInterruptedException(
|
TransactionInterruptedException(
|
||||||
"Project Pentests could not be updated in Database.",
|
"Project Pentests could not be updated in Database.",
|
||||||
Errorcode.ProjectPentestInsertionFailed
|
Errorcode.ProjectPentestInsertionFailed
|
||||||
)
|
)
|
||||||
}.map {
|
}.map {
|
||||||
pentest
|
newPentest
|
||||||
}
|
}
|
||||||
}.doOnError {
|
}.doOnError {
|
||||||
throw wrappedException(
|
throw wrappedException(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import com.securityc4po.api.ResponseBody
|
import com.securityc4po.api.ResponseBody
|
||||||
import com.securityc4po.api.finding.FindingRequestBody
|
import com.securityc4po.api.pentest.finding.FindingRequestBody
|
||||||
import org.springframework.data.mongodb.core.index.Indexed
|
import org.springframework.data.mongodb.core.index.Indexed
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
||||||
import com.securityc4po.api.extensions.getLoggerFor
|
import com.securityc4po.api.extensions.getLoggerFor
|
||||||
|
@ -6,7 +6,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
import com.securityc4po.api.ResponseBody
|
import com.securityc4po.api.ResponseBody
|
||||||
import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
|
import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
|
||||||
import com.securityc4po.api.configuration.error.handler.Errorcode
|
import com.securityc4po.api.configuration.error.handler.Errorcode
|
||||||
import com.securityc4po.api.finding.toFindingResponseBody
|
import com.securityc4po.api.pentest.finding.toFindingResponseBody
|
||||||
import com.securityc4po.api.pentest.PentestService
|
import com.securityc4po.api.pentest.PentestService
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.http.ResponseEntity.noContent
|
import org.springframework.http.ResponseEntity.noContent
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import com.securityc4po.api.BaseEntity
|
import com.securityc4po.api.BaseEntity
|
||||||
import org.springframework.data.mongodb.core.mapping.Document
|
import org.springframework.data.mongodb.core.mapping.Document
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import org.springframework.data.mongodb.repository.DeleteQuery
|
import org.springframework.data.mongodb.repository.DeleteQuery
|
||||||
import org.springframework.data.mongodb.repository.Query
|
import org.springframework.data.mongodb.repository.Query
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
import com.securityc4po.api.configuration.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
|
||||||
|
@ -7,7 +7,6 @@ import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
|
||||||
import com.securityc4po.api.configuration.error.handler.InvalidModelException
|
import com.securityc4po.api.configuration.error.handler.InvalidModelException
|
||||||
import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException
|
import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException
|
||||||
import com.securityc4po.api.extensions.getLoggerFor
|
import com.securityc4po.api.extensions.getLoggerFor
|
||||||
import com.securityc4po.api.finding.*
|
|
||||||
import com.securityc4po.api.pentest.PentestService
|
import com.securityc4po.api.pentest.PentestService
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.finding
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
import com.securityc4po.api.ResponseBody
|
import com.securityc4po.api.ResponseBody
|
||||||
import org.springframework.data.mongodb.core.index.Indexed
|
import org.springframework.data.mongodb.core.index.Indexed
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
|
import com.securityc4po.api.pentest.PentestCategory
|
||||||
|
import com.securityc4po.api.pentest.PentestRequestBody
|
||||||
|
import com.securityc4po.api.pentest.PentestService
|
||||||
|
import com.securityc4po.api.pentest.toPentestResponseBody
|
||||||
|
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
||||||
|
import com.securityc4po.api.extensions.getLoggerFor
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
|
import com.securityc4po.api.ResponseBody
|
||||||
|
import com.securityc4po.api.pentest.finding.FindingRequestBody
|
||||||
|
import com.securityc4po.api.pentest.finding.FindingService
|
||||||
|
import com.securityc4po.api.pentest.finding.toFindingDeleteResponseBody
|
||||||
|
import com.securityc4po.api.pentest.finding.toFindingResponseBody
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.http.ResponseEntity.noContent
|
||||||
|
import org.springframework.web.bind.annotation.*
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.kotlin.core.publisher.switchIfEmpty
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/pentests")
|
||||||
|
@CrossOrigin(
|
||||||
|
origins = [],
|
||||||
|
allowCredentials = "false",
|
||||||
|
allowedHeaders = ["*"],
|
||||||
|
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
|
||||||
|
)
|
||||||
|
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
||||||
|
class FindingController(private val pentestService: PentestService, private val findingService: FindingService) {
|
||||||
|
|
||||||
|
var logger = getLoggerFor<FindingController>()
|
||||||
|
|
||||||
|
@GetMapping("/{pentestId}/findings")
|
||||||
|
fun getFindings(@PathVariable(value = "pentestId") pentestId: String): Mono<ResponseEntity<List<ResponseBody>>> {
|
||||||
|
return this.pentestService.getFindingIdsByPentestId(pentestId).flatMap { findingIds: List<String> ->
|
||||||
|
this.findingService.getFindingsByIds(findingIds).map { findingList ->
|
||||||
|
findingList.map { it.toFindingResponseBody() }
|
||||||
|
}
|
||||||
|
}.map {
|
||||||
|
if (it.isEmpty()) noContent().build()
|
||||||
|
else ResponseEntity.ok(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{findingId}/finding")
|
||||||
|
fun getFinding(@PathVariable(value = "findingId") findingId: String):Mono<ResponseEntity<ResponseBody>> {
|
||||||
|
return this.findingService.getFindingById(findingId).map {
|
||||||
|
ResponseEntity.ok().body(it.toFindingResponseBody())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{pentestId}/finding")
|
||||||
|
fun saveFinding(
|
||||||
|
@PathVariable(value = "pentestId") pentestId: String,
|
||||||
|
@RequestBody body: FindingRequestBody
|
||||||
|
): Mono<ResponseEntity<ResponseBody>> {
|
||||||
|
return this.findingService.saveFinding(pentestId, body).map {
|
||||||
|
ResponseEntity.accepted().body(it.toFindingResponseBody())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{findingId}/finding")
|
||||||
|
fun updateFinding(
|
||||||
|
@PathVariable(value = "findingId") findingId: String,
|
||||||
|
@RequestBody body: FindingRequestBody
|
||||||
|
): Mono<ResponseEntity<ResponseBody>> {
|
||||||
|
return this.findingService.updateFinding(findingId, body).map {
|
||||||
|
ResponseEntity.accepted().body(it.toFindingResponseBody())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{pentestId}/finding/{findingId}")
|
||||||
|
fun deleteFinding(
|
||||||
|
@PathVariable(value = "pentestId") pentestId: String,
|
||||||
|
@PathVariable(value = "findingId") findingId: String
|
||||||
|
): Mono<ResponseEntity<ResponseBody>> {
|
||||||
|
// ToDo: Add remove finding from comment as well
|
||||||
|
return this.findingService.deleteFindingByPentestAndFindingId(pentestId, findingId).map {
|
||||||
|
ResponseEntity.ok().body(it.toFindingDeleteResponseBody())
|
||||||
|
}.switchIfEmpty {
|
||||||
|
Mono.just(noContent().build<ResponseBody>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.finding
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
import com.securityc4po.api.BaseEntity
|
import com.securityc4po.api.BaseEntity
|
||||||
import org.springframework.data.mongodb.core.mapping.Document
|
import org.springframework.data.mongodb.core.mapping.Document
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.finding
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
import org.springframework.data.mongodb.repository.DeleteQuery
|
import org.springframework.data.mongodb.repository.DeleteQuery
|
||||||
import org.springframework.data.mongodb.repository.Query
|
import org.springframework.data.mongodb.repository.Query
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.finding
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
|
import com.securityc4po.api.configuration.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
|
||||||
|
@ -37,15 +37,15 @@ class FindingService(private val findingRepository: FindingRepository, private v
|
||||||
val finding = body.toFinding()
|
val finding = body.toFinding()
|
||||||
val findingEntity = FindingEntity(finding)
|
val findingEntity = FindingEntity(finding)
|
||||||
return findingRepository.insert(findingEntity).flatMap { newFindingEntity: FindingEntity ->
|
return findingRepository.insert(findingEntity).flatMap { newFindingEntity: FindingEntity ->
|
||||||
val finding = newFindingEntity.toFinding()
|
val newFinding = newFindingEntity.toFinding()
|
||||||
// After successfully saving finding add id to pentest
|
// After successfully saving finding add id to pentest
|
||||||
pentestService.updatePentestFinding(pentestId, finding.id).onErrorMap {
|
pentestService.updatePentestFinding(pentestId, newFinding.id).onErrorMap {
|
||||||
TransactionInterruptedException(
|
TransactionInterruptedException(
|
||||||
"Pentest could not be updated in Database.",
|
"Pentest could not be updated in Database.",
|
||||||
Errorcode.PentestInsertionFailed
|
Errorcode.PentestInsertionFailed
|
||||||
)
|
)
|
||||||
}.map {
|
}.map {
|
||||||
finding
|
newFinding
|
||||||
}
|
}
|
||||||
}.doOnError {
|
}.doOnError {
|
||||||
throw wrappedException(
|
throw wrappedException(
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.finding
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
enum class Severity {
|
enum class Severity {
|
||||||
LOW,
|
LOW,
|
|
@ -6,7 +6,6 @@ import com.securityc4po.api.BaseDocumentationIntTest
|
||||||
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
||||||
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||||
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
||||||
import com.securityc4po.api.finding.*
|
|
||||||
import com.securityc4po.api.project.Project
|
import com.securityc4po.api.project.Project
|
||||||
import com.securityc4po.api.project.ProjectEntity
|
import com.securityc4po.api.project.ProjectEntity
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
|
@ -109,7 +108,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
findingIds = emptyList(),
|
||||||
commentIds = emptyList()
|
commentIds = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -233,287 +232,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class GetFindings {
|
|
||||||
@Test
|
|
||||||
fun getFindingsByPentestId() {
|
|
||||||
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
webTestClient.get()
|
|
||||||
.uri("/pentests/{pentestId}/findings", pentestTwoId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk
|
|
||||||
.expectHeader().doesNotExist("")
|
|
||||||
.expectBody().json(Json.write(getFindingsResponse()))
|
|
||||||
.consumeWith(
|
|
||||||
WebTestClientRestDocumentation.document(
|
|
||||||
"{methodName}",
|
|
||||||
Preprocessors.preprocessRequest(
|
|
||||||
Preprocessors.prettyPrint(),
|
|
||||||
Preprocessors.modifyUris().removePort(),
|
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")
|
|
||||||
),
|
|
||||||
Preprocessors.preprocessResponse(
|
|
||||||
Preprocessors.prettyPrint()
|
|
||||||
),
|
|
||||||
RequestDocumentation.relaxedPathParameters(
|
|
||||||
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to get the findings for")
|
|
||||||
),
|
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
|
||||||
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
|
|
||||||
.description("The id of the requested findings"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].severity").type(JsonFieldType.STRING)
|
|
||||||
.description("The severity of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
|
|
||||||
.description("The title of the requested finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING)
|
|
||||||
.description("The description number of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].impact").type(JsonFieldType.STRING)
|
|
||||||
.description("The impact of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].affectedUrls").type(JsonFieldType.ARRAY)
|
|
||||||
.description("List of affected Urls of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].reproduction").type(JsonFieldType.STRING)
|
|
||||||
.description("The reproduction steps of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("[].mitigation").type(JsonFieldType.STRING)
|
|
||||||
.description("The example mitigation for the finding")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingOne = Finding(
|
|
||||||
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
||||||
severity = Severity.LOW,
|
|
||||||
title = "Found Bug",
|
|
||||||
description = "OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "None"
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getFindingsResponse() = listOf(
|
|
||||||
findingOne.toFindingResponseBody()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class GetFinding {
|
|
||||||
@Test
|
|
||||||
fun getFindingById() {
|
|
||||||
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
|
||||||
webTestClient.get()
|
|
||||||
.uri("/pentests/{findingId}/finding", findingId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk
|
|
||||||
.expectHeader().doesNotExist("")
|
|
||||||
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
|
|
||||||
.consumeWith(
|
|
||||||
WebTestClientRestDocumentation.document(
|
|
||||||
"{methodName}",
|
|
||||||
Preprocessors.preprocessRequest(
|
|
||||||
Preprocessors.prettyPrint(),
|
|
||||||
Preprocessors.modifyUris().removePort(),
|
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")
|
|
||||||
),
|
|
||||||
Preprocessors.preprocessResponse(
|
|
||||||
Preprocessors.prettyPrint()
|
|
||||||
),
|
|
||||||
RequestDocumentation.relaxedPathParameters(
|
|
||||||
RequestDocumentation.parameterWithName("findingId").description("The id of the feinidng you want to get")
|
|
||||||
),
|
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
|
||||||
.description("The id of the requested finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
|
||||||
.description("The severity of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
|
||||||
.description("The title of the requested finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
|
|
||||||
.description("The description number of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
|
|
||||||
.description("The impact of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
|
|
||||||
.description("List of affected Urls of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
|
|
||||||
.description("The reproduction steps of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
|
|
||||||
.description("The example mitigation for the finding")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingOne = Finding(
|
|
||||||
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
||||||
severity = Severity.LOW,
|
|
||||||
title = "Found Bug",
|
|
||||||
description = "OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "None"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class SaveFinding {
|
|
||||||
@Test
|
|
||||||
fun saveFindingByPentestId() {
|
|
||||||
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
webTestClient.post()
|
|
||||||
.uri("/pentests/{pentestId}/finding", pentestTwoId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isAccepted
|
|
||||||
.expectHeader().doesNotExist("")
|
|
||||||
.expectBody().json(Json.write(findingBody))
|
|
||||||
.consumeWith(
|
|
||||||
WebTestClientRestDocumentation.document(
|
|
||||||
"{methodName}",
|
|
||||||
Preprocessors.preprocessRequest(
|
|
||||||
Preprocessors.prettyPrint(),
|
|
||||||
Preprocessors.modifyUris().removePort(),
|
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")
|
|
||||||
),
|
|
||||||
Preprocessors.preprocessResponse(
|
|
||||||
Preprocessors.prettyPrint()
|
|
||||||
),
|
|
||||||
RequestDocumentation.relaxedPathParameters(
|
|
||||||
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to save the finding for")
|
|
||||||
),
|
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
|
||||||
.description("The id of the saved finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
|
||||||
.description("The severity of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
|
||||||
.description("The title of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
|
|
||||||
.description("The description number of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
|
|
||||||
.description("The impact of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
|
|
||||||
.description("List of affected Urls of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
|
|
||||||
.description("The reproduction steps of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
|
|
||||||
.description("The example mitigation for the finding")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingBody = FindingRequestBody(
|
|
||||||
severity = "MEDIUM",
|
|
||||||
title = "Found another Bug",
|
|
||||||
description = "Another OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack more",
|
|
||||||
mitigation = "Another None"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class UpdateFinding {
|
|
||||||
@Test
|
|
||||||
fun updateFindingById() {
|
|
||||||
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
|
||||||
webTestClient.patch()
|
|
||||||
.uri("/pentests/{findingId}/finding", findingId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isAccepted
|
|
||||||
.expectHeader().doesNotExist("")
|
|
||||||
.expectBody().json(Json.write(findingBody))
|
|
||||||
.consumeWith(
|
|
||||||
WebTestClientRestDocumentation.document(
|
|
||||||
"{methodName}",
|
|
||||||
Preprocessors.preprocessRequest(
|
|
||||||
Preprocessors.prettyPrint(),
|
|
||||||
Preprocessors.modifyUris().removePort(),
|
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")
|
|
||||||
),
|
|
||||||
Preprocessors.preprocessResponse(
|
|
||||||
Preprocessors.prettyPrint()
|
|
||||||
),
|
|
||||||
RequestDocumentation.relaxedPathParameters(
|
|
||||||
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to update")
|
|
||||||
),
|
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
|
||||||
.description("The id of the updated finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
|
||||||
.description("The severity of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
|
||||||
.description("The title of the requested finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
|
|
||||||
.description("The description number of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
|
|
||||||
.description("The impact of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
|
|
||||||
.description("List of affected Urls of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
|
|
||||||
.description("The reproduction steps of the finding"),
|
|
||||||
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
|
|
||||||
.description("The example mitigation for the finding")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingBody = FindingRequestBody(
|
|
||||||
severity = "HIGH",
|
|
||||||
title = "Updated Bug",
|
|
||||||
description = "Updated OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "Still None"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class DeleteFinding {
|
|
||||||
@Test
|
|
||||||
fun deleteFindingByPentestAndFindingId() {
|
|
||||||
val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
|
||||||
webTestClient.delete()
|
|
||||||
.uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk
|
|
||||||
.expectHeader().doesNotExist("")
|
|
||||||
.expectBody()
|
|
||||||
.consumeWith(
|
|
||||||
WebTestClientRestDocumentation.document(
|
|
||||||
"{methodName}",
|
|
||||||
Preprocessors.preprocessRequest(
|
|
||||||
Preprocessors.prettyPrint(),
|
|
||||||
Preprocessors.modifyUris().removePort(),
|
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")
|
|
||||||
),
|
|
||||||
Preprocessors.preprocessResponse(
|
|
||||||
Preprocessors.prettyPrint()
|
|
||||||
),
|
|
||||||
RequestDocumentation.relaxedPathParameters(
|
|
||||||
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to remove the finidng from"),
|
|
||||||
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to delete")
|
|
||||||
),
|
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
|
||||||
.description("The id of the finding you deleted")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun persistBasicTestScenario() {
|
private fun persistBasicTestScenario() {
|
||||||
// setup test data
|
// setup test data
|
||||||
// Project
|
// Project
|
||||||
|
@ -542,7 +260,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
findingIds = emptyList(),
|
||||||
commentIds = emptyList()
|
commentIds = emptyList()
|
||||||
)
|
)
|
||||||
val pentestThree = Pentest(
|
val pentestThree = Pentest(
|
||||||
|
@ -554,23 +272,11 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
commentIds = emptyList()
|
commentIds = emptyList()
|
||||||
)
|
)
|
||||||
// Finding
|
|
||||||
val findingOne = Finding(
|
|
||||||
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
||||||
severity = Severity.LOW,
|
|
||||||
title = "Found Bug",
|
|
||||||
description = "OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "None"
|
|
||||||
)
|
|
||||||
// persist test data in database
|
// persist test data in database
|
||||||
mongoTemplate.save(ProjectEntity(projectOne))
|
mongoTemplate.save(ProjectEntity(projectOne))
|
||||||
mongoTemplate.save(PentestEntity(pentestOne))
|
mongoTemplate.save(PentestEntity(pentestOne))
|
||||||
mongoTemplate.save(PentestEntity(pentestTwo))
|
mongoTemplate.save(PentestEntity(pentestTwo))
|
||||||
mongoTemplate.save(PentestEntity(pentestThree))
|
mongoTemplate.save(PentestEntity(pentestThree))
|
||||||
mongoTemplate.save(FindingEntity(findingOne))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureAdminToken() {
|
private fun configureAdminToken() {
|
||||||
|
@ -580,7 +286,6 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
private fun cleanUp() {
|
private fun cleanUp() {
|
||||||
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
||||||
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
||||||
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
|
|
||||||
|
|
||||||
tokenAdmin = "n/a"
|
tokenAdmin = "n/a"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import com.securityc4po.api.BaseIntTest
|
||||||
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
||||||
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||||
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
||||||
import com.securityc4po.api.finding.*
|
|
||||||
import com.securityc4po.api.project.Project
|
import com.securityc4po.api.project.Project
|
||||||
import com.securityc4po.api.project.ProjectEntity
|
import com.securityc4po.api.project.ProjectEntity
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
|
@ -86,7 +85,7 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
findingIds = emptyList(),
|
||||||
commentIds = emptyList()
|
commentIds = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -159,145 +158,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class GetFindings {
|
|
||||||
@Test
|
|
||||||
fun `requesting findings by pentestId successfully`() {
|
|
||||||
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
webTestClient.get()
|
|
||||||
.uri("/pentests/{pentestId}/findings", pentestTwoId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk
|
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
||||||
.expectBody().json(Json.write(getFindings()))
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingOne = Finding(
|
|
||||||
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
||||||
severity = Severity.LOW,
|
|
||||||
title = "Found Bug",
|
|
||||||
description = "OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "None"
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getFindings() = listOf(
|
|
||||||
findingOne.toFindingResponseBody()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class GetFinding {
|
|
||||||
@Test
|
|
||||||
fun `requesting finding by findingId successfully`() {
|
|
||||||
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
|
||||||
webTestClient.get()
|
|
||||||
.uri("/pentests/{findingId}/finding", findingId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk
|
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
||||||
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingOne = Finding(
|
|
||||||
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
||||||
severity = Severity.LOW,
|
|
||||||
title = "Found Bug",
|
|
||||||
description = "OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "None"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class SaveFinding {
|
|
||||||
@Test
|
|
||||||
fun `save finding successfully`() {
|
|
||||||
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
webTestClient.post()
|
|
||||||
.uri("/pentests/{pentestId}/finding", pentestTwoId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isAccepted
|
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
||||||
.expectBody()
|
|
||||||
.jsonPath("$.severity").isEqualTo("MEDIUM")
|
|
||||||
.jsonPath("$.title").isEqualTo("Found another Bug")
|
|
||||||
.jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug")
|
|
||||||
.jsonPath("$.impact").isEqualTo("Service")
|
|
||||||
.jsonPath("$.affectedUrls").isEmpty
|
|
||||||
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack more")
|
|
||||||
.jsonPath("$.mitigation").isEqualTo("Another None")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingBody = FindingRequestBody(
|
|
||||||
severity = "MEDIUM",
|
|
||||||
title = "Found another Bug",
|
|
||||||
description = "Another OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack more",
|
|
||||||
mitigation = "Another None"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class UpdateFinding {
|
|
||||||
@Test
|
|
||||||
fun `update finding successfully`() {
|
|
||||||
val findingId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
webTestClient.post()
|
|
||||||
.uri("/pentests/{findingId}/finding", findingId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isAccepted
|
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
||||||
.expectBody()
|
|
||||||
.jsonPath("$.severity").isEqualTo("HIGH")
|
|
||||||
.jsonPath("$.title").isEqualTo("Updated Bug")
|
|
||||||
.jsonPath("$.description").isEqualTo("Updated OTG-INFO-002 Bug")
|
|
||||||
.jsonPath("$.impact").isEqualTo("Service")
|
|
||||||
.jsonPath("$.affectedUrls").isEmpty
|
|
||||||
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack")
|
|
||||||
.jsonPath("$.mitigation").isEqualTo("Still None")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findingBody = FindingRequestBody(
|
|
||||||
severity = "HIGH",
|
|
||||||
title = "Updated Bug",
|
|
||||||
description = "Updated OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "Still None"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class DeleteFinding {
|
|
||||||
@Test
|
|
||||||
fun `delete finding successfully`() {
|
|
||||||
val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
||||||
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
|
||||||
webTestClient.delete()
|
|
||||||
.uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId)
|
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
|
||||||
.exchange()
|
|
||||||
.expectStatus().isOk
|
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
||||||
.expectBody()
|
|
||||||
.jsonPath("$.id").isEqualTo("ab62d365-1b1d-4da1-89bc-5496616e220f")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun persistBasicTestScenario() {
|
private fun persistBasicTestScenario() {
|
||||||
// setup test data
|
// setup test data
|
||||||
// project
|
// project
|
||||||
|
@ -325,7 +185,7 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
category = PentestCategory.INFORMATION_GATHERING,
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
refNumber = "OTG-INFO-002",
|
refNumber = "OTG-INFO-002",
|
||||||
status = PentestStatus.IN_PROGRESS,
|
status = PentestStatus.IN_PROGRESS,
|
||||||
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
findingIds = emptyList(),
|
||||||
commentIds = emptyList()
|
commentIds = emptyList()
|
||||||
)
|
)
|
||||||
val pentestThree = Pentest(
|
val pentestThree = Pentest(
|
||||||
|
@ -337,23 +197,11 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
findingIds = emptyList(),
|
findingIds = emptyList(),
|
||||||
commentIds = emptyList()
|
commentIds = emptyList()
|
||||||
)
|
)
|
||||||
// Finding
|
|
||||||
val findingOne = Finding(
|
|
||||||
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
||||||
severity = Severity.LOW,
|
|
||||||
title = "Found Bug",
|
|
||||||
description = "OTG-INFO-002 Bug",
|
|
||||||
impact = "Service",
|
|
||||||
affectedUrls = emptyList(),
|
|
||||||
reproduction = "Step 1: Hack",
|
|
||||||
mitigation = "None"
|
|
||||||
)
|
|
||||||
// persist test data in database
|
// persist test data in database
|
||||||
mongoTemplate.save(ProjectEntity(projectOne))
|
mongoTemplate.save(ProjectEntity(projectOne))
|
||||||
mongoTemplate.save(PentestEntity(pentestOne))
|
mongoTemplate.save(PentestEntity(pentestOne))
|
||||||
mongoTemplate.save(PentestEntity(pentestTwo))
|
mongoTemplate.save(PentestEntity(pentestTwo))
|
||||||
mongoTemplate.save(PentestEntity(pentestThree))
|
mongoTemplate.save(PentestEntity(pentestThree))
|
||||||
mongoTemplate.save(FindingEntity(findingOne))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureAdminToken() {
|
private fun configureAdminToken() {
|
||||||
|
@ -363,7 +211,6 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
private fun cleanUp() {
|
private fun cleanUp() {
|
||||||
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
||||||
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
||||||
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
|
|
||||||
|
|
||||||
tokenAdmin = "n/a"
|
tokenAdmin = "n/a"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.github.tomakehurst.wiremock.common.Json
|
import com.github.tomakehurst.wiremock.common.Json
|
||||||
|
@ -10,6 +10,10 @@ import com.securityc4po.api.pentest.Pentest
|
||||||
import com.securityc4po.api.pentest.PentestCategory
|
import com.securityc4po.api.pentest.PentestCategory
|
||||||
import com.securityc4po.api.pentest.PentestEntity
|
import com.securityc4po.api.pentest.PentestEntity
|
||||||
import com.securityc4po.api.pentest.PentestStatus
|
import com.securityc4po.api.pentest.PentestStatus
|
||||||
|
import com.securityc4po.api.pentest.comment.Comment
|
||||||
|
import com.securityc4po.api.pentest.comment.CommentEntity
|
||||||
|
import com.securityc4po.api.pentest.comment.CommentRequestBody
|
||||||
|
import com.securityc4po.api.pentest.comment.toCommentResponseBody
|
||||||
import com.securityc4po.api.project.Project
|
import com.securityc4po.api.project.Project
|
||||||
import com.securityc4po.api.project.ProjectEntity
|
import com.securityc4po.api.project.ProjectEntity
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
|
@ -1,4 +1,4 @@
|
||||||
package com.securityc4po.api.comment
|
package com.securityc4po.api.pentest.comment
|
||||||
|
|
||||||
import com.github.tomakehurst.wiremock.common.Json
|
import com.github.tomakehurst.wiremock.common.Json
|
||||||
import com.securityc4po.api.BaseIntTest
|
import com.securityc4po.api.BaseIntTest
|
||||||
|
@ -9,6 +9,10 @@ import com.securityc4po.api.pentest.Pentest
|
||||||
import com.securityc4po.api.pentest.PentestCategory
|
import com.securityc4po.api.pentest.PentestCategory
|
||||||
import com.securityc4po.api.pentest.PentestEntity
|
import com.securityc4po.api.pentest.PentestEntity
|
||||||
import com.securityc4po.api.pentest.PentestStatus
|
import com.securityc4po.api.pentest.PentestStatus
|
||||||
|
import com.securityc4po.api.pentest.comment.Comment
|
||||||
|
import com.securityc4po.api.pentest.comment.CommentEntity
|
||||||
|
import com.securityc4po.api.pentest.comment.CommentRequestBody
|
||||||
|
import com.securityc4po.api.pentest.comment.toCommentResponseBody
|
||||||
import com.securityc4po.api.project.Project
|
import com.securityc4po.api.project.Project
|
||||||
import com.securityc4po.api.project.ProjectEntity
|
import com.securityc4po.api.project.ProjectEntity
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
|
@ -0,0 +1,403 @@
|
||||||
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.github.tomakehurst.wiremock.common.Json
|
||||||
|
import com.securityc4po.api.BaseDocumentationIntTest
|
||||||
|
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
||||||
|
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||||
|
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
||||||
|
import com.securityc4po.api.pentest.Pentest
|
||||||
|
import com.securityc4po.api.pentest.PentestCategory
|
||||||
|
import com.securityc4po.api.pentest.PentestEntity
|
||||||
|
import com.securityc4po.api.pentest.PentestStatus
|
||||||
|
import com.securityc4po.api.project.Project
|
||||||
|
import com.securityc4po.api.project.ProjectEntity
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.data.mongodb.core.MongoTemplate
|
||||||
|
import org.springframework.data.mongodb.core.query.Query
|
||||||
|
import org.springframework.restdocs.operation.preprocess.Preprocessors
|
||||||
|
import org.springframework.restdocs.payload.JsonFieldType
|
||||||
|
import org.springframework.restdocs.payload.PayloadDocumentation
|
||||||
|
import org.springframework.restdocs.request.RequestDocumentation
|
||||||
|
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
|
@SuppressFBWarnings(
|
||||||
|
SIC_INNER_SHOULD_BE_STATIC,
|
||||||
|
NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,
|
||||||
|
RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||||
|
)
|
||||||
|
class FindingControllerDocumentationTest: BaseDocumentationIntTest() {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var mongoTemplate: MongoTemplate
|
||||||
|
var mapper = ObjectMapper()
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
configureAdminToken()
|
||||||
|
persistBasicTestScenario()
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
fun destroy() {
|
||||||
|
cleanUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class GetFindings {
|
||||||
|
@Test
|
||||||
|
fun getFindingsByPentestId() {
|
||||||
|
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
webTestClient.get()
|
||||||
|
.uri("/pentests/{pentestId}/findings", pentestTwoId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().doesNotExist("")
|
||||||
|
.expectBody().json(Json.write(getFindingsResponse()))
|
||||||
|
.consumeWith(
|
||||||
|
WebTestClientRestDocumentation.document(
|
||||||
|
"{methodName}",
|
||||||
|
Preprocessors.preprocessRequest(
|
||||||
|
Preprocessors.prettyPrint(),
|
||||||
|
Preprocessors.modifyUris().removePort(),
|
||||||
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
|
),
|
||||||
|
Preprocessors.preprocessResponse(
|
||||||
|
Preprocessors.prettyPrint()
|
||||||
|
),
|
||||||
|
RequestDocumentation.relaxedPathParameters(
|
||||||
|
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to get the findings for")
|
||||||
|
),
|
||||||
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
|
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the requested findings"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].severity").type(JsonFieldType.STRING)
|
||||||
|
.description("The severity of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
|
||||||
|
.description("The title of the requested finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING)
|
||||||
|
.description("The description number of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].impact").type(JsonFieldType.STRING)
|
||||||
|
.description("The impact of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].affectedUrls").type(JsonFieldType.ARRAY)
|
||||||
|
.description("List of affected Urls of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].reproduction").type(JsonFieldType.STRING)
|
||||||
|
.description("The reproduction steps of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].mitigation").type(JsonFieldType.STRING)
|
||||||
|
.description("The example mitigation for the finding")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingOne = Finding(
|
||||||
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
||||||
|
severity = Severity.LOW,
|
||||||
|
title = "Found Bug",
|
||||||
|
description = "OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "None"
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getFindingsResponse() = listOf(
|
||||||
|
findingOne.toFindingResponseBody()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class GetFinding {
|
||||||
|
@Test
|
||||||
|
fun getFindingById() {
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.get()
|
||||||
|
.uri("/pentests/{findingId}/finding", findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().doesNotExist("")
|
||||||
|
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
|
||||||
|
.consumeWith(
|
||||||
|
WebTestClientRestDocumentation.document(
|
||||||
|
"{methodName}",
|
||||||
|
Preprocessors.preprocessRequest(
|
||||||
|
Preprocessors.prettyPrint(),
|
||||||
|
Preprocessors.modifyUris().removePort(),
|
||||||
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
|
),
|
||||||
|
Preprocessors.preprocessResponse(
|
||||||
|
Preprocessors.prettyPrint()
|
||||||
|
),
|
||||||
|
RequestDocumentation.relaxedPathParameters(
|
||||||
|
RequestDocumentation.parameterWithName("findingId").description("The id of the feinidng you want to get")
|
||||||
|
),
|
||||||
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the requested finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
||||||
|
.description("The severity of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
.description("The title of the requested finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
|
||||||
|
.description("The description number of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
|
||||||
|
.description("The impact of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
|
||||||
|
.description("List of affected Urls of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
|
||||||
|
.description("The reproduction steps of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
|
||||||
|
.description("The example mitigation for the finding")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingOne = Finding(
|
||||||
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
||||||
|
severity = Severity.LOW,
|
||||||
|
title = "Found Bug",
|
||||||
|
description = "OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "None"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class SaveFinding {
|
||||||
|
@Test
|
||||||
|
fun saveFindingByPentestId() {
|
||||||
|
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
webTestClient.post()
|
||||||
|
.uri("/pentests/{pentestId}/finding", pentestTwoId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isAccepted
|
||||||
|
.expectHeader().doesNotExist("")
|
||||||
|
.expectBody().json(Json.write(findingBody))
|
||||||
|
.consumeWith(
|
||||||
|
WebTestClientRestDocumentation.document(
|
||||||
|
"{methodName}",
|
||||||
|
Preprocessors.preprocessRequest(
|
||||||
|
Preprocessors.prettyPrint(),
|
||||||
|
Preprocessors.modifyUris().removePort(),
|
||||||
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
|
),
|
||||||
|
Preprocessors.preprocessResponse(
|
||||||
|
Preprocessors.prettyPrint()
|
||||||
|
),
|
||||||
|
RequestDocumentation.relaxedPathParameters(
|
||||||
|
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to save the finding for")
|
||||||
|
),
|
||||||
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the saved finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
||||||
|
.description("The severity of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
.description("The title of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
|
||||||
|
.description("The description number of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
|
||||||
|
.description("The impact of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
|
||||||
|
.description("List of affected Urls of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
|
||||||
|
.description("The reproduction steps of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
|
||||||
|
.description("The example mitigation for the finding")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingBody = FindingRequestBody(
|
||||||
|
severity = "MEDIUM",
|
||||||
|
title = "Found another Bug",
|
||||||
|
description = "Another OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack more",
|
||||||
|
mitigation = "Another None"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class UpdateFinding {
|
||||||
|
@Test
|
||||||
|
fun updateFindingById() {
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.patch()
|
||||||
|
.uri("/pentests/{findingId}/finding", findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isAccepted
|
||||||
|
.expectHeader().doesNotExist("")
|
||||||
|
.expectBody().json(Json.write(findingBody))
|
||||||
|
.consumeWith(
|
||||||
|
WebTestClientRestDocumentation.document(
|
||||||
|
"{methodName}",
|
||||||
|
Preprocessors.preprocessRequest(
|
||||||
|
Preprocessors.prettyPrint(),
|
||||||
|
Preprocessors.modifyUris().removePort(),
|
||||||
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
|
),
|
||||||
|
Preprocessors.preprocessResponse(
|
||||||
|
Preprocessors.prettyPrint()
|
||||||
|
),
|
||||||
|
RequestDocumentation.relaxedPathParameters(
|
||||||
|
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to update")
|
||||||
|
),
|
||||||
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the updated finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
||||||
|
.description("The severity of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
.description("The title of the requested finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
|
||||||
|
.description("The description number of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("impact").type(JsonFieldType.STRING)
|
||||||
|
.description("The impact of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("affectedUrls").type(JsonFieldType.ARRAY)
|
||||||
|
.description("List of affected Urls of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("reproduction").type(JsonFieldType.STRING)
|
||||||
|
.description("The reproduction steps of the finding"),
|
||||||
|
PayloadDocumentation.fieldWithPath("mitigation").type(JsonFieldType.STRING)
|
||||||
|
.description("The example mitigation for the finding")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingBody = FindingRequestBody(
|
||||||
|
severity = "HIGH",
|
||||||
|
title = "Updated Bug",
|
||||||
|
description = "Updated OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "Still None"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class DeleteFinding {
|
||||||
|
@Test
|
||||||
|
fun deleteFindingByPentestAndFindingId() {
|
||||||
|
val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.delete()
|
||||||
|
.uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().doesNotExist("")
|
||||||
|
.expectBody()
|
||||||
|
.consumeWith(
|
||||||
|
WebTestClientRestDocumentation.document(
|
||||||
|
"{methodName}",
|
||||||
|
Preprocessors.preprocessRequest(
|
||||||
|
Preprocessors.prettyPrint(),
|
||||||
|
Preprocessors.modifyUris().removePort(),
|
||||||
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
|
),
|
||||||
|
Preprocessors.preprocessResponse(
|
||||||
|
Preprocessors.prettyPrint()
|
||||||
|
),
|
||||||
|
RequestDocumentation.relaxedPathParameters(
|
||||||
|
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to remove the finidng from"),
|
||||||
|
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to delete")
|
||||||
|
),
|
||||||
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the finding you deleted")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun persistBasicTestScenario() {
|
||||||
|
// setup test data
|
||||||
|
// Project
|
||||||
|
val projectOne = Project(
|
||||||
|
id = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
client = "E Corp",
|
||||||
|
title = "Some Mock API (v1.0) Scanning",
|
||||||
|
createdAt = "2021-01-10T18:05:00Z",
|
||||||
|
tester = "Novatester",
|
||||||
|
projectPentests = emptyList(),
|
||||||
|
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||||
|
)
|
||||||
|
// Pentests
|
||||||
|
val pentestOne = Pentest(
|
||||||
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
||||||
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
|
refNumber = "OTG-INFO-001",
|
||||||
|
status = PentestStatus.NOT_STARTED,
|
||||||
|
findingIds = emptyList(),
|
||||||
|
commentIds = emptyList()
|
||||||
|
)
|
||||||
|
val pentestTwo = Pentest(
|
||||||
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
||||||
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
|
refNumber = "OTG-INFO-002",
|
||||||
|
status = PentestStatus.IN_PROGRESS,
|
||||||
|
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
||||||
|
commentIds = emptyList()
|
||||||
|
)
|
||||||
|
val pentestThree = Pentest(
|
||||||
|
id = "74eae112-f62c-11ec-b939-0242ac120002",
|
||||||
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
category = PentestCategory.AUTHENTICATION_TESTING,
|
||||||
|
refNumber = "OTG-AUTHN-001",
|
||||||
|
status = PentestStatus.COMPLETED,
|
||||||
|
findingIds = emptyList(),
|
||||||
|
commentIds = emptyList()
|
||||||
|
)
|
||||||
|
// Finding
|
||||||
|
val findingOne = Finding(
|
||||||
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
||||||
|
severity = Severity.LOW,
|
||||||
|
title = "Found Bug",
|
||||||
|
description = "OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "None"
|
||||||
|
)
|
||||||
|
// persist test data in database
|
||||||
|
mongoTemplate.save(ProjectEntity(projectOne))
|
||||||
|
mongoTemplate.save(PentestEntity(pentestOne))
|
||||||
|
mongoTemplate.save(PentestEntity(pentestTwo))
|
||||||
|
mongoTemplate.save(PentestEntity(pentestThree))
|
||||||
|
mongoTemplate.save(FindingEntity(findingOne))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureAdminToken() {
|
||||||
|
tokenAdmin = getAccessToken("test_admin", "test", "c4po_local", "c4po_realm_local")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanUp() {
|
||||||
|
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
||||||
|
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
||||||
|
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
|
||||||
|
|
||||||
|
tokenAdmin = "n/a"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
package com.securityc4po.api.pentest.finding
|
||||||
|
|
||||||
|
import com.github.tomakehurst.wiremock.common.Json
|
||||||
|
import com.securityc4po.api.BaseIntTest
|
||||||
|
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
||||||
|
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||||
|
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
||||||
|
import com.securityc4po.api.pentest.Pentest
|
||||||
|
import com.securityc4po.api.pentest.PentestCategory
|
||||||
|
import com.securityc4po.api.pentest.PentestEntity
|
||||||
|
import com.securityc4po.api.pentest.PentestStatus
|
||||||
|
import com.securityc4po.api.project.Project
|
||||||
|
import com.securityc4po.api.project.ProjectEntity
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.web.server.LocalServerPort
|
||||||
|
import org.springframework.data.mongodb.core.MongoTemplate
|
||||||
|
import org.springframework.data.mongodb.core.query.Query
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
@SuppressFBWarnings(
|
||||||
|
SIC_INNER_SHOULD_BE_STATIC,
|
||||||
|
NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,
|
||||||
|
RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
||||||
|
)
|
||||||
|
class FindingControllerIntTest: BaseIntTest() {
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private var port = 0
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var mongoTemplate: MongoTemplate
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var webTestClient: WebTestClient
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setupWebClient() {
|
||||||
|
webTestClient = WebTestClient.bindToServer()
|
||||||
|
.baseUrl("http://localhost:$port")
|
||||||
|
.responseTimeout(Duration.ofMillis(10000))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
configureAdminToken()
|
||||||
|
persistBasicTestScenario()
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
fun destroy() {
|
||||||
|
cleanUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class GetFindings {
|
||||||
|
@Test
|
||||||
|
fun `requesting findings by pentestId successfully`() {
|
||||||
|
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
webTestClient.get()
|
||||||
|
.uri("/pentests/{pentestId}/findings", pentestTwoId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
|
.expectBody().json(Json.write(getFindings()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingOne = Finding(
|
||||||
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
||||||
|
severity = Severity.LOW,
|
||||||
|
title = "Found Bug",
|
||||||
|
description = "OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "None"
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getFindings() = listOf(
|
||||||
|
findingOne.toFindingResponseBody()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class GetFinding {
|
||||||
|
@Test
|
||||||
|
fun `requesting finding by findingId successfully`() {
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.get()
|
||||||
|
.uri("/pentests/{findingId}/finding", findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
|
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingOne = Finding(
|
||||||
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
||||||
|
severity = Severity.LOW,
|
||||||
|
title = "Found Bug",
|
||||||
|
description = "OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "None"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class SaveFinding {
|
||||||
|
@Test
|
||||||
|
fun `save finding successfully`() {
|
||||||
|
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
webTestClient.post()
|
||||||
|
.uri("/pentests/{pentestId}/finding", pentestTwoId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isAccepted
|
||||||
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
|
.expectBody()
|
||||||
|
.jsonPath("$.severity").isEqualTo("MEDIUM")
|
||||||
|
.jsonPath("$.title").isEqualTo("Found another Bug")
|
||||||
|
.jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug")
|
||||||
|
.jsonPath("$.impact").isEqualTo("Service")
|
||||||
|
.jsonPath("$.affectedUrls").isEmpty
|
||||||
|
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack more")
|
||||||
|
.jsonPath("$.mitigation").isEqualTo("Another None")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingBody = FindingRequestBody(
|
||||||
|
severity = "MEDIUM",
|
||||||
|
title = "Found another Bug",
|
||||||
|
description = "Another OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack more",
|
||||||
|
mitigation = "Another None"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class UpdateFinding {
|
||||||
|
@Test
|
||||||
|
fun `update finding successfully`() {
|
||||||
|
val findingId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
webTestClient.post()
|
||||||
|
.uri("/pentests/{findingId}/finding", findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isAccepted
|
||||||
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
|
.expectBody()
|
||||||
|
.jsonPath("$.severity").isEqualTo("HIGH")
|
||||||
|
.jsonPath("$.title").isEqualTo("Updated Bug")
|
||||||
|
.jsonPath("$.description").isEqualTo("Updated OTG-INFO-002 Bug")
|
||||||
|
.jsonPath("$.impact").isEqualTo("Service")
|
||||||
|
.jsonPath("$.affectedUrls").isEmpty
|
||||||
|
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack")
|
||||||
|
.jsonPath("$.mitigation").isEqualTo("Still None")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val findingBody = FindingRequestBody(
|
||||||
|
severity = "HIGH",
|
||||||
|
title = "Updated Bug",
|
||||||
|
description = "Updated OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "Still None"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class DeleteFinding {
|
||||||
|
@Test
|
||||||
|
fun `delete finding successfully`() {
|
||||||
|
val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.delete()
|
||||||
|
.uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
|
.expectBody()
|
||||||
|
.jsonPath("$.id").isEqualTo("ab62d365-1b1d-4da1-89bc-5496616e220f")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun persistBasicTestScenario() {
|
||||||
|
// setup test data
|
||||||
|
// project
|
||||||
|
val projectOne = Project(
|
||||||
|
id = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
client = "E Corp",
|
||||||
|
title = "Some Mock API (v1.0) Scanning",
|
||||||
|
createdAt = "2021-01-10T18:05:00Z",
|
||||||
|
tester = "Novatester",
|
||||||
|
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||||
|
)
|
||||||
|
// pentests
|
||||||
|
val pentestOne = Pentest(
|
||||||
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
||||||
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
|
refNumber = "OTG-INFO-001",
|
||||||
|
status = PentestStatus.NOT_STARTED,
|
||||||
|
findingIds = emptyList(),
|
||||||
|
commentIds = emptyList()
|
||||||
|
)
|
||||||
|
val pentestTwo = Pentest(
|
||||||
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
||||||
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
category = PentestCategory.INFORMATION_GATHERING,
|
||||||
|
refNumber = "OTG-INFO-002",
|
||||||
|
status = PentestStatus.IN_PROGRESS,
|
||||||
|
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
||||||
|
commentIds = emptyList()
|
||||||
|
)
|
||||||
|
val pentestThree = Pentest(
|
||||||
|
id = "16vbc63c-f624-11ec-b939-0242ac120002",
|
||||||
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
||||||
|
category = PentestCategory.AUTHENTICATION_TESTING,
|
||||||
|
refNumber = "OTG-AUTHN-001",
|
||||||
|
status = PentestStatus.COMPLETED,
|
||||||
|
findingIds = emptyList(),
|
||||||
|
commentIds = emptyList()
|
||||||
|
)
|
||||||
|
// Finding
|
||||||
|
val findingOne = Finding(
|
||||||
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
||||||
|
severity = Severity.LOW,
|
||||||
|
title = "Found Bug",
|
||||||
|
description = "OTG-INFO-002 Bug",
|
||||||
|
impact = "Service",
|
||||||
|
affectedUrls = emptyList(),
|
||||||
|
reproduction = "Step 1: Hack",
|
||||||
|
mitigation = "None"
|
||||||
|
)
|
||||||
|
// persist test data in database
|
||||||
|
mongoTemplate.save(ProjectEntity(projectOne))
|
||||||
|
mongoTemplate.save(PentestEntity(pentestOne))
|
||||||
|
mongoTemplate.save(PentestEntity(pentestTwo))
|
||||||
|
mongoTemplate.save(PentestEntity(pentestThree))
|
||||||
|
mongoTemplate.save(FindingEntity(findingOne))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureAdminToken() {
|
||||||
|
tokenAdmin = getAccessToken("test_admin", "test", "c4po_local", "c4po_realm_local")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanUp() {
|
||||||
|
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
||||||
|
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
||||||
|
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
|
||||||
|
|
||||||
|
tokenAdmin = "n/a"
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
"data": {
|
"data": {
|
||||||
"_id": "89703b19-16c7-49e5-8e33-0c706313e5fe",
|
"_id": "89703b19-16c7-49e5-8e33-0c706313e5fe",
|
||||||
"title": "Test Comment",
|
"title": "Test Comment",
|
||||||
"description": "Test Comment Description",
|
"description": "No related findings",
|
||||||
"relatedFindings": []
|
"relatedFindings": []
|
||||||
},
|
},
|
||||||
"_class": "com.securityc4po.api.comment.CommentEntity"
|
"_class": "com.securityc4po.api.comment.CommentEntity"
|
||||||
|
@ -25,8 +25,8 @@
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"_id": "df516de6-ca5e-44a6-ac50-db89bb17aac3",
|
"_id": "df516de6-ca5e-44a6-ac50-db89bb17aac3",
|
||||||
"title": "New Test",
|
"title": "New Test Comment",
|
||||||
"description": "New Test",
|
"description": "Two related findings",
|
||||||
"relatedFindings": [
|
"relatedFindings": [
|
||||||
"0bda8950-94fa-4ec6-8fa7-e09f5a8cd3e8",
|
"0bda8950-94fa-4ec6-8fa7-e09f5a8cd3e8",
|
||||||
"4ddb84f6-068c-4319-a8ee-1000008bb75a"
|
"4ddb84f6-068c-4319-a8ee-1000008bb75a"
|
||||||
|
@ -44,8 +44,8 @@
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"_id": "e55e943b-6a48-4d84-8d72-b48d7d9de5b7",
|
"_id": "e55e943b-6a48-4d84-8d72-b48d7d9de5b7",
|
||||||
"title": "Wow another one?",
|
"title": "Another Test Comment",
|
||||||
"description": "Epic!",
|
"description": "One related findings",
|
||||||
"relatedFindings": [
|
"relatedFindings": [
|
||||||
"5e22d38f-a4f6-4809-84ea-a803b5f1f9fc"
|
"5e22d38f-a4f6-4809-84ea-a803b5f1f9fc"
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue