Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-design Vulnerability details page #2352

Merged
Merged
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
7a70ae6
Rename current Vulnerability page to make room for new one
ameliav Oct 24, 2023
d3ecd73
Add new Vulnerability.tsx with start of redesign
ameliav Oct 24, 2023
971801c
Rename current Vulnerability page to make room for new one
ameliav Oct 24, 2023
beb69f0
Add new Vulnerability.tsx with start of redesign
ameliav Oct 24, 2023
8ba0ba7
Merge branch '2306-redesign-vuln-details-page-overview-section' of ht…
ameliav Oct 24, 2023
5be6a18
Update frame responsiveness in Vulnerability.tsx
ameliav Oct 24, 2023
5469de1
Update code format in Vulnerability.tsx
ameliav Oct 24, 2023
0c2e8dc
Update negative conditional return in Vulnerability.tsx
ameliav Oct 24, 2023
deff687
Update typescript format
ameliav Oct 24, 2023
cb3295a
Comment unused code in Vulnerability.tsx
ameliav Oct 24, 2023
a2b6a71
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Oct 25, 2023
a511fe5
Update span to Box in Vulnerability.tsx
ameliav Oct 25, 2023
43b0543
Update style to sx in Vulnerability.tsx
ameliav Oct 25, 2023
94b45ac
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
Nov 1, 2023
9158a1d
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
Nov 9, 2023
0235b73
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Dec 14, 2023
b7edbd8
Add CVC Highlight section links in Vulnerability.tsx
ameliav Dec 18, 2023
e2beebd
Update CWE and CVE links in Vulnerability.tsx
ameliav Dec 18, 2023
04ae18d
Add empty Severity, Affected, History & Notes sections in Vulnerabili…
ameliav Dec 18, 2023
63d2a54
Update iconButton with link in Vulnerability.tsx
ameliav Dec 18, 2023
9d5dcc3
Update Typography with div component in Vulnerability.tsx
ameliav Dec 18, 2023
9160a8d
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Dec 19, 2023
6f2d73f
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Jan 8, 2024
b75a7db
Organize Vulnerability.tsx file
ameliav Jan 8, 2024
9485201
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
nickviola Jan 12, 2024
fd026cc
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Jan 30, 2024
dd69f85
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Feb 5, 2024
d2f6b40
Remove unused files in the pages/Vulnerability folder
ameliav Feb 5, 2024
1054adb
Add basic cve endpoints and test suite.
Matthew-Grayson Feb 5, 2024
a7eb7e9
Merge branch '2306-redesign-vuln-details-page-overview-section' of gi…
Matthew-Grayson Feb 5, 2024
3329573
Hardcode organization and cve id used for testing.
Matthew-Grayson Feb 5, 2024
15c9982
Add authenticatedRoute for cves endpoint; rename api/cve.ts to api/cv…
Matthew-Grayson Feb 5, 2024
801ad22
Update expect statements to match cve endpoint response.
Matthew-Grayson Feb 5, 2024
b19ccd3
Add get by id endpoint and rename get by name endpoint.
Matthew-Grayson Feb 6, 2024
fe1948d
Add future todos.
Matthew-Grayson Feb 6, 2024
232fbbf
Add test for get cves/:cve_uid endpoint.
Matthew-Grayson Feb 6, 2024
276fc93
Create API and asociated test for product_info table; update todos in…
Matthew-Grayson Feb 6, 2024
cb3e047
Join product_info entries to results from /vulnerabilities/:id endpoint.
Matthew-Grayson Feb 6, 2024
084c36e
Join product_info entries to results from /cves/:cve_uid endpoint.
Matthew-Grayson Feb 6, 2024
bce7315
Join product_info entries to results from /cves/:cve_uid endpoint.
Matthew-Grayson Feb 6, 2024
8afb1df
Add left join for service table to /vulnerabilities/:id endpoint resp…
Matthew-Grayson Feb 6, 2024
c38f76a
Add cves name api call to Vulnerability.tsx
ameliav Feb 6, 2024
1931f3d
Add cve interface to types/cve.ts
ameliav Feb 6, 2024
2d6e6ec
Add cve interface to types/cve.ts
ameliav Feb 6, 2024
b6e1b65
Create cve interface in frontend/src/types.
Matthew-Grayson Feb 6, 2024
4a01633
Remove redundant left join of service table in vulnerabilities api.
Matthew-Grayson Feb 6, 2024
af63f1e
Refactor types/cpe to product-info to match database model.
Matthew-Grayson Feb 6, 2024
1957b48
Apply getResults method to /cves/name endpoint to ensure response con…
Matthew-Grayson Feb 6, 2024
64258a6
Revert /cves/name endpoint to use findOne and add join to product_inf…
Matthew-Grayson Feb 6, 2024
f237384
Add product info to Vulnerability.tsx
ameliav Feb 7, 2024
5b246ee
Add Misc known products to Vulnerability.tsx
ameliav Feb 7, 2024
aa9521b
Remove hard-coded UUIDs from tests for support with GitHub Actions.
Matthew-Grayson Feb 7, 2024
ec126d0
Refactor /cves/:cve_uid endpoint to use getOne; remove unused CveFilt…
Matthew-Grayson Feb 7, 2024
aae93f5
Remove unused CpeFilters and CpeSearch classes.
Matthew-Grayson Feb 7, 2024
5c6fd10
Add CVSS scoring and color to utils.ts and Vulnerability.tsx
ameliav Feb 7, 2024
e093d47
Add TODOs to Vulnerability.tsx
ameliav Feb 7, 2024
edf0694
Update installed known products and remove cwe name in Vulnerability.tsx
ameliav Feb 7, 2024
630c2ca
Remove unused typography tag in Vulnerability.tsx
ameliav Feb 7, 2024
c4a5c3a
Remove invalid reference from cpe get swagger comment.
Matthew-Grayson Feb 8, 2024
25d08c3
Merge branch '2306-redesign-vuln-details-page-overview-section' of gi…
Matthew-Grayson Feb 8, 2024
bdedb98
Add tags to CPE endpoint to improve organization in API docs.
Matthew-Grayson Feb 8, 2024
c96b849
Merge branch 'master' into 2306-redesign-vuln-details-page-overview-s…
ameliav Feb 9, 2024
4982994
Partially alphabetize type interfaces for cve and product-info keepin…
Matthew-Grayson Feb 9, 2024
f6a743f
Add issue number to Add Notes TODO in Vulnerability.tsx
ameliav Feb 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as cors from 'cors';
import * as helmet from 'helmet';
import { handler as healthcheck } from './healthcheck';
import * as auth from './auth';
import * as cpes from './cpes';
import * as cves from './cves';
import * as domains from './domains';
import * as search from './search';
import * as vulnerabilities from './vulnerabilities';
Expand Down Expand Up @@ -287,6 +289,12 @@ authenticatedRoute.delete('/api-keys/:keyId', handlerToExpress(apiKeys.del));

authenticatedRoute.post('/search', handlerToExpress(search.search));
authenticatedRoute.post('/search/export', handlerToExpress(search.export_));
authenticatedRoute.get('/cpes/:id', handlerToExpress(cpes.get));
authenticatedRoute.get('/cves/:cve_uid', handlerToExpress(cves.get));
authenticatedRoute.get(
'/cves/name/:cve_name',
handlerToExpress(cves.getByName)
);
authenticatedRoute.post('/domain/search', handlerToExpress(domains.list));
authenticatedRoute.post('/domain/export', handlerToExpress(domains.export_));
authenticatedRoute.get('/domain/:domainId', handlerToExpress(domains.get));
Expand Down
39 changes: 39 additions & 0 deletions backend/src/api/cpes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ProductInfo, connectToDatabase } from '../models';
import { wrapHandler, NotFound } from './helpers';

// TODO: Join cves to cpe get method
Matthew-Grayson marked this conversation as resolved.
Show resolved Hide resolved
// TODO: Create CpeFilters and CpeSearch classes to handle filtering and pagination of additional fields

/**
* @swagger
* /cpes/{id}:
* get:
* description: Retrieve a CPE by ID
* tags:
* - CPEs
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
*/
export const get = wrapHandler(async (event) => {
const connection = await connectToDatabase();
const repository = connection.getRepository(ProductInfo);

const id = event.pathParameters?.id;
if (!id) {
return NotFound;
}

const productInfo = await repository.findOne(id);
if (!productInfo) {
return NotFound;
}

return {
statusCode: 200,
body: JSON.stringify(productInfo)
};
});
79 changes: 79 additions & 0 deletions backend/src/api/cves.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Cve, connectToDatabase } from '../models';
import { wrapHandler } from './helpers';

// TODO: Add test for joining product_info
// TODO: Create CveFilters and CveSearch classes to handle filtering and pagination of additional fields

/**
* @swagger
* /cves/{cve_uid}:
* get:
* description: Retrieve a CVE by ID.
* tags:
* - CVEs
* parameters:
* - in: path
* name: cve_uid
* required: true
* schema:
* type: string
*/
export const get = wrapHandler(async (event) => {
await connectToDatabase();
const cve_uid = event.pathParameters?.cve_uid;

const cve = await Cve.createQueryBuilder('cve')
.leftJoinAndSelect('cve.product_info', 'product_info')
.where('cve.cve_uid = :cve_uid', { cve_uid: cve_uid })
.getOne();

if (!cve) {
return {
statusCode: 404,
body: JSON.stringify(Error)
};
}

return {
statusCode: 200,
body: JSON.stringify(cve)
};
});

//TODO: Remove getByName endpoint once a one-to-one relationship is established between vulnerability.cve and cve.cve_id
/**
* @swagger
*
* /cves/name/{cve_name}:
* get:
* description: Retrieve a single CVE record by its name.
* tags:
* - CVE
* parameters:
* - name: cve_name
* in: path
* required: true
* schema:
* type: string
*/
export const getByName = wrapHandler(async (event) => {
await connectToDatabase();
const cve_name = event.pathParameters?.cve_name;

const cve = await Cve.createQueryBuilder('cve')
.leftJoinAndSelect('cve.product_info', 'product_info')
.where('cve.cve_name = :cve_name', { cve_name })
.getOne();

if (!cve) {
return {
statusCode: 404,
body: JSON.stringify(Error)
};
}

return {
statusCode: 200,
body: JSON.stringify(cve)
};
});
7 changes: 3 additions & 4 deletions backend/src/api/vulnerabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ class VulnerabilitySearch {
: `vulnerability.${this.sort}`;
let qs = Vulnerability.createQueryBuilder('vulnerability')
.leftJoinAndSelect('vulnerability.domain', 'domain')
.leftJoinAndSelect('domain.organization', 'organization');
.leftJoinAndSelect('domain.organization', 'organization')
.leftJoinAndSelect('vulnerability.service', 'service');

if (groupBy) {
qs = qs
Expand All @@ -185,9 +186,7 @@ class VulnerabilitySearch {
])
.orderBy('cnt', 'DESC');
} else {
qs = qs
.leftJoinAndSelect('vulnerability.service', 'service')
.orderBy(sort, this.order);
qs = qs.orderBy(sort, this.order);
}

if (pageSize !== -1) {
Expand Down
3 changes: 2 additions & 1 deletion backend/src/models/cve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import {
} from 'typeorm';
import { ProductInfo } from './product-info';

//TODO: Refactor column names to camelCase to match the rest of the codebase?
@Entity()
@Unique(['cve_name'])
export class Cve extends BaseEntity {
@PrimaryGeneratedColumn('uuid')
cve_uid: string;
cve_uid: string; //TODO: Refactor to id to match other UUIDs?

@Column({ nullable: true })
cve_name: string;
Expand Down
7 changes: 3 additions & 4 deletions backend/src/models/product-info.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { mapping } from 'src/tasks/censys/mapping';
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToMany,
BaseEntity,
Unique
} from 'typeorm';
import { Cve } from './cve';

//TODO: Refactor column names to camelCase to match the rest of the codebase?
//TODO: Refactor table name to product or cpe for brevity?
@Entity()
@Unique(['cpe_product_name', 'version_number', 'vender'])
export class ProductInfo extends BaseEntity {
@PrimaryGeneratedColumn('uuid')
id: string; //TODO: change this to something else??
id: string;

@Column()
cpe_product_name: string;
Expand Down
51 changes: 51 additions & 0 deletions backend/test/cpes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as request from 'supertest';
import app from '../src/api/app';
import { Organization, ProductInfo, connectToDatabase } from '../src/models';
import { createUserToken } from './util';

describe('cpes', () => {
let connection;
let organization: Organization;
let productInfo: ProductInfo;
beforeAll(async () => {
connection = await connectToDatabase();
productInfo = ProductInfo.create({
last_seen: new Date(),
cpe_product_name: 'Test Product',
version_number: '1.0.0',
vender: 'Test Vender'
});
await productInfo.save();
organization = Organization.create({
name: 'test-' + Math.random(),
rootDomains: ['test-' + Math.random()],
ipBlocks: [],
isPassive: false
});
await organization.save();
});

afterAll(async () => {
await ProductInfo.delete(productInfo.id);
await connection.close();
});

describe('CPE API', () => {
it('should return a single CPE by id', async () => {
const response = await request(app)
.get(`/cpes/${productInfo.id}`)
.set(
'Authorization',
createUserToken({
roles: [{ org: organization.id, role: 'user' }]
})
)
.send({})
.expect(200);
expect(response.body.id).toEqual(productInfo.id);
expect(response.body.cpe_product_name).toEqual(
productInfo.cpe_product_name
);
});
});
});
63 changes: 63 additions & 0 deletions backend/test/cves.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as request from 'supertest';
import app from '../src/api/app';
import { Cve, Organization, connectToDatabase } from '../src/models';
import { createUserToken } from './util';

// TODO: Add test for joining product_info
describe('cves', () => {
let connection;
let cve: Cve;
let organization: Organization;
beforeAll(async () => {
connection = await connectToDatabase();
cve = Cve.create({
cve_name: 'CVE-0001-0001'
});
await cve.save();
organization = Organization.create({
name: 'test-' + Math.random(),
rootDomains: ['test-' + Math.random()],
ipBlocks: [],
isPassive: false
});
await organization.save();
});

afterAll(async () => {
await Cve.delete(cve.cve_uid);
await Organization.delete(organization.id);
await connection.close();
});
describe('CVE API', () => {
it('should return a single CVE by cve_name', async () => {
const response = await request(app)
.get(`/cves/name/${cve.cve_name}`)
.set(
'Authorization',
createUserToken({
roles: [{ org: organization.id, role: 'user' }]
})
)
.send({})
.expect(200);
expect(response.body.cve_uid).toEqual(cve.cve_uid);
expect(response.body.cve_name).toEqual(cve.cve_name);
});
});
describe('CVE API', () => {
it('should return a single CVE by cve_uid', async () => {
const response = await request(app)
.get(`/cves/${cve.cve_uid}`)
.set(
'Authorization',
createUserToken({
roles: [{ org: organization.id, role: 'user' }]
})
)
.send({})
.expect(200);
expect(response.body.cve_uid).toEqual(cve.cve_uid);
expect(response.body.cve_name).toEqual(cve.cve_name);
});
});
});
8 changes: 8 additions & 0 deletions frontend/src/pages/Risk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ export const getSeverityColor = ({ id }: { id: string }) => {
else if (id === 'High') return '#B51D09';
else return '#540C03';
};
export const getCVSSColor = (score: number) => {
if (!score || score === 0) return ['#EFF1F5', 'NONE'];
else if (0.1 <= score && score <= 3.9) return ['#99cc33', 'LOW'];
else if (4 <= score && score <= 6.9) return ['#ffcc00', 'MEDIUM'];
else if (7 <= score && score <= 8.9) return ['#ff9966', 'HIGH'];
else if (9 <= score && score <= 10) return ['#ff6254', 'CRITICAL'];
else return '#540C03';
};
export const offsets: any = {
Vermont: [50, -8],
'New Hampshire': [34, 2],
Expand Down
Loading
Loading