From e9494f3e121102ff5ede94eda6bf142e16dfc0fd Mon Sep 17 00:00:00 2001 From: Revanshu Paliwal Date: Thu, 29 Sep 2022 12:08:27 +0200 Subject: [PATCH] SONAR-17393 Add new security tab for OWASP ASVS --- .../js/apps/issues/__tests__/utils-test.ts | 2 + .../__snapshots__/IssuesApp-test.tsx.snap | 1 + .../js/apps/issues/sidebar/StandardFacet.tsx | 9 ++-- .../src/main/js/apps/issues/utils.ts | 8 +++- .../security-hotspots/SecurityHotspotsApp.tsx | 4 +- .../SecurityHotspotsApp-test.tsx.snap | 1 + .../SecurityHotspotsAppRenderer-test.tsx.snap | 10 ++++ .../__tests__/HotspotSimpleList-test.tsx | 3 +- .../main/js/apps/security-hotspots/utils.ts | 7 ++- .../__tests__/security-standard-test.ts | 47 ++++++++++++++++--- .../js/helpers/mocks/security-hotspots.ts | 5 ++ .../src/main/js/helpers/security-standard.ts | 9 ++++ server/sonar-web/src/main/js/helpers/urls.ts | 3 +- .../sonar-web/src/main/js/types/security.ts | 5 +- 14 files changed, 95 insertions(+), 19 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts index 9b01dc67a28..2f23a97d038 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts @@ -53,6 +53,7 @@ describe('serialize/deserialize', () => { 'owaspTop10-2021': ['a', 'b'], 'pciDss-3.2': ['a', 'b'], 'pciDss-4.0': ['a', 'b'], + 'owaspAsvs-4.0': ['2'], projects: ['a', 'b'], resolutions: ['a', 'b'], resolved: true, @@ -83,6 +84,7 @@ describe('serialize/deserialize', () => { 'owaspTop10-2021': 'a,b', 'pciDss-3.2': 'a,b', 'pciDss-4.0': 'a,b', + 'owaspAsvs-4.0': '2', projects: 'a,b', resolutions: 'a,b', rules: 'a,b', diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap index 84c81e0689f..31fcf90c9f7 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap @@ -118,6 +118,7 @@ exports[`should show warnning when not all projects are accessible 1`] = ` "inNewCodePeriod": false, "issues": Array [], "languages": Array [], + "owaspAsvs-4.0": Array [], "owaspTop10": Array [], "owaspTop10-2021": Array [], "pciDss-3.2": Array [], diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx index b093d6a73e1..02b892f6bb0 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx @@ -98,7 +98,8 @@ export default class StandardFacet extends React.PureComponent { cwe: {}, sonarsourceSecurity: {}, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} } }; @@ -137,7 +138,8 @@ export default class StandardFacet extends React.PureComponent { cwe, sonarsourceSecurity, 'pciDss-3.2': pciDss3_2, - 'pciDss-4.0': pciDss4_0 + 'pciDss-4.0': pciDss4_0, + 'owaspAsvs-4.0': owaspAsvs4_0 }: Standards) => { if (this.mounted) { this.setState({ @@ -148,7 +150,8 @@ export default class StandardFacet extends React.PureComponent { cwe, sonarsourceSecurity, 'pciDss-3.2': pciDss3_2, - 'pciDss-4.0': pciDss4_0 + 'pciDss-4.0': pciDss4_0, + 'owaspAsvs-4.0': owaspAsvs4_0 } }); } diff --git a/server/sonar-web/src/main/js/apps/issues/utils.ts b/server/sonar-web/src/main/js/apps/issues/utils.ts index c3023256cb6..9eac3657ed8 100644 --- a/server/sonar-web/src/main/js/apps/issues/utils.ts +++ b/server/sonar-web/src/main/js/apps/issues/utils.ts @@ -38,6 +38,8 @@ import { SecurityStandard } from '../../types/security'; import { Dict, Issue, Paging, RawQuery } from '../../types/types'; import { UserBase } from '../../types/users'; +const OWASP_ASVS_4_0 = 'owaspAsvs-4.0'; + export interface Query { assigned: boolean; assignees: string[]; @@ -55,6 +57,7 @@ export interface Query { 'owaspTop10-2021': string[]; 'pciDss-3.2': string[]; 'pciDss-4.0': string[]; + [OWASP_ASVS_4_0]: string[]; projects: string[]; resolutions: string[]; resolved: boolean; @@ -95,6 +98,7 @@ export function parseQuery(query: RawQuery): Query { 'owaspTop10-2021': parseAsArray(query['owaspTop10-2021'], parseAsString), 'pciDss-3.2': parseAsArray(query['pciDss-3.2'], parseAsString), 'pciDss-4.0': parseAsArray(query['pciDss-4.0'], parseAsString), + [OWASP_ASVS_4_0]: parseAsArray(query[OWASP_ASVS_4_0], parseAsString), projects: parseAsArray(query.projects, parseAsString), resolutions: parseAsArray(query.resolutions, parseAsString), resolved: parseAsBoolean(query.resolved), @@ -139,6 +143,7 @@ export function serializeQuery(query: Query): RawQuery { 'owaspTop10-2021': serializeStringArray(query['owaspTop10-2021']), 'pciDss-3.2': serializeStringArray(query['pciDss-3.2']), 'pciDss-4.0': serializeStringArray(query['pciDss-4.0']), + [OWASP_ASVS_4_0]: serializeStringArray(query[OWASP_ASVS_4_0]), projects: serializeStringArray(query.projects), resolutions: serializeStringArray(query.resolutions), resolved: query.resolved ? undefined : 'false', @@ -228,9 +233,8 @@ export function getSelectedLocation( locations.length >= selectedLocationIndex ) { return locations[selectedLocationIndex]; - } else { - return undefined; } + return undefined; } export function allLocationsEmpty( diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx index 260afe05f41..d4669b90065 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx @@ -99,7 +99,8 @@ export class SecurityHotspotsApp extends React.PureComponent { [SecurityStandard.SONARSOURCE]: {}, [SecurityStandard.CWE]: {}, [SecurityStandard.PCI_DSS_3_2]: {}, - [SecurityStandard.PCI_DSS_4_0]: {} + [SecurityStandard.PCI_DSS_4_0]: {}, + [SecurityStandard.OWASP_ASVS_4_0]: {} }, filters: { ...this.constructFiltersFromProps(props), @@ -454,6 +455,7 @@ export class SecurityHotspotsApp extends React.PureComponent { [SecurityStandard.OWASP_TOP10_2021]: undefined, [SecurityStandard.PCI_DSS_3_2]: undefined, [SecurityStandard.PCI_DSS_4_0]: undefined, + [SecurityStandard.OWASP_ASVS_4_0]: undefined, file: undefined } }); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap index 4b6a0206bfb..1891d1068b1 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap @@ -56,6 +56,7 @@ exports[`should render correctly 1`] = ` standards={ Object { "cwe": Object {}, + "owaspAsvs-4.0": Object {}, "owaspTop10": Object {}, "owaspTop10-2021": Object {}, "pciDss-3.2": Object {}, diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap index 9a9fc621891..6e3c4c9c0db 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap @@ -145,6 +145,11 @@ exports[`should render correctly when filtered by category or cwe: category 1`] "title": "No CWE associated", }, }, + "owaspAsvs-4.0": Object { + "1": Object { + "title": "New OWASP ASVS cat 1", + }, + }, "owaspTop10": Object { "a1": Object { "title": "Injection", @@ -286,6 +291,11 @@ exports[`should render correctly when filtered by category or cwe: cwe 1`] = ` "title": "No CWE associated", }, }, + "owaspAsvs-4.0": Object { + "1": Object { + "title": "New OWASP ASVS cat 1", + }, + }, "owaspTop10": Object { "a1": Object { "title": "Injection", diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx index 9a660270786..29a57bf93fb 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx @@ -79,7 +79,8 @@ function shallowRender(props: Partial = {}) { sansTop25: {}, sonarsourceSecurity: {}, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts b/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts index 02f28614fd0..e425b2e8d94 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts +++ b/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts @@ -20,6 +20,7 @@ import { flatten, groupBy, sortBy } from 'lodash'; import { renderCWECategory, + renderOwaspAsvs40Category, renderOwaspTop102021Category, renderOwaspTop10Category, renderPciDss32Category, @@ -56,7 +57,8 @@ export const SECURITY_STANDARDS = [ SecurityStandard.SANS_TOP25, SecurityStandard.CWE, SecurityStandard.PCI_DSS_3_2, - SecurityStandard.PCI_DSS_4_0 + SecurityStandard.PCI_DSS_4_0, + SecurityStandard.OWASP_ASVS_4_0 ]; export const SECURITY_STANDARD_RENDERER = { @@ -66,7 +68,8 @@ export const SECURITY_STANDARD_RENDERER = { [SecurityStandard.SONARSOURCE]: renderSonarSourceSecurityCategory, [SecurityStandard.CWE]: renderCWECategory, [SecurityStandard.PCI_DSS_3_2]: renderPciDss32Category, - [SecurityStandard.PCI_DSS_4_0]: renderPciDss40Category + [SecurityStandard.PCI_DSS_4_0]: renderPciDss40Category, + [SecurityStandard.OWASP_ASVS_4_0]: renderOwaspAsvs40Category }; export function mapRules(rules: Array<{ key: string; name: string }>): Dict { diff --git a/server/sonar-web/src/main/js/helpers/__tests__/security-standard-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/security-standard-test.ts index 38c76db0106..1032221e971 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/security-standard-test.ts +++ b/server/sonar-web/src/main/js/helpers/__tests__/security-standard-test.ts @@ -20,6 +20,7 @@ import { Standards } from '../../types/security'; import { renderCWECategory, + renderOwaspAsvs40Category, renderOwaspTop102021Category, renderOwaspTop10Category, renderPciDss32Category, @@ -43,7 +44,8 @@ describe('renderCWECategory', () => { sansTop25: {}, sonarsourceSecurity: {}, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }; it('should render cwe categories correctly', () => { expect(renderCWECategory(standards, '1004')).toEqual( @@ -66,7 +68,8 @@ describe('renderOwaspTop10Category', () => { sansTop25: {}, sonarsourceSecurity: {}, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }; it('should render owasp categories correctly', () => { expect(renderOwaspTop10Category(standards, 'a1')).toEqual('A1 - Injection'); @@ -88,7 +91,8 @@ describe('renderOwaspTop102021Category', () => { sansTop25: {}, sonarsourceSecurity: {}, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }; it('should render owasp categories correctly', () => { expect(renderOwaspTop102021Category(standards, 'a1')).toEqual('A1 - Injection'); @@ -110,7 +114,8 @@ describe('renderPciDss32Category', () => { title: 'Install and maintain a firewall configuration to protect cardholder data' } }, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }; it('should render Pci Dss 3.2 correctly', () => { expect(renderPciDss32Category(standards, '1')).toEqual( @@ -132,7 +137,8 @@ describe('renderPciDss40Category', () => { '1': { title: 'Install and maintain a firewall configuration to protect cardholder data' } - } + }, + 'owaspAsvs-4.0': {} }; it('should render Pci Dss 4.0 correctly', () => { expect(renderPciDss40Category(standards, '1')).toEqual( @@ -142,6 +148,31 @@ describe('renderPciDss40Category', () => { }); }); +describe('renderOwaspAsvs40Category', () => { + const standards: Standards = { + cwe: {}, + owaspTop10: {}, + 'owaspTop10-2021': {}, + sansTop25: {}, + sonarsourceSecurity: {}, + 'pciDss-3.2': {}, + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': { + '1': { + title: 'Main category' + }, + '1.1': { + title: 'Sub category', + level: '2' + } + } + }; + it('should render OwaspAsvs 4.0 correctly', () => { + expect(renderOwaspAsvs40Category(standards, '1')).toEqual('1 - Main category'); + expect(renderOwaspAsvs40Category(standards, '1.1')).toEqual('1.1 - Sub category (Level 2)'); + }); +}); + describe('renderSansTop25Category', () => { const standards: Standards = { cwe: {}, @@ -154,7 +185,8 @@ describe('renderSansTop25Category', () => { }, sonarsourceSecurity: {}, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }; it('should render sans categories correctly', () => { expect(renderSansTop25Category(standards, 'insecure-interaction')).toEqual( @@ -183,7 +215,8 @@ describe('renderSonarSourceSecurityCategory', () => { } }, 'pciDss-3.2': {}, - 'pciDss-4.0': {} + 'pciDss-4.0': {}, + 'owaspAsvs-4.0': {} }; it('should render sonarsource categories correctly', () => { expect(renderSonarSourceSecurityCategory(standards, 'xss')).toEqual( diff --git a/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts b/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts index 57026803263..6ce02172949 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts @@ -193,6 +193,11 @@ export function mockStandards(): Standards { '2': { title: 'This is useless...' } + }, + 'owaspAsvs-4.0': { + '1': { + title: 'New OWASP ASVS cat 1' + } } }; } diff --git a/server/sonar-web/src/main/js/helpers/security-standard.ts b/server/sonar-web/src/main/js/helpers/security-standard.ts index 870f62e934f..ab51b5f107e 100644 --- a/server/sonar-web/src/main/js/helpers/security-standard.ts +++ b/server/sonar-web/src/main/js/helpers/security-standard.ts @@ -101,6 +101,15 @@ export function renderPciDss40Category(standards: Standards, category: string): return `${category} - ${record.title}`; } +export function renderOwaspAsvs40Category(standards: Standards, category: string): string { + const record = standards['owaspAsvs-4.0'][category]; + if (!record) { + return category; + } + const levelInfo = record.level ? ` (Level ${record.level})` : ''; + return `${category} - ${record.title}${levelInfo}`; +} + function addPrefix(title: string, prefix: string, withPrefix: boolean) { return withPrefix ? `${prefix} ${title}` : title; } diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts index 73d69cfc6bb..c7d6cffd444 100644 --- a/server/sonar-web/src/main/js/helpers/urls.ts +++ b/server/sonar-web/src/main/js/helpers/urls.ts @@ -213,7 +213,8 @@ export function getComponentSecurityHotspotsUrl(componentKey: string, query: Que SecurityStandard.SANS_TOP25, SecurityStandard.CWE, SecurityStandard.PCI_DSS_3_2, - SecurityStandard.PCI_DSS_4_0 + SecurityStandard.PCI_DSS_4_0, + SecurityStandard.OWASP_ASVS_4_0 ]) }), hash: '' diff --git a/server/sonar-web/src/main/js/types/security.ts b/server/sonar-web/src/main/js/types/security.ts index b77e256874c..eb5992ca939 100644 --- a/server/sonar-web/src/main/js/types/security.ts +++ b/server/sonar-web/src/main/js/types/security.ts @@ -26,11 +26,12 @@ export enum SecurityStandard { SONARSOURCE = 'sonarsourceSecurity', CWE = 'cwe', PCI_DSS_3_2 = 'pciDss-3.2', - PCI_DSS_4_0 = 'pciDss-4.0' + PCI_DSS_4_0 = 'pciDss-4.0', + OWASP_ASVS_4_0 = 'owaspAsvs-4.0' } export type StandardType = SecurityStandard; export type Standards = { - [key in StandardType]: Dict<{ title: string; description?: string }>; + [key in StandardType]: Dict<{ title: string; description?: string; level?: string }>; }; -- 2.39.5