diff --git a/frontend/src/app/app.service.ts b/frontend/src/app/app.service.ts
index e41334aa..27fb3a4a 100644
--- a/frontend/src/app/app.service.ts
+++ b/frontend/src/app/app.service.ts
@@ -169,6 +169,14 @@ export class AppService {
});
}
+ /**
+ * Delete assessment by ID
+ * @returns success/error message
+ */
+ deleteAssessment(assessmentId: number) {
+ return this.http.delete(`${this.api}/assessment/${assessmentId}`);
+ }
+
/**
* Function is responsible for returning all vulnerabilites related to an assessment
* @param assessmentId is the ID associated with the assessment
diff --git a/frontend/src/app/assessments/assessments.component.html b/frontend/src/app/assessments/assessments.component.html
index 1b8cafa4..eb2d4804 100644
--- a/frontend/src/app/assessments/assessments.component.html
+++ b/frontend/src/app/assessments/assessments.component.html
@@ -24,10 +24,14 @@
style="margin-right: 10px;" data-toggle="tooltip" data-placement="bottom" title="Edit Assessment">
-
-
+
\ No newline at end of file
diff --git a/frontend/src/app/assessments/assessments.component.ts b/frontend/src/app/assessments/assessments.component.ts
index 1afefdcb..fac2983f 100644
--- a/frontend/src/app/assessments/assessments.component.ts
+++ b/frontend/src/app/assessments/assessments.component.ts
@@ -1,13 +1,16 @@
import { Component, OnInit } from '@angular/core';
import { AppService } from '../app.service';
+import { AlertService } from '../alert/alert.service';
import { ActivatedRoute, Router } from '@angular/router';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faHaykal } from '@fortawesome/free-solid-svg-icons';
+import { faTrash } from '@fortawesome/free-solid-svg-icons';
+import { Assessment } from '../assessment-form/Assessment';
@Component({
selector: 'app-assessments',
templateUrl: './assessments.component.html',
- styleUrls: ['./assessments.component.sass']
+ styleUrls: ['./assessments.component.sass'],
})
export class AssessmentsComponent implements OnInit {
assessmentAry: any = [];
@@ -15,11 +18,18 @@ export class AssessmentsComponent implements OnInit {
orgId: number;
faPencilAlt = faPencilAlt;
faHaykal = faHaykal;
-
- constructor(public activatedRoute: ActivatedRoute, public router: Router) {}
+ faTrash = faTrash;
+ constructor(
+ public activatedRoute: ActivatedRoute,
+ public router: Router,
+ public appService: AppService,
+ public alertService: AlertService
+ ) {}
ngOnInit() {
- this.activatedRoute.data.subscribe(({ assessments }) => (this.assessmentAry = assessments));
+ this.activatedRoute.data.subscribe(
+ ({ assessments }) => (this.assessmentAry = assessments)
+ );
this.activatedRoute.params.subscribe((params) => {
this.assetId = params.assetId;
this.orgId = params.orgId;
@@ -31,7 +41,9 @@ export class AssessmentsComponent implements OnInit {
* @param id of vulnerability to load
*/
navigateToVulnerability(id: number) {
- this.router.navigate([`organization/${this.orgId}/asset/${this.assetId}/assessment/${id}/vulnerability`]);
+ this.router.navigate([
+ `organization/${this.orgId}/asset/${this.assetId}/assessment/${id}/vulnerability`,
+ ]);
}
/**
@@ -46,7 +58,9 @@ export class AssessmentsComponent implements OnInit {
* Function responsible for directing the user to the main Assessment view
*/
navigateToAssessment() {
- this.router.navigate([`organization/${this.orgId}/asset/${this.assetId}/assessment`]);
+ this.router.navigate([
+ `organization/${this.orgId}/asset/${this.assetId}/assessment`,
+ ]);
}
/**
@@ -55,6 +69,27 @@ export class AssessmentsComponent implements OnInit {
* @param assessmentId is the ID associated to the assessment to load
*/
navigateToAssessmentById(assessmentId: number) {
- this.router.navigate([`organization/${this.orgId}/asset/${this.assetId}/assessment/${assessmentId}`]);
+ this.router.navigate([
+ `organization/${this.orgId}/asset/${this.assetId}/assessment/${assessmentId}`,
+ ]);
+ }
+
+ /**
+ * Delete assessment by ID
+ * ID
+ * @param assessmentId is the ID associated to the assessment to load
+ */
+ deleteAssessment(assessment: Assessment) {
+ const r = confirm(`Delete the assessment "${assessment.name}"`);
+ if (r === true) {
+ this.appService
+ .deleteAssessment(assessment.id)
+ .subscribe((success: string) => {
+ this.alertService.success(success);
+ this.appService
+ .getAssessments(this.orgId)
+ .then((res) => (this.assessmentAry = res));
+ });
+ }
}
}
diff --git a/frontend/src/app/vulnerability/vulnerability.component.html b/frontend/src/app/vulnerability/vulnerability.component.html
index f17f33e0..a272805a 100644
--- a/frontend/src/app/vulnerability/vulnerability.component.html
+++ b/frontend/src/app/vulnerability/vulnerability.component.html
@@ -20,15 +20,11 @@
{{ vuln?.jiraId }}
{{ vuln?.status }} |
-
+
-
+
|
@@ -41,12 +37,8 @@
Preview Report
-
+
Back to Assessments
-
+
\ No newline at end of file
diff --git a/frontend/src/app/vulnerability/vulnerability.component.ts b/frontend/src/app/vulnerability/vulnerability.component.ts
index 5eef052b..57b442b6 100644
--- a/frontend/src/app/vulnerability/vulnerability.component.ts
+++ b/frontend/src/app/vulnerability/vulnerability.component.ts
@@ -4,11 +4,12 @@ import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faTrash } from '@fortawesome/free-solid-svg-icons';
import { Vulnerability } from '../vuln-form/Vulnerability';
import { AppService } from '../app.service';
+import { AlertService } from '../alert/alert.service';
@Component({
selector: 'app-vulnerability',
templateUrl: './vulnerability.component.html',
- styleUrls: ['./vulnerability.component.sass']
+ styleUrls: ['./vulnerability.component.sass'],
})
export class VulnerabilityComponent implements OnInit {
vulnAry: any = [];
@@ -18,10 +19,17 @@ export class VulnerabilityComponent implements OnInit {
faPencilAlt = faPencilAlt;
faTrash = faTrash;
- constructor(public activatedRoute: ActivatedRoute, public router: Router, public appService: AppService) {}
+ constructor(
+ public activatedRoute: ActivatedRoute,
+ public router: Router,
+ public appService: AppService,
+ public alertService: AlertService
+ ) {}
ngOnInit() {
- this.activatedRoute.data.subscribe(({ vulnerabilities }) => (this.vulnAry = vulnerabilities));
+ this.activatedRoute.data.subscribe(
+ ({ vulnerabilities }) => (this.vulnAry = vulnerabilities)
+ );
this.activatedRoute.params.subscribe((params) => {
this.assetId = params.assetId;
this.assessmentId = params.assessmentId;
@@ -35,7 +43,7 @@ export class VulnerabilityComponent implements OnInit {
*/
navigateToVulnerabilityForm() {
this.router.navigate([
- `organization/${this.orgId}/asset/${this.assetId}/assessment/${this.assessmentId}/vuln-form`
+ `organization/${this.orgId}/asset/${this.assetId}/assessment/${this.assessmentId}/vuln-form`,
]);
}
@@ -45,7 +53,7 @@ export class VulnerabilityComponent implements OnInit {
*/
navigateToVulnerabilityFormById(vulnId: number) {
this.router.navigate([
- `organization/${this.orgId}/asset/${this.assetId}/assessment/${this.assessmentId}/vuln-form/${vulnId}`
+ `organization/${this.orgId}/asset/${this.assetId}/assessment/${this.assessmentId}/vuln-form/${vulnId}`,
]);
}
@@ -60,7 +68,9 @@ export class VulnerabilityComponent implements OnInit {
* Function responsible for navigating to report area, takes no params directly
*/
navigateToReport() {
- this.router.navigate([`organization/${this.orgId}/asset/${this.assetId}/assessment/${this.assessmentId}/report`]);
+ this.router.navigate([
+ `organization/${this.orgId}/asset/${this.assetId}/assessment/${this.assessmentId}/report`,
+ ]);
}
/**
@@ -71,9 +81,11 @@ export class VulnerabilityComponent implements OnInit {
deleteVuln(vuln: Vulnerability) {
const r = confirm(`Delete the vulnerability "${vuln.name}"`);
if (r === true) {
- this.appService.deleteVuln(vuln.id).subscribe((success) => {
- // TODO: Success message
- this.appService.getVulnerabilities(this.assessmentId).then((res) => (this.vulnAry = res));
+ this.appService.deleteVuln(vuln.id).subscribe((success: string) => {
+ this.alertService.success(success);
+ this.appService
+ .getVulnerabilities(this.assessmentId)
+ .then((res) => (this.vulnAry = res));
});
}
}
diff --git a/src/app.ts b/src/app.ts
index 5c2ccc0f..18259134 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -18,9 +18,9 @@ const orgController = require('./routes/organization.controller');
const userController = require('./routes/user.controller');
const fileUploadController = require('./routes/file-upload.controller');
import * as assetController from './routes/asset.controller';
-const assessmentController = require('./routes/assessment.controller');
+import * as assessmentController from './routes/assessment.controller';
const vulnController = require('./routes/vulnerability.controller');
-const jwtMiddleware = require('./middleware/jwt.middleware');
+import * as jwtMiddleware from './middleware/jwt.middleware';
const puppeteerUtility = require('./utilities/puppeteer.utility');
const helmet = require('helmet');
const cors = require('cors');
@@ -73,6 +73,7 @@ createConnection().then((_) => {
app.get('/api/assessment/:id', jwtMiddleware.checkToken, assessmentController.getAssessmentsByAssetId);
app.get('/api/assessment/:id/vulnerability', jwtMiddleware.checkToken, assessmentController.getAssessmentVulns);
app.post('/api/assessment', jwtMiddleware.checkToken, assessmentController.createAssessment);
+ app.delete('/api/assessment/:assessmentId', jwtMiddleware.checkToken, assessmentController.deleteAssessmentById);
app.get(
'/api/asset/:assetId/assessment/:assessmentId',
jwtMiddleware.checkToken,
diff --git a/src/middleware/jwt.middleware.ts b/src/middleware/jwt.middleware.ts
index 38f10207..86aa3b65 100644
--- a/src/middleware/jwt.middleware.ts
+++ b/src/middleware/jwt.middleware.ts
@@ -5,7 +5,7 @@ import jwt = require('jsonwebtoken');
* @param {Request} req
* @param {Response} res
*/
-const checkToken = (req, res, next) => {
+export const checkToken = (req, res, next) => {
const token = req.headers.authorization; // Express headers are auto converted to lowercase
if (token) {
jwt.verify(token, process.env.JWT_KEY, (err, decoded) => {
@@ -26,7 +26,7 @@ const checkToken = (req, res, next) => {
* @param {Request} req
* @param {Response} res
*/
-const checkRefreshToken = (req, res, next) => {
+export const checkRefreshToken = (req, res, next) => {
const token = req.body.refreshToken;
if (token) {
jwt.verify(token, process.env.JWT_REFRESH_KEY, (err, decoded) => {
@@ -41,8 +41,3 @@ const checkRefreshToken = (req, res, next) => {
return res.status(401).json('Refresh token not supplied');
}
};
-
-module.exports = {
- checkToken,
- checkRefreshToken
-};
diff --git a/src/routes/assessment.controller.spec.ts b/src/routes/assessment.controller.spec.ts
new file mode 100644
index 00000000..8a0514d9
--- /dev/null
+++ b/src/routes/assessment.controller.spec.ts
@@ -0,0 +1,81 @@
+import { getConnection } from 'typeorm';
+import { Assessment } from '../entity/Assessment';
+import { Asset } from '../entity/Asset';
+import { Vulnerability } from '../entity/Vulnerability';
+import { Organization } from '../entity/Organization';
+import { createConnection } from 'typeorm';
+import { File } from '../entity/File';
+import { User } from '../entity/User';
+import { ProblemLocation } from '../entity/ProblemLocation';
+import { Resource } from '../entity/Resource';
+import MockExpressResponse = require('mock-express-response');
+import MockExpressRequest = require('mock-express-request');
+import * as assessmentController from './assessment.controller';
+
+describe('Assessment Controller', () => {
+ beforeEach(async () => {
+ await createConnection({
+ type: 'sqlite',
+ database: ':memory:',
+ dropSchema: true,
+ entities: [Asset, Organization, File, Vulnerability, Assessment, User, ProblemLocation, Resource],
+ synchronize: true,
+ logging: false,
+ name: 'default'
+ });
+ });
+ afterEach(() => {
+ const conn = getConnection('default');
+ return conn.close();
+ });
+ test('Assessment Delete', async () => {
+ const response = new MockExpressResponse();
+ const request = new MockExpressRequest({
+ params: {
+ assessmentId: 'abc'
+ }
+ });
+ await assessmentController.deleteAssessmentById(request, response);
+ expect(response.statusCode).toBe(400);
+ const response2 = new MockExpressResponse();
+ const request2 = new MockExpressRequest({
+ params: {
+ assessmentId: null
+ }
+ });
+ await assessmentController.deleteAssessmentById(request2, response2);
+ expect(response2.statusCode).toBe(400);
+ const response3 = new MockExpressResponse();
+ const request3 = new MockExpressRequest({
+ params: {
+ assessmentId: 3
+ }
+ });
+ await assessmentController.deleteAssessmentById(request3, response3);
+ expect(response3.statusCode).toBe(404);
+ const assessment: Assessment = {
+ id: null,
+ name: 'testAssessment',
+ executiveSummary: '',
+ jiraId: '',
+ testUrl: '',
+ prodUrl: '',
+ scope: '',
+ tag: '',
+ startDate: new Date(),
+ endDate: new Date(),
+ asset: new Asset(),
+ testers: null,
+ vulnerabilities: null
+ };
+ await getConnection().getRepository(Assessment).insert(assessment);
+ const response4 = new MockExpressResponse();
+ const request4 = new MockExpressRequest({
+ params: {
+ assessmentId: 1
+ }
+ });
+ await assessmentController.deleteAssessmentById(request4, response4);
+ expect(response4.statusCode).toBe(200);
+ });
+});
diff --git a/src/routes/assessment.controller.ts b/src/routes/assessment.controller.ts
index badfc308..0787d4bb 100644
--- a/src/routes/assessment.controller.ts
+++ b/src/routes/assessment.controller.ts
@@ -15,7 +15,7 @@ const userController = require('../routes/user.controller');
* @param {Response} res
* @returns Asset assessments
*/
-const getAssessmentsByAssetId = async (req: UserRequest, res: Response) => {
+export const getAssessmentsByAssetId = async (req: UserRequest, res: Response) => {
if (!req.params.id) {
return res.status(400).json('Invalid Assessment request');
}
@@ -38,7 +38,7 @@ const getAssessmentsByAssetId = async (req: UserRequest, res: Response) => {
* @param {Response} res
* @returns assessment vulnerabilities
*/
-const getAssessmentVulns = async (req: UserRequest, res: Response) => {
+export const getAssessmentVulns = async (req: UserRequest, res: Response) => {
if (!req.params.id) {
return res.status(400).json('Invalid Vulnerability request');
}
@@ -61,7 +61,7 @@ const getAssessmentVulns = async (req: UserRequest, res: Response) => {
* @param {Response} res c
* @returns success message
*/
-const createAssessment = async (req: UserRequest, res: Response) => {
+export const createAssessment = async (req: UserRequest, res: Response) => {
if (isNaN(req.body.asset)) {
return res.status(400).json('Asset ID is invalid');
}
@@ -99,7 +99,7 @@ const createAssessment = async (req: UserRequest, res: Response) => {
* @param {Response} res
* @returns assessment
*/
-const getAssessmentById = async (req: UserRequest, res: Response) => {
+export const getAssessmentById = async (req: UserRequest, res: Response) => {
if (!req.params.assessmentId) {
return res.status(400).send('Invalid assessment request');
}
@@ -126,7 +126,7 @@ const getAssessmentById = async (req: UserRequest, res: Response) => {
* @param {Response} res
* @returns success message
*/
-const updateAssessmentById = async (req: UserRequest, res: Response) => {
+export const updateAssessmentById = async (req: UserRequest, res: Response) => {
if (!req.params.assessmentId) {
return res.status(400).send('Invalid assessment request');
}
@@ -164,7 +164,7 @@ const updateAssessmentById = async (req: UserRequest, res: Response) => {
* @param {Response} res
* @returns report information
*/
-const queryReportDataByAssessment = async (req: UserRequest, res: Response) => {
+export const queryReportDataByAssessment = async (req: UserRequest, res: Response) => {
if (!req.params.assessmentId) {
return res.status(400).send('Invalid report request');
}
@@ -210,11 +210,24 @@ const queryReportDataByAssessment = async (req: UserRequest, res: Response) => {
res.status(200).json(report);
};
-module.exports = {
- getAssessmentsByAssetId,
- getAssessmentVulns,
- createAssessment,
- getAssessmentById,
- updateAssessmentById,
- queryReportDataByAssessment
+/**
+ * @description Delete assessment by ID
+ * @param {UserRequest} req vulnID is required
+ * @param {Response} res contains JSON object with the success/fail
+ * @returns success/error message
+ */
+export const deleteAssessmentById = async (req: UserRequest, res: Response) => {
+ if (!req.params.assessmentId) {
+ return res.status(400).send('Invalid assessment ID');
+ }
+ if (isNaN(+req.params.assessmentId)) {
+ return res.status(400).send('Invalid assessment ID');
+ }
+ const assessment = await getConnection().getRepository(Assessment).findOne(req.params.assessmentId);
+ if (!assessment) {
+ return res.status(404).send('Assessment does not exist.');
+ } else {
+ await getConnection().getRepository(Assessment).delete(assessment);
+ res.status(200).json(`Assessment #${assessment.id}: "${assessment.name}" successfully deleted`);
+ }
};
diff --git a/src/routes/vulnerability.controller.ts b/src/routes/vulnerability.controller.ts
index 720dcf04..3b1fe7af 100644
--- a/src/routes/vulnerability.controller.ts
+++ b/src/routes/vulnerability.controller.ts
@@ -1,12 +1,12 @@
import { UserRequest } from '../interfaces/user-request.interface';
import { Response } from 'express';
-import { getConnection } from 'typeorm'
+import { getConnection } from 'typeorm';
import { Assessment } from '../entity/Assessment';
import { validate } from 'class-validator';
import { Vulnerability } from '../entity/Vulnerability';
import { File } from '../entity/File';
-import { ProblemLocation } from '../entity/ProblemLocation'
-import { Resource } from '../entity/Resource'
+import { ProblemLocation } from '../entity/ProblemLocation';
+import { Resource } from '../entity/Resource';
const fileUploadController = require('../routes/file-upload.controller');
/**
@@ -16,21 +16,23 @@ const fileUploadController = require('../routes/file-upload.controller');
* @returns vulnerability data
*/
const getVulnById = async (req: UserRequest, res: Response) => {
- if (!req.params.vulnId) {
- return res.status(400).send('Invalid Vulnerability UserRequest');
- }
- if (isNaN(+req.params.vulnId)) {
- return res.status(400).send('Invalid Vulnerability ID');
- }
- // TODO: Utilize createQueryBuilder to only return screenshot IDs and not the full object
- const vuln = await getConnection().getRepository(Vulnerability).findOne(req.params.vulnId, {
- relations: ['screenshots', 'problemLocations', 'resources']
+ if (!req.params.vulnId) {
+ return res.status(400).send('Invalid Vulnerability UserRequest');
+ }
+ if (isNaN(+req.params.vulnId)) {
+ return res.status(400).send('Invalid Vulnerability ID');
+ }
+ // TODO: Utilize createQueryBuilder to only return screenshot IDs and not the full object
+ const vuln = await getConnection()
+ .getRepository(Vulnerability)
+ .findOne(req.params.vulnId, {
+ relations: ['screenshots', 'problemLocations', 'resources']
});
- if (!vuln) {
- return res.status(404).send('Vulnerability does not exist.');
- }
- res.status(200).json(vuln);
-}
+ if (!vuln) {
+ return res.status(404).send('Vulnerability does not exist.');
+ }
+ res.status(200).json(vuln);
+};
/**
* @description Delete vulnerability by ID
* @param {UserRequest} req vulnID is required
@@ -38,20 +40,20 @@ const getVulnById = async (req: UserRequest, res: Response) => {
* @returns success/error message
*/
const deleteVulnById = async (req: UserRequest, res: Response) => {
- if (!req.params.vulnId) {
- return res.status(400).send('Invalid vulnerability UserRequest');
- }
- if (isNaN(+req.params.vulnId)) {
- return res.status(400).send('Invalid vulnerability ID');
- }
- const vuln = await getConnection().getRepository(Vulnerability).findOne(req.params.vulnId);
- if (!vuln) {
- return res.status(404).send('Vulnerability does not exist.');
- } else {
- await getConnection().getRepository(Vulnerability).delete(vuln);
- res.status(200).json('Vulnerability successfully deleted');
- }
-}
+ if (!req.params.vulnId) {
+ return res.status(400).send('Invalid vulnerability UserRequest');
+ }
+ if (isNaN(+req.params.vulnId)) {
+ return res.status(400).send('Invalid vulnerability ID');
+ }
+ const vuln = await getConnection().getRepository(Vulnerability).findOne(req.params.vulnId);
+ if (!vuln) {
+ return res.status(404).send('Vulnerability does not exist.');
+ } else {
+ await getConnection().getRepository(Vulnerability).delete(vuln);
+ res.status(200).json(`Vulnerability #${vuln.id}: "${vuln.name}" successfully deleted`);
+ }
+};
/**
* @description Update vulnerability by ID
* @param {UserRequest} req
@@ -60,85 +62,88 @@ const deleteVulnById = async (req: UserRequest, res: Response) => {
* // TODO: Break apart this function into smaller functions. Also make common as create is almost the same.
*/
const patchVulnById = async (req: UserRequest, res: Response) => {
- req = await fileUploadController.uploadFileArray(req, res);
- if (isNaN(+req.body.assessment) || !req.body.assessment) {
- return res.status(400).json('Invalid Assessment ID');
+ req = await fileUploadController.uploadFileArray(req, res);
+ if (isNaN(+req.body.assessment) || !req.body.assessment) {
+ return res.status(400).json('Invalid Assessment ID');
+ }
+ const assessment = await getConnection().getRepository(Assessment).findOne(req.body.assessment);
+ if (!assessment) {
+ return res.status(404).json('Assessment does not exist');
+ }
+ if (isNaN(+req.params.vulnId)) {
+ return res.status(400).json('Vulnerability ID is invalid');
+ }
+ const vulnerability = await getConnection().getRepository(Vulnerability).findOne(req.params.vulnId);
+ if (!vulnerability) {
+ return res.status(404).json('Vulnerability does not exist');
+ }
+ const callback = (resStatus: number, message: any) => {
+ res.status(resStatus).send(message);
+ };
+ vulnerability.id = +req.params.vulnId;
+ vulnerability.impact = req.body.impact;
+ vulnerability.likelihood = req.body.likelihood;
+ vulnerability.risk = req.body.risk;
+ vulnerability.status = req.body.status;
+ vulnerability.description = req.body.description;
+ vulnerability.remediation = req.body.remediation;
+ vulnerability.jiraId = req.body.jiraId;
+ vulnerability.cvssScore = req.body.cvssScore;
+ vulnerability.cvssUrl = req.body.cvssUrl;
+ vulnerability.detailedInfo = req.body.detailedInfo;
+ vulnerability.assessment = assessment;
+ vulnerability.name = req.body.name;
+ vulnerability.systemic = req.body.systemic;
+ const errors = await validate(vulnerability);
+ if (errors.length > 0) {
+ return res.status(400).send('Vulnerability form validation failed');
+ } else {
+ await getConnection().getRepository(Vulnerability).save(vulnerability);
+ // Remove deleted files
+ if (req.body.screenshotsToDelete) {
+ const existingScreenshots = await getConnection()
+ .getRepository(File)
+ .find({ where: { vulnerability: vulnerability.id } });
+ const existingScreenshotIds = existingScreenshots.map((screenshot) => screenshot.id);
+ let screenshotsToDelete = JSON.parse(req.body.screenshotsToDelete);
+ // We only want to remove the files associated to the vulnerability
+ screenshotsToDelete = existingScreenshotIds.filter((value) => screenshotsToDelete.includes(value));
+ for (const screenshotId of screenshotsToDelete) {
+ getConnection().getRepository(File).delete(screenshotId);
+ }
}
- const assessment = await getConnection().getRepository(Assessment).findOne(req.body.assessment);
- if (!assessment) {
- return res.status(404).json('Assessment does not exist');
+ saveScreenshots(req.files, vulnerability, callback);
+ // Remove deleted problem locations
+ if (req.body.problemLocations.length) {
+ const clientProdLocs = JSON.parse(req.body.problemLocations);
+ const clientProdLocsIds = clientProdLocs.map((value) => value.id);
+ const existingProbLocs = await getConnection()
+ .getRepository(ProblemLocation)
+ .find({ where: { vulnerability: vulnerability.id } });
+ const existingProbLocIds = existingProbLocs.map((probLoc) => probLoc.id);
+ const prodLocsToDelete = existingProbLocIds.filter((value) => !clientProdLocsIds.includes(value));
+ for (const probLoc of prodLocsToDelete) {
+ getConnection().getRepository(ProblemLocation).delete(probLoc);
+ }
+ saveProblemLocations(clientProdLocs, vulnerability, callback);
}
- if (isNaN(+req.params.vulnId)) {
- return res.status(400).json('Vulnerability ID is invalid');
+ // Remove deleted resources
+ if (req.body.resources.length) {
+ const clientResources = JSON.parse(req.body.resources);
+ const clientResourceIds = clientResources.map((value) => value.id);
+ const existingResources = await getConnection()
+ .getRepository(Resource)
+ .find({ where: { vulnerability: vulnerability.id } });
+ const existingResourceIds = existingResources.map((resource) => resource.id);
+ const resourcesToDelete = existingResourceIds.filter((value) => !clientResourceIds.includes(value));
+ for (const resource of resourcesToDelete) {
+ getConnection().getRepository(Resource).delete(resource);
+ }
+ saveResources(clientResources, vulnerability, callback);
}
- const vulnerability = await getConnection().getRepository(Vulnerability).findOne(req.params.vulnId);
- if (!vulnerability) {
- return res.status(404).json('Vulnerability does not exist');
- }
- const callback = (resStatus: number, message: any) => {
- res.status(resStatus).send(message);
- };
- vulnerability.id = +req.params.vulnId;
- vulnerability.impact = req.body.impact;
- vulnerability.likelihood = req.body.likelihood;
- vulnerability.risk = req.body.risk;
- vulnerability.status = req.body.status;
- vulnerability.description = req.body.description;
- vulnerability.remediation = req.body.remediation;
- vulnerability.jiraId = req.body.jiraId;
- vulnerability.cvssScore = req.body.cvssScore;
- vulnerability.cvssUrl = req.body.cvssUrl;
- vulnerability.detailedInfo = req.body.detailedInfo;
- vulnerability.assessment = assessment;
- vulnerability.name = req.body.name;
- vulnerability.systemic = req.body.systemic;
- const errors = await validate(vulnerability);
- if (errors.length > 0) {
- return res.status(400).send('Vulnerability form validation failed');
- } else {
- await getConnection().getRepository(Vulnerability).save(vulnerability);
- // Remove deleted files
- if (req.body.screenshotsToDelete) {
- const existingScreenshots = await getConnection().getRepository(File)
- .find({ where: { vulnerability: vulnerability.id } });
- const existingScreenshotIds = existingScreenshots.map(screenshot => screenshot.id);
- let screenshotsToDelete = JSON.parse(req.body.screenshotsToDelete);
- // We only want to remove the files associated to the vulnerability
- screenshotsToDelete = existingScreenshotIds.filter(value => screenshotsToDelete.includes(value));
- for (const screenshotId of screenshotsToDelete) {
- getConnection().getRepository(File).delete(screenshotId);
- }
- }
- saveScreenshots(req.files, vulnerability, callback);
- // Remove deleted problem locations
- if (req.body.problemLocations.length) {
- const clientProdLocs = JSON.parse(req.body.problemLocations);
- const clientProdLocsIds = clientProdLocs.map(value => value.id);
- const existingProbLocs = await getConnection().getRepository(ProblemLocation)
- .find({ where: { vulnerability: vulnerability.id } });
- const existingProbLocIds = existingProbLocs.map(probLoc => probLoc.id);
- const prodLocsToDelete = existingProbLocIds.filter(value => !clientProdLocsIds.includes(value));
- for (const probLoc of prodLocsToDelete) {
- getConnection().getRepository(ProblemLocation).delete(probLoc);
- }
- saveProblemLocations(clientProdLocs, vulnerability, callback);
- }
- // Remove deleted resources
- if (req.body.resources.length) {
- const clientResources = JSON.parse(req.body.resources);
- const clientResourceIds = clientResources.map(value => value.id);
- const existingResources = await getConnection().getRepository(Resource)
- .find({ where: { vulnerability: vulnerability.id } });
- const existingResourceIds = existingResources.map(resource => resource.id);
- const resourcesToDelete = existingResourceIds.filter(value => !clientResourceIds.includes(value));
- for (const resource of resourcesToDelete) {
- getConnection().getRepository(Resource).delete(resource);
- }
- saveResources(clientResources, vulnerability, callback)
- }
- return res.status(200).json('Vulnerability patched successfully');
- }
-}
+ return res.status(200).json('Vulnerability patched successfully');
+ }
+};
/**
* @description Create vulnerability
* @param {UserRequest} req
@@ -146,44 +151,44 @@ const patchVulnById = async (req: UserRequest, res: Response) => {
* @returns success/error message
*/
const createVuln = async (req: UserRequest, res: Response) => {
- req = await fileUploadController.uploadFileArray(req, res);
- if (isNaN(+req.body.assessment) || !req.body.assessment) {
- return res.status(400).json('Invalid Assessment ID');
- }
- const assessment = await getConnection().getRepository(Assessment).findOne(req.body.assessment);
- if (!assessment) {
- return res.status(404).json('Assessment does not exist');
- }
- const callback = (resStatus: number, message: any) => {
- res.status(resStatus).send(message);
- };
- const vulnerability = new Vulnerability();
- vulnerability.impact = req.body.impact;
- vulnerability.likelihood = req.body.likelihood;
- vulnerability.risk = req.body.risk;
- vulnerability.status = req.body.status;
- vulnerability.description = req.body.description;
- vulnerability.remediation = req.body.remediation;
- vulnerability.jiraId = req.body.jiraId;
- vulnerability.cvssScore = req.body.cvssScore;
- vulnerability.cvssUrl = req.body.cvssUrl;
- vulnerability.detailedInfo = req.body.detailedInfo;
- vulnerability.assessment = assessment;
- vulnerability.name = req.body.name;
- vulnerability.systemic = req.body.systemic;
- const errors = await validate(vulnerability);
- if (errors.length > 0) {
- return res.status(400).send('Vulnerability form validation failed');
- } else {
- await getConnection().getRepository(Vulnerability).save(vulnerability);
- saveScreenshots(req.files, vulnerability, callback)
- const problemLocations = JSON.parse(req.body.problemLocations);
- saveProblemLocations(problemLocations, vulnerability, callback)
- const resources = JSON.parse(req.body.resources);
- saveResources(resources, vulnerability, callback)
- res.status(200).json('Vulnerability saved successfully');
- }
-}
+ req = await fileUploadController.uploadFileArray(req, res);
+ if (isNaN(+req.body.assessment) || !req.body.assessment) {
+ return res.status(400).json('Invalid Assessment ID');
+ }
+ const assessment = await getConnection().getRepository(Assessment).findOne(req.body.assessment);
+ if (!assessment) {
+ return res.status(404).json('Assessment does not exist');
+ }
+ const callback = (resStatus: number, message: any) => {
+ res.status(resStatus).send(message);
+ };
+ const vulnerability = new Vulnerability();
+ vulnerability.impact = req.body.impact;
+ vulnerability.likelihood = req.body.likelihood;
+ vulnerability.risk = req.body.risk;
+ vulnerability.status = req.body.status;
+ vulnerability.description = req.body.description;
+ vulnerability.remediation = req.body.remediation;
+ vulnerability.jiraId = req.body.jiraId;
+ vulnerability.cvssScore = req.body.cvssScore;
+ vulnerability.cvssUrl = req.body.cvssUrl;
+ vulnerability.detailedInfo = req.body.detailedInfo;
+ vulnerability.assessment = assessment;
+ vulnerability.name = req.body.name;
+ vulnerability.systemic = req.body.systemic;
+ const errors = await validate(vulnerability);
+ if (errors.length > 0) {
+ return res.status(400).send('Vulnerability form validation failed');
+ } else {
+ await getConnection().getRepository(Vulnerability).save(vulnerability);
+ saveScreenshots(req.files, vulnerability, callback);
+ const problemLocations = JSON.parse(req.body.problemLocations);
+ saveProblemLocations(problemLocations, vulnerability, callback);
+ const resources = JSON.parse(req.body.resources);
+ saveResources(resources, vulnerability, callback);
+ res.status(200).json('Vulnerability saved successfully');
+ }
+};
/**
* @description Save screenshots to vulnerability
* @param {UserRequest} req
@@ -192,18 +197,18 @@ const createVuln = async (req: UserRequest, res: Response) => {
* @returns saved file or error message
*/
const saveScreenshots = async (files: File[], vulnerability: Vulnerability, callback) => {
- for (const screenshot of files) {
- let file = new File();
- file = screenshot;
- file.vulnerability = vulnerability;
- const fileErrors = await validate(file);
- if (fileErrors.length > 0) {
- return callback(400, JSON.stringify('File validation failed'));
- } else {
- await getConnection().getRepository(File).save(file);
- }
+ for (const screenshot of files) {
+ let file = new File();
+ file = screenshot;
+ file.vulnerability = vulnerability;
+ const fileErrors = await validate(file);
+ if (fileErrors.length > 0) {
+ return callback(400, JSON.stringify('File validation failed'));
+ } else {
+ await getConnection().getRepository(File).save(file);
}
-}
+ }
+};
/**
* @description Save problem locations to vulnerability
* @param {UserRequest} req
@@ -212,22 +217,22 @@ const saveScreenshots = async (files: File[], vulnerability: Vulnerability, call
* @returns saved problem locations or error message
*/
const saveProblemLocations = async (problemLocations: ProblemLocation[], vulnerability: Vulnerability, callback) => {
- for (const probLoc of problemLocations) {
- if (probLoc && probLoc.location && probLoc.target) {
- let problemLocation = new ProblemLocation();
- problemLocation = probLoc;
- problemLocation.vulnerability = vulnerability;
- const plErrors = await validate(problemLocation);
- if (plErrors.length > 0) {
- return callback(400, JSON.stringify('Problem Location validation failed'));
- } else {
- await getConnection().getRepository(ProblemLocation).save(problemLocation);
- }
- } else {
- return callback(400, JSON.stringify('Invalid Problem Location'));
- }
+ for (const probLoc of problemLocations) {
+ if (probLoc && probLoc.location && probLoc.target) {
+ let problemLocation = new ProblemLocation();
+ problemLocation = probLoc;
+ problemLocation.vulnerability = vulnerability;
+ const plErrors = await validate(problemLocation);
+ if (plErrors.length > 0) {
+ return callback(400, JSON.stringify('Problem Location validation failed'));
+ } else {
+ await getConnection().getRepository(ProblemLocation).save(problemLocation);
+ }
+ } else {
+ return callback(400, JSON.stringify('Invalid Problem Location'));
}
-}
+ }
+};
/**
* @description Save resources to vulnerability
* @param {UserRequest} req
@@ -236,25 +241,25 @@ const saveProblemLocations = async (problemLocations: ProblemLocation[], vulnera
* @returns saved resources or error message
*/
const saveResources = async (resources: Resource[], vulnerability: Vulnerability, callback) => {
- for (const resource of resources) {
- if (resource.description && resource.url) {
- let newResource = new Resource();
- newResource = resource;
- newResource.vulnerability = vulnerability;
- const nrErrors = await validate(newResource);
- if (nrErrors.length > 0) {
- return callback(400, JSON.stringify('Resource Location validation failed'))
- } else {
- await getConnection().getRepository(Resource).save(newResource);
- }
- } else {
- return callback(400, JSON.stringify('Resource Location Invalid'))
- }
+ for (const resource of resources) {
+ if (resource.description && resource.url) {
+ let newResource = new Resource();
+ newResource = resource;
+ newResource.vulnerability = vulnerability;
+ const nrErrors = await validate(newResource);
+ if (nrErrors.length > 0) {
+ return callback(400, JSON.stringify('Resource Location validation failed'));
+ } else {
+ await getConnection().getRepository(Resource).save(newResource);
+ }
+ } else {
+ return callback(400, JSON.stringify('Resource Location Invalid'));
}
-}
+ }
+};
module.exports = {
- getVulnById,
- deleteVulnById,
- patchVulnById,
- createVuln
-}
\ No newline at end of file
+ getVulnById,
+ deleteVulnById,
+ patchVulnById,
+ createVuln
+};