feat: added pentest table and added ngrx store for project management

This commit is contained in:
mhg 2022-04-21 15:09:44 +02:00 committed by GitHub
parent 501a6d3427
commit 5d5dbe95fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 10077 additions and 5268 deletions

View File

@ -1,5 +1,5 @@
# base image
FROM node:12.13.1
FROM node:14.15.3
# set working directory
WORKDIR /app
@ -10,7 +10,7 @@ ENV PATH /app/node_modules/.bin:$PATH
# install and cache app dependencies
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@10.2.0
RUN npm install -g @angular/cli@12.2.17
# add app
COPY . /app

View File

@ -22,7 +22,6 @@
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/assets/images/favicons/favicon.ico",
"src/assets/images/favicons/corporate_favicon.ico",
@ -40,7 +39,13 @@
"deep-equal",
"moment-timezone",
"uuid"
]
],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
@ -53,9 +58,7 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
@ -71,7 +74,8 @@
}
]
}
}
},
"defaultConfiguration": ""
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",

File diff suppressed because it is too large Load Diff

View File

@ -11,55 +11,56 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^11.0.3",
"@angular/cdk": "^10.2.7",
"@angular/common": "^10.2.3",
"@angular/compiler": "~10.2.0",
"@angular/core": "~10.2.0",
"@angular/animations": "^12.2.16",
"@angular/cdk": "^12.2.7",
"@angular/common": "^12.2.16",
"@angular/compiler": "~12.2.16",
"@angular/core": "~12.2.16",
"@angular/flex-layout": "^11.0.0-beta.33",
"@angular/forms": "~10.2.0",
"@angular/localize": "^11.0.2",
"@angular/platform-browser": "~10.2.0",
"@angular/platform-browser-dynamic": "~10.2.0",
"@angular/router": "~10.2.0",
"@angular/forms": "~12.2.16",
"@angular/localize": "^12.2.16",
"@angular/platform-browser": "~12.2.16",
"@angular/platform-browser-dynamic": "~12.2.16",
"@angular/router": "~12.2.16",
"@briebug/jest-schematic": "^3.0.0",
"@fortawesome/angular-fontawesome": "^0.8.0",
"@fortawesome/fontawesome-common-types": "^0.2.32",
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@nebular/eva-icons": "^6.2.1",
"@nebular/theme": "^6.2.1",
"@fortawesome/angular-fontawesome": "^0.8.2",
"@fortawesome/fontawesome-common-types": "^0.2.36",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-regular-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@nebular/eva-icons": "^8.0.0",
"@nebular/theme": "^8.0.0",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"@ngxs/store": "^3.7.0",
"@ngxs/store": "^3.7.3",
"eva-icons": "^1.1.3",
"i18n-iso-countries": "^6.2.2",
"i18n-iso-countries": "^6.8.0",
"jwt-decode": "^3.1.2",
"keycloak-angular": "^8.1.0",
"keycloak-js": "^13.0.0",
"keycloak-angular": "^8.4.0",
"keycloak-js": "^13.0.1",
"moment": "^2.29.1",
"moment-timezone": "latest",
"ng-mocks": "^13.4.2",
"ngx-moment": "^5.0.0",
"ngx-take-until-destroy": "^5.4.0",
"ngx-translate-testing": "^5.0.0",
"ngx-translate-testing": "^5.2.0",
"roboto-fontface": "^0.10.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"uuid": "^8.3.1",
"zone.js": "~0.10.2"
"rxjs": "^6.6.7",
"tslib": "^2.3.1",
"uuid": "^8.3.2",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-builders/jest": "10.0.1",
"@angular-devkit/build-angular": "~0.1002.0",
"@angular/cli": "~10.2.0",
"@angular/compiler-cli": "~10.2.0",
"@schematics/angular": "~10.2.0",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@angular-devkit/build-angular": "~12.2.16",
"@angular/cli": "~12.2.16",
"@angular/compiler-cli": "~12.2.16",
"@schematics/angular": "^10.2.4",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "^2.0.10",
"@types/jest": "26.0.15",
"@types/node": "^12.20.33",
"codelyzer": "^6.0.0",
"@types/node": "^12.20.47",
"codelyzer": "^6.0.2",
"font-awesome": "^4.7.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
@ -67,6 +68,9 @@
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
"typescript": "~4.3.5"
},
"resolutions": {
"webpack": "^5.0.0"
}
}

View File

@ -27,7 +27,7 @@ const routes: Routes = [
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -29,6 +29,8 @@ import {FlexLayoutModule} from '@angular/flex-layout';
import {DialogService} from '@shared/services/dialog-service/dialog.service';
import {ConfirmDialogModule} from '@shared/modules/confirm-dialog/confirm-dialog.module';
import {OverlayContainer} from '@angular/cdk/overlay';
import {NgxsLoggerPluginModule} from '@shared/stores/plugins/store-logger-plugin';
import {ProjectState} from '@shared/stores/project-state/project-state';
@NgModule({
declarations: [
@ -50,7 +52,8 @@ import {OverlayContainer} from '@angular/cdk/overlay';
NbEvaIconsModule,
NbSelectModule,
ConfirmDialogModule,
NgxsModule.forRoot([SessionState], {developmentMode: !environment.production}),
NgxsModule.forRoot([SessionState, ProjectState], {developmentMode: !environment.production}),
NgxsLoggerPluginModule.forRoot({developmentMode: !environment.production}),
HttpClientModule,
TranslateModule.forRoot({
loader: {

View File

@ -3,12 +3,13 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HeaderComponent} from './header.component';
import {CommonModule} from '@angular/common';
import {FontAwesomeTestingModule} from '@fortawesome/angular-fontawesome/testing';
import {NbActionsModule} from '@nebular/theme';
import {NbActionsModule, NbSelectModule} from '@nebular/theme';
import {ThemeModule} from '@assets/@theme/theme.module';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpLoaderFactory} from '../common-app.module';
import {HttpClient} from '@angular/common/http';
import {RouterTestingModule} from '@angular/router/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing';
describe('HeaderComponent', () => {
let component: HeaderComponent;
@ -22,7 +23,9 @@ describe('HeaderComponent', () => {
imports: [
CommonModule,
NbActionsModule,
NbSelectModule,
FontAwesomeTestingModule,
HttpClientTestingModule,
ThemeModule.forRoot(),
TranslateModule.forRoot({
loader: {

View File

@ -1,9 +1,8 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule, Routes} from '@angular/router';
import {ProjectComponent} from '../project-overview/project/project.component';
const routes: Routes = [];
const routes: Routes = [
];
@NgModule({
imports: [RouterModule.forChild(routes)],

View File

@ -3,7 +3,11 @@ import {CommonModule} from '@angular/common';
import {PentestHeaderComponent} from './pentest-header/pentest-header.component';
import {PentestCategoriesComponent} from './pentest-categories/pentest-categories.component';
import {PentestTableComponent} from './pentest-table/pentest-table.component';
import {NbLayoutModule} from '@nebular/theme';
import {NbCardModule, NbLayoutModule, NbTreeGridModule} from '@nebular/theme';
import {TranslateModule} from '@ngx-translate/core';
import {StatusTagModule} from '@shared/widgets/status-tag/status-tag.module';
import {FindigWidgetModule} from '@shared/widgets/findig-widget/findig-widget.module';
import {RouterModule} from '@angular/router';
@NgModule({
declarations: [
@ -18,7 +22,13 @@ import {NbLayoutModule} from '@nebular/theme';
],
imports: [
CommonModule,
NbLayoutModule
NbLayoutModule,
NbCardModule,
NbTreeGridModule,
TranslateModule,
StatusTagModule,
FindigWidgetModule,
RouterModule
]
})
export class PentestOverviewModule {

View File

@ -1 +1,54 @@
<p>pentest-table works!</p>
<nb-card class="pentest-table">
<table [nbTreeGrid]="dataSource">
<tr nbTreeGridHeaderRow *nbTreeGridHeaderRowDef="columns"></tr>
<tr nbTreeGridRow *nbTreeGridRowDef="let pentest; columns: columns"
class="pentest-cell"
routerLink="pentest"
fragment="{{pentest.data['refNumber']}}"
[skipLocationChange]="true">
</tr>
<!-- Test ID -->
<ng-container [nbTreeGridColumnDef]="columns[0]">
<th nbTreeGridHeaderCell *nbTreeGridHeaderCellDef>
{{ 'pentest.testId' | translate }}
</th>
<td nbTreeGridCell *nbTreeGridCellDef="let pentest">
<!-- Opens sub categories if row needs to be extendend -->
<nb-tree-grid-row-toggle
[expanded]="pentest.expanded"
*ngIf="pentest.data?.childEntries?.length > 0">
</nb-tree-grid-row-toggle>
<!---->
{{pentest.data['refNumber'] || '-'}}
</td>
</ng-container>
<!-- Title -->
<ng-container [nbTreeGridColumnDef]="columns[1]">
<th nbTreeGridHeaderCell *nbTreeGridHeaderCellDef>
{{ 'pentest.title' | translate }}
</th>
<td nbTreeGridCell *nbTreeGridCellDef="let pentest">
{{ getTitle(pentest.data['refNumber']) | translate }}
</td>
</ng-container>
<!-- Status -->
<ng-container [nbTreeGridColumnDef]="columns[2]">
<th nbTreeGridHeaderCell *nbTreeGridHeaderCellDef>
{{ 'pentest.status' | translate }}
</th>
<td nbTreeGridCell *nbTreeGridCellDef="let pentest">
<app-status-tag [currentStatus]="pentest.data['status']"></app-status-tag>
</td>
</ng-container>
<!-- Findings -->
<ng-container [nbTreeGridColumnDef]="columns[3]">
<th nbTreeGridHeaderCell *nbTreeGridHeaderCellDef>
{{ 'pentest.findings' | translate }}
</th>
<td nbTreeGridCell *nbTreeGridCellDef="let pentest">
<app-findig-widget [numberOfFindigs]="pentest.data['findings']"></app-findig-widget>
<!--{{pentest.data['findings'] || '-'}}-->
</td>
</ng-container>
</table>
</nb-card>

View File

@ -0,0 +1,13 @@
@import '../../../assets/@theme/styles/themes';
.pentest-table {
.pentest-cell {
// Add style here
}
.pentest-cell:hover {
cursor: pointer;
background-color: nb-theme(color-basic-transparent-focus);
}
}

View File

@ -1,6 +1,19 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { PentestTableComponent } from './pentest-table.component';
import {PentestTableComponent} from './pentest-table.component';
import {NbCardModule, NbTreeGridModule} from '@nebular/theme';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpLoaderFactory} from '../../common-app.module';
import {HttpClient} from '@angular/common/http';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {ThemeModule} from '@assets/@theme/theme.module';
import {RouterTestingModule} from '@angular/router/testing';
import {StatusTagComponent} from '@shared/widgets/status-tag/status-tag.component';
import {FindigWidgetComponent} from '@shared/widgets/findig-widget/findig-widget.component';
import {MockComponent} from 'ng-mocks';
import {NgxsModule} from '@ngxs/store';
import {ProjectState} from '@shared/stores/project-state/project-state';
import {HttpClientTestingModule} from '@angular/common/http/testing';
describe('PentestTableComponent', () => {
let component: PentestTableComponent;
@ -8,9 +21,29 @@ describe('PentestTableComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PentestTableComponent ]
declarations: [
PentestTableComponent,
MockComponent(StatusTagComponent),
MockComponent(FindigWidgetComponent)
],
imports: [
BrowserAnimationsModule,
HttpClientTestingModule,
NbCardModule,
NbTreeGridModule,
ThemeModule.forRoot(),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
RouterTestingModule.withRoutes([]),
NgxsModule.forRoot([ProjectState])
]
})
.compileComponents();
.compileComponents();
});
beforeEach(() => {

View File

@ -1,15 +1,74 @@
import { Component, OnInit } from '@angular/core';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {NbGetters, NbTreeGridDataSource, NbTreeGridDataSourceBuilder} from '@nebular/theme';
import {Pentest, PentestEntry, transformPentestsToEntries} from '@shared/models/pentest.model';
import {PentestService} from '@shared/services/pentest.service';
import {Store} from '@ngxs/store';
import {ProjectState} from '@shared/stores/project-state/project-state';
import {untilDestroyed} from 'ngx-take-until-destroy';
import {catchError, switchMap} from 'rxjs/operators';
import {of} from 'rxjs';
import {getTitleKeyForRefNumber} from '@shared/functions/categories/get-title-key-for-ref-number.function';
@Component({
selector: 'app-pentest-table',
templateUrl: './pentest-table.component.html',
styleUrls: ['./pentest-table.component.scss']
})
export class PentestTableComponent implements OnInit {
export class PentestTableComponent implements OnInit, OnDestroy {
constructor() { }
columns: Array<PentestColumns> = [PentestColumns.TEST_ID, PentestColumns.TITLE, PentestColumns.STATUS, PentestColumns.FINDINGS];
dataSource: NbTreeGridDataSource<PentestEntry>;
ngOnInit(): void {
private data: PentestEntry[] = [];
getters: NbGetters<PentestEntry, PentestEntry> = {
dataGetter: (node: PentestEntry) => node,
childrenGetter: (node: PentestEntry) => node.childEntries || undefined,
expandedGetter: (node: PentestEntry) => !!node.expanded,
};
constructor(
private store: Store,
private pentestService: PentestService,
private dataSourceBuilder: NbTreeGridDataSourceBuilder<PentestEntry>
) {
this.dataSource = dataSourceBuilder.create(this.data, this.getters);
}
ngOnInit(): void {
this.loadPentestData();
}
loadPentestData(): void {
this.store.select(ProjectState.selectedCategory).pipe(
switchMap(category => this.pentestService.loadPentests(category)),
catchError(_ => of(null)),
untilDestroyed(this)
).subscribe({
next: (pentests: Pentest[]) => {
this.data = transformPentestsToEntries(pentests);
this.dataSource.setData(this.data, this.getters);
},
error: error => {
console.error(error);
}
});
}
// HTML only
getTitle(refNumber: string): string {
return getTitleKeyForRefNumber(refNumber);
}
ngOnDestroy(): void {
// This method must be present when using ngx-take-until-destroy
// even when empty
}
}
enum PentestColumns {
TEST_ID = 'testId',
TITLE = 'title',
STATUS = 'status',
FINDINGS = 'findings'
}

View File

@ -0,0 +1,2 @@
export {PentestModule} from './pentest.module';
export {PentestRoutingModule} from './pentest-routing.module';

View File

@ -0,0 +1,17 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {PentestComponent} from './pentest.component';
const routes: Routes = [
{
path: '',
component: PentestComponent
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PentestRoutingModule {
}

View File

@ -0,0 +1,5 @@
<nb-layout>
<nb-layout-header>
<p>pentest works!</p>
</nb-layout-header>
</nb-layout>

View File

@ -0,0 +1,35 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {PentestComponent} from './pentest.component';
import {NbLayoutModule} from '@nebular/theme';
import {ThemeModule} from '@assets/@theme/theme.module';
import {RouterTestingModule} from '@angular/router/testing';
describe('PentestComponent', () => {
let component: PentestComponent;
let fixture: ComponentFixture<PentestComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
PentestComponent
],
imports: [
NbLayoutModule,
ThemeModule.forRoot(),
RouterTestingModule.withRoutes([])
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PentestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,17 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-pentest',
templateUrl: './pentest.component.html',
styleUrls: ['./pentest.component.scss']
})
export class PentestComponent implements OnInit {
constructor() { }
ngOnInit(): void {
// tslint:disable-next-line:no-console
console.info('pentest component renderd');
}
}

View File

@ -0,0 +1,21 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {PentestComponent} from './pentest.component';
import {NbLayoutModule} from '@nebular/theme';
@NgModule({
declarations: [
PentestComponent
],
imports: [
CommonModule,
RouterModule.forChild([{
path: '',
component: PentestComponent
}]),
NbLayoutModule
]
})
export class PentestModule {
}

View File

@ -10,7 +10,7 @@ const routes: Routes = [
{
path: 'id',
loadChildren: () => import('./project').then(mod => mod.ProjectModule),
},
}
];
@NgModule({

View File

@ -26,6 +26,7 @@ import {DialogService} from '@shared/services/dialog-service/dialog.service';
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
import {ProjectDialogService} from '@shared/modules/project-dialog/service/project-dialog.service';
import {ProjectDialogServiceMock} from '@shared/modules/project-dialog/service/project-dialog.service.mock';
import {MockComponent, MockPipe} from 'ng-mocks';
describe('ProjectOverviewComponent', () => {
let component: ProjectOverviewComponent;
@ -35,8 +36,8 @@ describe('ProjectOverviewComponent', () => {
await TestBed.configureTestingModule({
declarations: [
ProjectOverviewComponent,
LoadingSpinnerComponent,
DateTimeFormatPipe
MockComponent(LoadingSpinnerComponent),
MockPipe(DateTimeFormatPipe)
],
imports: [
CommonModule,
@ -49,6 +50,7 @@ describe('ProjectOverviewComponent', () => {
TranslateModule,
NbProgressBarModule,
NbSpinnerModule,
HttpClientTestingModule,
ThemeModule.forRoot(),
TranslateModule.forRoot({
loader: {
@ -58,9 +60,7 @@ describe('ProjectOverviewComponent', () => {
}
}),
RouterTestingModule.withRoutes([]),
NgxsModule.forRoot([SessionState]),
HttpClientModule,
HttpClientTestingModule
NgxsModule.forRoot([SessionState])
],
providers: [
KeycloakService,

View File

@ -7,6 +7,10 @@ const routes: Routes = [
path: '',
component: ProjectComponent
},
{
path: 'pentest',
loadChildren: () => import('../../pentest-overview/pentest').then(mod => mod.PentestModule),
},
];
@NgModule({

View File

@ -12,11 +12,12 @@
.categories-column {
width: 20%;
height: calc(100% - #{$pentest-header-height});
height: calc(100vh - #{$pentest-header-height});
}
.table-column {
overflow: auto !important;
width: 80%;
height: calc(100% - #{$pentest-header-height});
height: 80vh;
}
}

View File

@ -1,15 +1,42 @@
import { Component, OnInit } from '@angular/core';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {Store} from '@ngxs/store';
import {InitProjectState} from '@shared/stores/project-state/project-state.actions';
import {Router} from '@angular/router';
import {Route} from '@shared/models/route.enum';
import {untilDestroyed} from 'ngx-take-until-destroy';
import {Project} from '@shared/models/project.model';
@Component({
selector: 'app-project',
templateUrl: './project.component.html',
styleUrls: ['./project.component.scss']
})
export class ProjectComponent implements OnInit {
export class ProjectComponent implements OnInit, OnDestroy {
constructor() { }
ngOnInit(): void {
constructor(
private store: Store,
private readonly router: Router) {
}
ngOnInit(): void {
if (history?.state && 'selectedProject' in history?.state) {
this.initProjectStore();
} else {
this.router.navigate([Route.PROJECT_OVERVIEW]).finally();
}
}
private initProjectStore(): void {
const project: Project = history?.state?.selectedProject ? history?.state?.selectedProject : null;
this.store.dispatch(new InitProjectState(
project,
[],
[]
)).pipe(untilDestroyed(this)).subscribe();
}
ngOnDestroy(): void {
// This method must be present when using ngx-take-until-destroy
// even when empty
}
}

View File

@ -1,2 +1,2 @@
$header-height: 10rem;
$header-height: 8rem;
$pentest-header-height: 8rem;

View File

@ -69,5 +69,143 @@
"delete.success": "Projekt erfolgreich gelöscht",
"delete.failed": "Projekt konnte nicht gelöscht werden"
}
},
"pentest": {
"testId": "Nr.",
"title": "Titel",
"findings": "Funde",
"comments": "Kommentare",
"status": "Status",
"statusText": {
"not_started": "Nicht angefangen",
"disabled": "Deaktiviert",
"open": "Offen",
"checked": "Geprüft",
"reported": "Gemeldet",
"under_review": "Unter Beurteilung",
"triaged": "Triagiert"
},
"info": {
"001": "Nutze Suchmaschinenerkennung und -aufklärung für Informationslecks",
"002": "Fingerabdruck-Webserver",
"003": "Prüfe Webserver-Metadateien auf Informationslecks",
"004": "Anwendungen auf dem Webserver auflisten",
"005": "Prüfe Webseitenkommentare und Metadaten auf Informationslecks",
"006": "Einstiegspunkte für Identitätsanträge",
"007": "Zuordnen von Ausführungspfade der Anwendung",
"008": "Framework für Fingerabdruck-Webanwendungen",
"009": "Fingerabdruck-Webanwendungen",
"010": "Zuordnen der Anwendungsarchitektur"
},
"config": {
"001": "Netzwerk-/Infrastrukturkonfiguration testen",
"002": "Testen Sie die Konfiguration der Anwendungsplattform",
"003": "Testen der Behandlung von Dateierweiterungen für vertrauliche Informationen",
"004": "Backup und nicht referenzierte Dateien für sensible Informationen",
"005": "Aufzählen der Infrastruktur- und Anwendungsverwaltungsschnittstellen",
"006": "HTTP-Methoden testen",
"007": "Testen Sie HTTP Strict Transport Security",
"008": "Testen der domänenübergreifende RIA-Richtlinie"
},
"ident": {
"001": "Rollendefinitionen testen",
"002": "Registrierungsprozess testen",
"003": "Konto-Bereitstellungsprozess testen",
"004": "Testen auf Kontoaufzählung und erratbares Benutzerkonto",
"005": "Test auf schwache oder nicht erzwungene Richtlinie für Benutzernamen",
"006": "Testberechtigungen von Gast-/Schulungskonten",
"007": "Sperrung/Wiederaufnahme des Kontos testen"
},
"authn": {
"001": "Testen auf Anmeldeinformationen, die über einen verschlüsselten Kanal transportiert werden",
"002": "Testen auf Standardanmeldeinformationen",
"003": "Test auf schwachen Sperrmechanismus",
"004": "Testen auf Umgehung des Authentifizierungsschemas",
"005": "Testen der Passwort speichern Funktion",
"006": "Test auf Browser-Cache-Schwäche",
"007": "Testen auf schwache Richtlinie für Kennwörter",
"008": "Prüfung auf schwache Sicherheitsfrage/Antwort",
"009": "Testen auf schwache Passwortänderungs- oder Zurücksetzungsfunktionen",
"010": "Testen auf schwächere Authentifizierung über alternativen Kanal"
},
"authz": {
"001": "Testen von Directory Traversal/File Include",
"002": "Testen auf Umgehung des Autorisierungsschemas",
"003": "Testen auf Rechteausweitung",
"004": "Testen auf unsichere direkte Objektreferenzen"
},
"sess": {
"001": "Testen auf Umgehung des Sitzungsverwaltungsschemas",
"002": "Testen auf Cookies-Attribute",
"003": "Testen auf Sitzungsfixierung",
"004": "Testen auf sichtbare Sitzungsvariablen",
"005": "Prüfung auf Cross-Site-Request-Forgery",
"006": "Testen der Abmeldefunktion",
"007": "Testen der Sitzungszeitüberschreitung",
"008": "Testen auf Session-Rätsel"
},
"inpval": {
"001": "Testen auf reflektiertes Cross-Site-Scripting",
"002": "Testen auf konserviertes Cross Site Scripting",
"003": "Testen auf HTTP-Verb-Manipulation",
"004": "Prüfung auf HTTP-Parameterverschmutzung",
"005": "N/A",
"006": "Testen auf SQL-Injection",
"006_1": "Oracle-Tests",
"006_2": "SQL Server-Tests",
"006_3": "Testen von PostgreSQL",
"006_4": "MS-Access-Tests",
"006_5": "Testen auf NoSQL-Injection",
"007": "Testen auf LDAP-Injection",
"008": "Prüfung auf ORM-Injection",
"009": "Testen auf XML-Injection",
"010": "Prüfung auf SSI-Injection",
"011": "Testen auf XPath-Injection",
"012": "IMAP/SMTP-Injection",
"013": "Testen auf Code-Injection",
"013_1": "Testen der lokalen Dateieinbindung",
"013_2": "Testen der Remote-Dateieinbindung",
"014": "Testen auf BefehlInjection",
"015": "Test auf Pufferüberlauf",
"015_1": "Test auf Heap-Überlauf",
"015_2": "Test auf Stack-Überlauf",
"015_3": "Testen auf Formatzeichenfolge",
"016": "Testen auf inkubierte Schwachstellen",
"017": "Testen auf HTTP-Splitting/-Schmuggel"
},
"err": {
"001": "Analyse von Fehlercodes",
"002": "Analyse von Stack-Traces"
},
"crypst": {
"001": "Testen auf schwache SSL/TSL-Chiffren, unzureichenden Transportschichtschutz",
"002": "Testen für Padding Oracle",
"003": "Prüfung auf sensible Informationen, die über unverschlüsselte Kanäle gesendet werden"
},
"buslogic": {
"001": "Testen Sie die Datenvalidierung der Geschäftslogik",
"002": "Testen Sie die Fähigkeit, Anfragen zu fälschen",
"003": "Integritätsprüfungen testen",
"004": "Testen Sie das Prozesstiming",
"005": "Testen Sie, wie oft eine Funktion verwendet werden kann",
"006": "Prüfung auf Umgehung von Arbeitsabläufen",
"007": "Testen Sie Abwehrmaßnahmen gegen Anwendungsmissbrauch",
"008": "Test-Upload von unerwarteten Dateitypen",
"009": "Test-Upload schädlicher Dateien"
},
"client": {
"001": "Testen auf DOM-basiertes Cross Site Scripting",
"002": "Testen auf JavaScript-Ausführung",
"003": "Testen auf HTML-Injection",
"004": "Testen der clientseitigen URL-Umleitung",
"005": "Testen auf CSS-Injektion",
"006": "Testen auf clientseitige Ressourcenmanipulation",
"007": "Testen Sie die ursprungsübergreifende Ressourcenfreigabe",
"008": "Testen auf Cross-Site-Flashing",
"009": "Test auf Clickjacking",
"010": "Testen von WebSockets",
"011": "Testen von Web-Messaging",
"012": "Lokalen Speicher testen"
}
}
}

View File

@ -69,5 +69,143 @@
"delete.success": "Project deleted successfully",
"delete.failed": "Project could not be deleted"
}
},
"pentest": {
"testId": "No.",
"title": "Title",
"findings": "Findings",
"comments": "Comments",
"status": "Status",
"statusText": {
"not_started": "Not Started",
"disabled": "Disabled",
"open": "Open",
"checked": "Checked",
"reported": "Reported",
"under_review": "Under Review",
"triaged": "Triaged"
},
"info": {
"001": "Conduct Search Engine Discovery and Reconnaissance for Information Leakage",
"002": "Fingerprint Web Server",
"003": "Review Webserver Metafiles for Information Leakage",
"004": "Enumerate Applications on Webserver",
"005": "Review Webpage Comments and Metadata for Information Leakage",
"006": "Identity application entry points",
"007": "Map execution paths through application",
"008": "Fingerprint Web Application Framework",
"009": "Fingerprint Web Application",
"010": "Map Application Architecture"
},
"config": {
"001": "Test Network/Infrastructure Configuration",
"002": "Test Application Platform Configuration",
"003": "Test File Extensions Handling for Sensitive Information",
"004": "Backup and Unreferenced Files for Sensitive Information",
"005": "Enumerate Infrastructure and Application Admin Interfaces",
"006": "Test HTTP Methods",
"007": "Test HTTP Strict Transport Security",
"008": "Test RIA cross domain policy"
},
"ident": {
"001": "Test Role Definitions",
"002": "Test User Registration Process",
"003": "Test Account Provisioning Process",
"004": "Testing for Account Enumeration and Guessable User Account",
"005": "Testing for Weak or unenforced username policy",
"006": "Test Permissions of Guest/Training Accounts",
"007": "Test Account Suspension/Resumption Process"
},
"authn": {
"001": "Testing for Credentials Transported over an Encrypted Channel",
"002": "Testing for default credentials",
"003": "Testing for Weak lock out mechanism",
"004": "Testing for bypassing authentication schema",
"005": "Test remember password functionality",
"006": "Testing for Browser cache weakness",
"007": "Testing for Weak password policy",
"008": "Testing for Weak security question/answer",
"009": "Testing for weak password change or reset functionalities",
"010": "Testing for Weaker authentication in alternative channel"
},
"authz": {
"001": "Testing Directory traversal/file include",
"002": "Testing for bypassing authorization schema",
"003": "Testing for Privilege Escalation",
"004": "Testing for Insecure Direct Object References"
},
"sess": {
"001": "Testing for Bypassing Session Management Schema",
"002": "Testing for Cookies attributes",
"003": "Testing for Session Fixation",
"004": "Testing for Exposed Session Variables",
"005": "Testing for Cross Site Request Forgery",
"006": "Testing for logout functionality",
"007": "Test Session Timeout",
"008": "Testing for Session puzzling"
},
"inpval": {
"001": "Testing for Reflected Cross Site Scripting",
"002": "Testing for Stored Cross Site Scripting",
"003": "Testing for HTTP Verb Tampering",
"004": "Testing for HTTP Parameter pollution",
"005": "N/A",
"006": "Testing for SQL Injection",
"006_1": "Oracle Testing",
"006_2": "SQL Server Testing",
"006_3": "Testing PostgreSQL",
"006_4": "MS Access Testing",
"006_5": "Testing for NoSQL Injection",
"007": "Testing for LDAP Injection",
"008": "Testing for ORM Injection",
"009": "Testing for XML Injection",
"010": "Testing for SSI Injection",
"011": "Testing for XPath Injection",
"012": "IMAP/SMTP Injection",
"013": "Testing for Code Injection",
"013_1": "Testing Local File Inclusion",
"013_2": "Testing Remote File Inclusion",
"014": "Testing for Command Injection",
"015": "Testing for Buffer overflow",
"015_1": "Testing for Heap overflow",
"015_2": "Testing for Stack overflow",
"015_3": "Testing for Format string",
"016": "Testing for incubated vulnerabilities",
"017": "Testing for HTTP Splitting/Smuggling"
},
"err": {
"001": "Analysis of Error Codes",
"002": "Analysis of Stack Traces"
},
"crypst": {
"001": "Testing for Weak SSL/TSL Ciphers, Insufficient Transport Layer Protection",
"002": "Testing for Padding Oracle",
"003": "Testing for Sensitive information sent via unencrypted channels"
},
"buslogic": {
"001": "Test Business Logic Data Validation",
"002": "Test Ability to Forge Requests",
"003": "Test Integrity Checks",
"004": "Test for Process Timing",
"005": "Test Number of Times a Function Can be Used Limits",
"006": "Testing for the Circumvention of Work Flows",
"007": "Test Defenses Against Application Mis-use",
"008": "Test Upload of Unexpected File Types",
"009": "Test Upload of Malicious Files"
},
"client": {
"001": "Testing for DOM based Cross Site Scripting",
"002": "Testing for JavaScript Execution",
"003": "Testing for HTML Injection",
"004": "Testing for Client Side URL Redirect",
"005": "Testing for CSS Injection",
"006": "Testing for Client Side Resource Manipulation",
"007": "Test Cross Origin Resource Sharing",
"008": "Testing for Cross Site Flashing",
"009": "Testing for Clickjacking",
"010": "Testing WebSockets",
"011": "Test Web Messaging",
"012": "Test Local Storage"
}
}
}

View File

@ -22,4 +22,4 @@ export const environment = {
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@ -59,7 +59,7 @@ import '@angular/localize/init';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js'; // Included with Angular CLI.
(window as any).global = window;
(window as any).process = {
env: {DEBUG: undefined},

View File

@ -0,0 +1,58 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getAUTHN_Pentests(): Pentest[] {
return [
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-001',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-002',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-003',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-004',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-005',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-006',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-007',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-008',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-009',
status: Status.NOT_STARTED
},
{
category: Category.AUTHENTICATION_TESTING,
refNumber: 'OTG-AUTHN-010',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,28 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getAUTHZ_Pentests(): Pentest[] {
return [
{
category: Category.AUTHORIZATION_TESTING,
refNumber: 'OTG-AUTHZ-001',
status: Status.NOT_STARTED
},
{
category: Category.AUTHORIZATION_TESTING,
refNumber: 'OTG-AUTHZ-002',
status: Status.NOT_STARTED
},
{
category: Category.AUTHORIZATION_TESTING,
refNumber: 'OTG-AUTHZ-003',
status: Status.NOT_STARTED
},
{
category: Category.AUTHORIZATION_TESTING,
refNumber: 'OTG-AUTHZ-004',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,53 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getBUSLOGIC_Pentests(): Pentest[] {
return [
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-001',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-002',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-003',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-004',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-005',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-006',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-007',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-008',
status: Status.NOT_STARTED
},
{
category: Category.BUSINESS_LOGIC_TESTING,
refNumber: 'OTG-BUSLOGIC-009',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,68 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getCLIENT_Pentests(): Pentest[] {
return [
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-001',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-002',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-003',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-004',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-005',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-006',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-007',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-008',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-009',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-010',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-011',
status: Status.NOT_STARTED
},
{
category: Category.CLIENT_SIDE_TESTING,
refNumber: 'OTG-CLIENT-012',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,48 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getCONFIG_Pentests(): Pentest[] {
return [
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-001',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-002',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-003',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-004',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-005',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-006',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-007',
status: Status.NOT_STARTED
},
{
category: Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
refNumber: 'OTG-CONFIG-008',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,23 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getCRYPST_Pentests(): Pentest[] {
return [
{
category: Category.CRYPTOGRAPHY,
refNumber: 'OTG-CRYPST-001',
status: Status.NOT_STARTED
},
{
category: Category.CRYPTOGRAPHY,
refNumber: 'OTG-CRYPST-002',
status: Status.NOT_STARTED
},
{
category: Category.CRYPTOGRAPHY,
refNumber: 'OTG-CRYPST-003',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,18 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getERR_Pentests(): Pentest[] {
return [
{
category: Category.ERROR_HANDLING,
refNumber: 'OTG-ERR-001',
status: Status.NOT_STARTED
},
{
category: Category.ERROR_HANDLING,
refNumber: 'OTG-ERR-002',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,43 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getIDENT_Pentests(): Pentest[] {
return [
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-001',
status: Status.NOT_STARTED
},
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-002',
status: Status.NOT_STARTED
},
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-003',
status: Status.NOT_STARTED
},
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-004',
status: Status.NOT_STARTED
},
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-005',
status: Status.NOT_STARTED
},
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-006',
status: Status.NOT_STARTED
},
{
category: Category.IDENTITY_MANAGEMENT_TESTING,
refNumber: 'OTG-IDENT-007',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,58 @@
import {Pentest} from '@shared/models/pentest.model';
import {Status} from '@shared/models/status.model';
import {Category} from '@shared/models/category.model';
export function getINFO_Pentests(): Pentest[] {
return [
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-001',
status: Status.NOT_STARTED
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-002',
status: Status.NOT_STARTED
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-003',
status: Status.NOT_STARTED
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-004',
status: Status.NOT_STARTED,
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-005',
status: Status.NOT_STARTED,
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-006',
status: Status.NOT_STARTED,
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-007',
status: Status.NOT_STARTED,
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-008',
status: Status.NOT_STARTED,
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-009',
status: Status.NOT_STARTED,
},
{
category: Category.INFORMATION_GATHERING,
refNumber: 'OTG-INFO-010',
status: Status.NOT_STARTED,
}
];
}

View File

@ -0,0 +1,149 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getINPVAL_Pentests(): Pentest[] {
return [
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-001',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-002',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-003',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-004',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-005',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-006',
status: Status.NOT_STARTED,
childEntries: [
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-006_1',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-006_2',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-006_3',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-006_4',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-006_5',
status: Status.NOT_STARTED
},
]
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-007',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-008',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-009',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-010',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-011',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-012',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-013',
status: Status.NOT_STARTED,
childEntries: [
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-013_1',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-013_2',
status: Status.NOT_STARTED
}
]
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-014',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-015',
status: Status.NOT_STARTED,
childEntries: [
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-015_1',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-015_2',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-015_3',
status: Status.NOT_STARTED
}
]
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-016',
status: Status.NOT_STARTED
},
{
category: Category.INPUT_VALIDATION_TESTING,
refNumber: 'OTG-INPVAL-017',
status: Status.NOT_STARTED
},
];
}

View File

@ -0,0 +1,48 @@
import {Pentest} from '@shared/models/pentest.model';
import {Category} from '@shared/models/category.model';
import {Status} from '@shared/models/status.model';
export function getSESS_Pentests(): Pentest[] {
return [
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-001',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-002',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-003',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-004',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-005',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-006',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-007',
status: Status.NOT_STARTED
},
{
category: Category.SESSION_MANAGEMENT_TESTING,
refNumber: 'OTG-SESS-008',
status: Status.NOT_STARTED
}
];
}

View File

@ -0,0 +1,70 @@
import {Category} from '@shared/models/category.model';
import {Pentest} from '@shared/models/pentest.model';
import {getINFO_Pentests} from '@shared/functions/categories/INFO/pentests.function';
import {getCONFIG_Pentests} from '@shared/functions/categories/CONFIG/pentests.function';
import {getIDENT_Pentests} from '@shared/functions/categories/IDENT/pentests.function';
import {getAUTHN_Pentests} from '@shared/functions/categories/AUTHN/pentests.function';
import {getAUTHZ_Pentests} from '@shared/functions/categories/AUTHZ/pentests.function';
import {getSESS_Pentests} from '@shared/functions/categories/SESS/pentests.function';
import {getINPVAL_Pentests} from '@shared/functions/categories/INPVAL/pentests.function';
import {getERR_Pentests} from '@shared/functions/categories/ERR/pentests.function';
import {getCRYPST_Pentests} from '@shared/functions/categories/CRYPST/pentests.function';
import {getBUSLOGIC_Pentests} from '@shared/functions/categories/BUSLOGIC/pentests.function';
import {getCLIENT_Pentests} from '@shared/functions/categories/CLIENT/pentests.function';
export function getTempPentestsForCategory(requestedCategory: Category): Pentest[] {
let pentests: Pentest[];
switch (requestedCategory) {
case Category.INFORMATION_GATHERING: {
pentests = getINFO_Pentests();
break;
}
case Category.CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING: {
pentests = getCONFIG_Pentests();
break;
}
case Category.IDENTITY_MANAGEMENT_TESTING: {
pentests = getIDENT_Pentests();
break;
}
case Category.AUTHENTICATION_TESTING: {
pentests = getAUTHN_Pentests();
break;
}
case Category.AUTHORIZATION_TESTING: {
pentests = getAUTHZ_Pentests();
break;
}
case Category.SESSION_MANAGEMENT_TESTING: {
pentests = getSESS_Pentests();
break;
}
case Category.INPUT_VALIDATION_TESTING: {
pentests = getINPVAL_Pentests();
break;
}
case Category.ERROR_HANDLING: {
pentests = getERR_Pentests();
break;
}
case Category.CRYPTOGRAPHY: {
pentests = getCRYPST_Pentests();
break;
}
case Category.BUSINESS_LOGIC_TESTING: {
pentests = getBUSLOGIC_Pentests();
break;
}
case Category.CLIENT_SIDE_TESTING: {
pentests = getCLIENT_Pentests();
break;
}
default: {
pentests = [];
console.error('Invalid categories: ', requestedCategory);
break;
}
}
return pentests;
}

View File

@ -0,0 +1,63 @@
export function getTitleKeyForRefNumber(refNumber: string): string {
let translationKey = 'pentest.';
let subRefNumberKey;
const refNumberKey = refNumber.slice(refNumber.length - 3);
switch (true) {
case refNumber.includes('INFO'): {
translationKey += 'info.' + refNumberKey;
break;
}
case refNumber.includes('CONFIG'): {
translationKey += 'config.' + refNumberKey;
break;
}
case refNumber.includes('IDENT'): {
translationKey += 'ident.' + refNumberKey;
break;
}
case refNumber.includes('AUTHN'): {
translationKey += 'authn.' + refNumberKey;
break;
}
case refNumber.includes('AUTHZ'): {
translationKey += 'authz.' + refNumberKey;
break;
}
case refNumber.includes('SESS'): {
translationKey += 'sess.' + refNumberKey;
break;
}
case refNumber.includes('INPVAL'): {
if (refNumber.includes('_')) {
subRefNumberKey = refNumber.slice(refNumber.length - 5);
translationKey += 'inpval.' + subRefNumberKey;
} else {
translationKey += 'inpval.' + refNumberKey;
}
break;
}
case refNumber.includes('ERR'): {
translationKey += 'err.' + refNumberKey;
break;
}
case refNumber.includes('CRYPST'): {
translationKey += 'crypst.' + refNumberKey;
break;
}
case refNumber.includes('BUSLOGIC'): {
translationKey += 'buslogic.' + refNumberKey;
break;
}
case refNumber.includes('CLIENT'): {
translationKey += 'client.' + refNumberKey;
break;
}
default: {
translationKey = 'pentest.categories.translation';
console.error('Invalid category number: ', refNumber.slice(4 - refNumber.length));
break;
}
}
return translationKey;
}

View File

@ -0,0 +1,20 @@
export enum Category {
INFORMATION_GATHERING,
CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
IDENTITY_MANAGEMENT_TESTING,
AUTHENTICATION_TESTING,
AUTHORIZATION_TESTING,
SESSION_MANAGEMENT_TESTING,
INPUT_VALIDATION_TESTING,
ERROR_HANDLING,
CRYPTOGRAPHY,
BUSINESS_LOGIC_TESTING,
CLIENT_SIDE_TESTING
}
export class CategoryDetails {
id: string;
name: Category;
disabledPentests: Array<string>;
disabled: false;
}

View File

@ -0,0 +1,51 @@
import {v4 as UUID} from 'uuid';
import {Status} from '@shared/models/status.model';
import {Category} from '@shared/models/category.model';
export class Pentest {
id?: string;
category: Category;
refNumber: string;
childEntries?: Pentest[];
status: Status;
findingsIds?: Array<string>;
commentsIds?: Array<string>;
constructor(category: Category,
refNumber: string,
status: Status,
id?: string,
findingsIds?: Array<string>,
commentsIds?: Array<string>) {
this.id = id ? id : UUID();
this.category = category;
this.refNumber = refNumber;
this.status = status;
this.findingsIds = findingsIds ? findingsIds : [];
this.commentsIds = commentsIds ? commentsIds : [];
}
}
export interface PentestEntry {
refNumber: string;
status: string;
findings?: number;
kind?: string;
childEntries?: PentestEntry[];
expanded?: boolean;
}
export function transformPentestsToEntries(pentests: Pentest[]): PentestEntry[] {
const pentestEntries: PentestEntry[] = [];
pentests.forEach((value: Pentest) => {
pentestEntries.push({
refNumber: value.refNumber,
status: value.status,
findings: value.findingsIds ? value.findingsIds.length : 0,
kind: value.childEntries ? 'dir' : 'cell',
childEntries: value.childEntries ? value.childEntries : null,
expanded: !!value.childEntries
} as PentestEntry);
});
return pentestEntries;
}

View File

@ -0,0 +1,4 @@
export enum Route {
HOME = 'home',
PROJECT_OVERVIEW = 'projects'
}

View File

@ -0,0 +1,9 @@
export enum Status {
NOT_STARTED = 'NOT_STARTED',
DISABLED = 'DISABLED',
OPEN = 'OPEN',
CHECKED = 'CHECKED',
REPORTED = 'REPORTED',
UNDER_REVIEW = 'UNDER_REVIEW',
TRIAGED = 'TRIAGED'
}

View File

@ -3,13 +3,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ConfirmDialogComponent } from './confirm-dialog.component';
import {DialogService} from '@shared/services/dialog-service/dialog.service';
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
import {NbButtonModule, NbCardModule, NbDialogRef, NbLayoutModule} from '@nebular/theme';
import {NbButtonModule, NbCardModule, NbDialogRef, NbLayoutModule, NbStatusService} from '@nebular/theme';
import {CommonModule} from '@angular/common';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpLoaderFactory} from '../../../app/common-app.module';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {FlexLayoutModule} from '@angular/flex-layout';
import {MockProvider} from 'ng-mocks';
describe('ConfirmDialogComponent', () => {
let component: ConfirmDialogComponent;
@ -37,6 +38,7 @@ describe('ConfirmDialogComponent', () => {
HttpClientTestingModule
],
providers: [
MockProvider(NbStatusService),
{provide: DialogService, useClass: DialogServiceMock},
{provide: NbDialogRef, useValue: {}}
]

View File

@ -0,0 +1,26 @@
import { TestBed } from '@angular/core/testing';
import { PentestService } from './pentest.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('PentestService', () => {
let service: PentestService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
BrowserAnimationsModule,
NgxsModule.forRoot([ProjectState])
]
});
service = TestBed.inject(PentestService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,52 @@
import {Injectable} from '@angular/core';
import {environment} from '../../environments/environment';
import {HttpClient} from '@angular/common/http';
import {Observable, of} from 'rxjs';
import {Category} from '@shared/models/category.model';
import {Pentest} from '@shared/models/pentest.model';
import {Store} from '@ngxs/store';
import {ProjectState} from '@shared/stores/project-state/project-state';
import {catchError, map, switchMap} from 'rxjs/operators';
import {getTempPentestsForCategory} from '@shared/functions/categories/get-temp-pentests-for-category.function';
@Injectable({
providedIn: 'root'
})
export class PentestService {
private apiBaseURL = `${environment.apiEndpoint}/pentests`;
constructor(
private http: HttpClient,
private readonly store: Store) {
}
/**
* Load Pentests
* @param category the categories of which the pentests should be requested
*/
public loadPentests(category: Category): Observable<Pentest[]> {
return this.store.selectOnce(ProjectState.project).pipe(
switchMap(project => this.getPentestByProjectIdAndCategory(project.id, category)),
catchError(_ => of(null)),
map(response => {
let pentests = response;
if (!pentests) {
pentests = getTempPentestsForCategory(category);
// tslint:disable-next-line:no-console
console.info('Initial pentest data loaded.');
}
return pentests;
})
);
}
/**
* Get Pentests
* @param projectId the id of the project
* @param category the categories of which the pentests should be requested
*/
private getPentestByProjectIdAndCategory(projectId: string, category: Category): Observable<Pentest[]> {
return this.http.get<Pentest[]>(`${this.apiBaseURL}?projectId=${projectId}?category=${category}`);
}
}

View File

@ -35,7 +35,6 @@ export class ProjectService {
* @param project the information of the project
*/
public updateProject(projectId: string, project: ProjectDialogBody): Observable<Project> {
console.log('update Project');
return this.http.patch<Project>(`${this.apiBaseURL}/${projectId}`, project);
}

View File

@ -0,0 +1,45 @@
import {Injectable, Inject, NgModule, ModuleWithProviders, InjectionToken} from '@angular/core';
import {NgxsPlugin, NGXS_PLUGINS, getActionTypeFromInstance} from '@ngxs/store';
import {tap} from 'rxjs/operators';
export const NGXS_LOGGER_PLUGIN_OPTIONS = new InjectionToken('NGXS_LOGGER_PLUGIN_OPTIONS');
@Injectable()
export class LoggerPlugin implements NgxsPlugin {
constructor(@Inject(NGXS_LOGGER_PLUGIN_OPTIONS) private options: any) {
}
handle(state, action, next): any {
if (this.options?.developmentMode) {
const actionName = getActionTypeFromInstance(action);
// console.debug('@', actionName, 'started', state);
return next(state, action)
.pipe(tap(result => {
// tslint:disable-next-line:no-console
console.debug('@', actionName, ' - AFTER', result);
}));
} else {
return next(state, action);
}
}
}
@NgModule()
export class NgxsLoggerPluginModule {
static forRoot(config?: any): ModuleWithProviders<any> {
return {
ngModule: NgxsLoggerPluginModule,
providers: [
{
provide: NGXS_PLUGINS,
useClass: LoggerPlugin,
multi: true
},
{
provide: NGXS_LOGGER_PLUGIN_OPTIONS,
useValue: config
}
]
};
}
}

View File

@ -0,0 +1,34 @@
import {Project} from '@shared/models/project.model';
import {Category} from '@shared/models/category.model';
export class InitProjectState {
static readonly type = '[ProjectState] InitProjectState';
constructor(
public project: Project,
public disabledCategories: Array<string>,
public disabledPentests: Array<string>) {
}
}
export class ChangeProject {
static readonly type = '[ProjectState] ChangeProject';
constructor(public project: Project) {
}
}
export class ChangeCategory {
static readonly type = '[ProjectState] ChangeCategory';
constructor(public category: Category) {
}
}
export class ChangePentest {
static readonly type = '[ProjectState] ChangePentest';
constructor(public pentestId: string) {
}
}

View File

@ -0,0 +1,56 @@
import {NgxsModule, Store} from '@ngxs/store';
import {TestBed} from '@angular/core/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpLoaderFactory} from '../../../app/common-app.module';
import {HttpClient} from '@angular/common/http';
import {PROJECT_STATE_NAME, ProjectState, ProjectStateModel} from '@shared/stores/project-state/project-state';
import {Category} from '@shared/models/category.model';
const INITIAL_PROJECT_STATE_SESSION: ProjectStateModel = {
selectedProject: null,
disabledCategories: [],
selectedCategory: Category.INFORMATION_GATHERING,
disabledPentests: [],
selectedPentestId: null
};
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
selectedProject: null,
disabledCategories: [],
selectedCategory: Category.INFORMATION_GATHERING,
disabledPentests: [],
selectedPentestId: null
};
describe('SessionState', () => {
let store: Store;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
NgxsModule.forRoot([ProjectState]),
]
});
store = TestBed.inject(Store);
store.reset({
...store.snapshot(),
[PROJECT_STATE_NAME]: INITIAL_PROJECT_STATE_SESSION
});
});
it('should contain store for PROJECT_STATE_NAME', (done) => {
store.selectSnapshot(state => {
expect(state[PROJECT_STATE_NAME]).toBeTruthy();
done();
});
});
});

View File

@ -0,0 +1,82 @@
import {Action, Selector, State, StateContext} from '@ngxs/store';
import {Injectable} from '@angular/core';
import {Project} from '@shared/models/project.model';
import {ChangeCategory, ChangePentest, ChangeProject, InitProjectState} from '@shared/stores/project-state/project-state.actions';
import {Category} from '@shared/models/category.model';
export const PROJECT_STATE_NAME = 'project';
export interface ProjectStateModel {
selectedProject: Project;
// Manages Categories
disabledCategories: Array<string>;
selectedCategory: Category;
// Manages Pentests of Category
disabledPentests: Array<string>;
selectedPentestId: string;
}
@State<ProjectStateModel>({
name: PROJECT_STATE_NAME,
defaults: {
selectedProject: null,
disabledCategories: [],
selectedCategory: Category.INFORMATION_GATHERING,
disabledPentests: [],
selectedPentestId: null
}
})
@Injectable()
export class ProjectState {
@Selector()
static project(state: ProjectStateModel): Project {
return state.selectedProject;
}
@Selector()
static selectedCategory(state: ProjectStateModel): Category {
return state.selectedCategory;
}
@Selector()
static selectedPentestId(state: ProjectStateModel): string {
return state.selectedPentestId;
}
@Action(InitProjectState)
initProjectState(ctx: StateContext<ProjectStateModel>, action: InitProjectState): void {
ctx.setState({
selectedProject: action.project,
disabledCategories: action.disabledCategories,
selectedCategory: Category.INFORMATION_GATHERING,
disabledPentests: action.disabledPentests,
selectedPentestId: null
});
}
@Action(ChangeProject)
changeProject(ctx: StateContext<ProjectStateModel>, {project}: ChangeProject): void {
const state = ctx.getState();
// ToDo: Add logic to change selectedCategory if disabled
ctx.patchState({
selectedProject: project
});
}
@Action(ChangeCategory)
changeCategory(ctx: StateContext<ProjectStateModel>, {category}: ChangeCategory): void {
const state = ctx.getState();
// ToDo: Add logic to change selectedCategory if disabled
ctx.patchState({
selectedCategory: category
});
}
@Action(ChangePentest)
changePentest(ctx: StateContext<ProjectStateModel>, {pentestId}: ChangePentest): void {
const state = ctx.getState();
ctx.patchState({
selectedPentestId: pentestId
});
}
}

View File

@ -0,0 +1,9 @@
<div class="finding-widget">
<ng-container *ngIf="numberOfFindigs > 0; else noFindings">
<fa-icon [icon]="fa.faExclamationCircle" size="lg" class="finding-icon"></fa-icon>
<span class="findings-count">{{numberOfFindigs}}</span>
</ng-container>
<ng-template #noFindings>
{{'-'}}
</ng-template>
</div>

View File

@ -0,0 +1,14 @@
@import '../../../assets/@theme/styles/themes';
.finding-widget {
align-items: center;
.finding-icon {
color: nb-theme(color-danger-default);
}
.findings-count {
margin-left: 0.45rem;
color: nb-theme(color-danger-default);
}
}

View File

@ -0,0 +1,31 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {FindigWidgetComponent} from './findig-widget.component';
import {FontAwesomeTestingModule} from '@fortawesome/angular-fontawesome/testing';
describe('FindigWidgetComponent', () => {
let component: FindigWidgetComponent;
let fixture: ComponentFixture<FindigWidgetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
FindigWidgetComponent
],
imports: [
FontAwesomeTestingModule
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FindigWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,20 @@
import {Component, Input, OnInit} from '@angular/core';
import * as FA from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'app-findig-widget',
templateUrl: './findig-widget.component.html',
styleUrls: ['./findig-widget.component.scss']
})
export class FindigWidgetComponent implements OnInit {
@Input() numberOfFindigs: number;
// HTML only
readonly fa = FA;
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,19 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FindigWidgetComponent} from '@shared/widgets/findig-widget/findig-widget.component';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
@NgModule({
declarations: [
FindigWidgetComponent
],
exports: [
FindigWidgetComponent
],
imports: [
CommonModule,
FontAwesomeModule
]
})
export class FindigWidgetModule {
}

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class FindigModule { }

View File

@ -0,0 +1,16 @@
<ng-container [ngSwitch]="currentStatus">
<nb-tag-list>
<nb-tag *ngSwitchCase="status.OPEN" status="info" appearance="filled"
text="{{getTranslationKey() | translate}}"></nb-tag>
<nb-tag *ngSwitchCase="status.CHECKED" status="primary" appearance="filled"
text="{{getTranslationKey() | translate}}"></nb-tag>
<nb-tag *ngSwitchCase="status.REPORTED" status="danger" appearance="filled"
text="{{getTranslationKey() | translate}}"></nb-tag>
<nb-tag *ngSwitchCase="status.UNDER_REVIEW" status="warning" appearance="filled"
text=" {{getTranslationKey() | translate}}"></nb-tag>
<nb-tag *ngSwitchCase="status.TRIAGED" status="success" appearance="filled"
text="{{getTranslationKey() | translate}}"></nb-tag>
<nb-tag *ngSwitchDefault status="basic" appearance="filled"
text="{{getTranslationKey() | translate}}"></nb-tag>
</nb-tag-list>
</ng-container>

View File

@ -0,0 +1,47 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {StatusTagComponent} from './status-tag.component';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpLoaderFactory} from '../../../app/common-app.module';
import {HttpClient} from '@angular/common/http';
import {NbCardModule, NbFocusMonitor, NbTagModule} from '@nebular/theme';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {CommonModule} from '@angular/common';
import {MockModule} from 'ng-mocks';
import {HttpClientTestingModule} from '@angular/common/http/testing';
describe('StatusTagComponent', () => {
let component: StatusTagComponent;
let fixture: ComponentFixture<StatusTagComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
StatusTagComponent
],
imports: [
HttpClientTestingModule,
NbCardModule,
MockModule(NbTagModule),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(StatusTagComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,40 @@
import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core';
import {Status} from '@shared/models/status.model';
@Component({
selector: 'app-status-tag',
templateUrl: './status-tag.component.html',
styleUrls: ['./status-tag.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StatusTagComponent implements OnInit {
@Input() currentStatus: Status = Status.NOT_STARTED;
// HTML only
status = Status;
readonly statusTexts: Array<StatusText> = [
{value: Status.NOT_STARTED, translationText: 'pentest.statusText.not_started'},
{value: Status.DISABLED, translationText: 'pentest.statusText.disabled'},
{value: Status.OPEN, translationText: 'pentest.statusText.open'},
{value: Status.CHECKED, translationText: 'pentest.statusText.checked'},
{value: Status.REPORTED, translationText: 'pentest.statusText.reported'},
{value: Status.UNDER_REVIEW, translationText: 'pentest.statusText.under_review'},
{value: Status.TRIAGED, translationText: 'pentest.statusText.triaged'}
];
constructor() { }
ngOnInit(): void {
}
getTranslationKey(): string {
const index = this.statusTexts.findIndex(statusText => statusText.value === this.currentStatus);
return this.statusTexts[index].translationText;
}
}
interface StatusText {
value: string;
translationText: string;
}

View File

@ -0,0 +1,22 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {StatusTagComponent} from '@shared/widgets/status-tag/status-tag.component';
import {NbCardModule, NbTagModule} from '@nebular/theme';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [
StatusTagComponent
],
exports: [
StatusTagComponent
],
imports: [
CommonModule,
NbCardModule,
NbTagModule,
TranslateModule
]
})
export class StatusTagModule {
}

View File

@ -6,7 +6,7 @@
"types": []
},
"angularCompilerOptions": {
"enableIvy": false
"enableIvy": true
},
"files": [
"src/main.ts",