From 57f9d92cb9616895b344a7a8fdaaf8c96b420f77 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 18 Jan 2021 18:14:38 +0100 Subject: [PATCH] Add support for Security Hotspots that are provided by SonarQube and SonarCloud --- .changeset/giant-hairs-collect.md | 5 +++++ .github/styles/vocab.txt | 1 + plugins/sonarqube/dev/index.tsx | 9 +++++++++ plugins/sonarqube/src/api/SonarQubeApi.ts | 1 + .../sonarqube/src/api/SonarQubeClient.test.ts | 16 ++++++++++++++- plugins/sonarqube/src/api/SonarQubeClient.ts | 6 +++++- plugins/sonarqube/src/api/types.ts | 4 ++++ .../components/SonarQubeCard/RatingCard.tsx | 1 + .../SonarQubeCard/SonarQubeCard.tsx | 20 +++++++++++++++++++ 9 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 .changeset/giant-hairs-collect.md diff --git a/.changeset/giant-hairs-collect.md b/.changeset/giant-hairs-collect.md new file mode 100644 index 0000000000..01d2790354 --- /dev/null +++ b/.changeset/giant-hairs-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-sonarqube': patch +--- + +Add support for the security hotspots that are provided by SonarQube and SonarCloud. diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index c3c67ab66e..625098e915 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -92,6 +92,7 @@ Henneke Heroku horizontalpodautoscalers Hostname +hotspots html http https diff --git a/plugins/sonarqube/dev/index.tsx b/plugins/sonarqube/dev/index.tsx index 830d8aa575..5e8aedd577 100644 --- a/plugins/sonarqube/dev/index.tsx +++ b/plugins/sonarqube/dev/index.tsx @@ -59,6 +59,8 @@ createDevApp() projectUrl: `/#${componentKey}`, getIssuesUrl: i => `/#${componentKey}/issues/${i}`, getComponentMeasuresUrl: i => `/#${componentKey}/measures/${i}`, + getSecurityHotspotsUrl: () => + `#${componentKey}/security_hotspots`, } as FindingSummary; case 'failed': @@ -70,6 +72,7 @@ createDevApp() reliability_rating: '2.0', vulnerabilities: '18', security_rating: '3.0', + security_review_rating: '3.0', code_smells: '22', sqale_rating: '5.0', coverage: '15.7', @@ -78,6 +81,8 @@ createDevApp() projectUrl: `/#${componentKey}`, getIssuesUrl: i => `/#${componentKey}/issues/${i}`, getComponentMeasuresUrl: i => `/#${componentKey}/measures/${i}`, + getSecurityHotspotsUrl: () => + `#${componentKey}/security_hotspots`, } as FindingSummary; case 'passed': @@ -89,6 +94,8 @@ createDevApp() reliability_rating: '1.0', vulnerabilities: '0', security_rating: '1.0', + security_hotspots_reviewed: '100.0', + security_review_rating: '1.0', code_smells: '0', sqale_rating: '1.0', coverage: '100.0', @@ -97,6 +104,8 @@ createDevApp() projectUrl: `/#${componentKey}`, getIssuesUrl: i => `/#${componentKey}/issues/${i}`, getComponentMeasuresUrl: i => `/#${componentKey}/measures/${i}`, + getSecurityHotspotsUrl: () => + `#${componentKey}/security_hotspots`, } as FindingSummary; default: diff --git a/plugins/sonarqube/src/api/SonarQubeApi.ts b/plugins/sonarqube/src/api/SonarQubeApi.ts index 5734f9a3ca..0715d1fd02 100644 --- a/plugins/sonarqube/src/api/SonarQubeApi.ts +++ b/plugins/sonarqube/src/api/SonarQubeApi.ts @@ -30,6 +30,7 @@ export interface FindingSummary { projectUrl: string; getIssuesUrl: SonarUrlProcessorFunc; getComponentMeasuresUrl: SonarUrlProcessorFunc; + getSecurityHotspotsUrl: () => string; } export const sonarQubeApiRef = createApiRef({ diff --git a/plugins/sonarqube/src/api/SonarQubeClient.test.ts b/plugins/sonarqube/src/api/SonarQubeClient.test.ts index a3ae24de52..1b3b5c5709 100644 --- a/plugins/sonarqube/src/api/SonarQubeClient.test.ts +++ b/plugins/sonarqube/src/api/SonarQubeClient.test.ts @@ -46,7 +46,7 @@ describe('SonarQubeClient', () => { server.use( rest.get(`${mockBaseUrl}/sonarqube/measures/search`, (req, res, ctx) => { expect(req.url.searchParams.toString()).toBe( - 'projectKeys=our-service&metricKeys=alert_status%2Cbugs%2Creliability_rating%2Cvulnerabilities%2Csecurity_rating%2Ccode_smells%2Csqale_rating%2Ccoverage%2Cduplicated_lines_density', + 'projectKeys=our-service&metricKeys=alert_status%2Cbugs%2Creliability_rating%2Cvulnerabilities%2Csecurity_rating%2Csecurity_hotspots_reviewed%2Csecurity_review_rating%2Ccode_smells%2Csqale_rating%2Ccoverage%2Cduplicated_lines_density', ); return res( ctx.json({ @@ -81,6 +81,16 @@ describe('SonarQubeClient', () => { value: '1.0', component: 'our-service', }, + { + metric: 'security_hotspots_reviewed', + value: '100', + component: 'our-service', + }, + { + metric: 'security_review_rating', + value: '1.0', + component: 'our-service', + }, { metric: 'code_smells', value: '100', @@ -123,6 +133,8 @@ describe('SonarQubeClient', () => { reliability_rating: '3.0', vulnerabilities: '4', security_rating: '1.0', + security_hotspots_reviewed: '100', + security_review_rating: '1.0', code_smells: '100', sqale_rating: '2.0', coverage: '55.5', @@ -158,6 +170,8 @@ describe('SonarQubeClient', () => { reliability_rating: '3.0', vulnerabilities: '4', security_rating: '1.0', + security_hotspots_reviewed: '100', + security_review_rating: '1.0', code_smells: '100', sqale_rating: '2.0', coverage: '55.5', diff --git a/plugins/sonarqube/src/api/SonarQubeClient.ts b/plugins/sonarqube/src/api/SonarQubeClient.ts index 893f32b0a6..061919dd41 100644 --- a/plugins/sonarqube/src/api/SonarQubeClient.ts +++ b/plugins/sonarqube/src/api/SonarQubeClient.ts @@ -63,6 +63,8 @@ export class SonarQubeClient implements SonarQubeApi { reliability_rating: undefined, vulnerabilities: undefined, security_rating: undefined, + security_hotspots_reviewed: undefined, + security_review_rating: undefined, code_smells: undefined, sqale_rating: undefined, coverage: undefined, @@ -92,10 +94,12 @@ export class SonarQubeClient implements SonarQubeApi { `${ this.baseUrl }project/issues?id=${componentKey}&types=${identifier.toUpperCase()}&resolved=false`, - getComponentMeasuresUrl: (identifier: string) => + getComponentMeasuresUrl: identifier => `${ this.baseUrl }component_measures?id=${componentKey}&metric=${identifier.toLowerCase()}&resolved=false&view=list`, + getSecurityHotspotsUrl: () => + `${this.baseUrl}project/security_hotspots?id=${componentKey}`, }; } } diff --git a/plugins/sonarqube/src/api/types.ts b/plugins/sonarqube/src/api/types.ts index fc99b2d87d..1e0593126f 100644 --- a/plugins/sonarqube/src/api/types.ts +++ b/plugins/sonarqube/src/api/types.ts @@ -42,6 +42,10 @@ export type MetricKey = | 'code_smells' | 'sqale_rating' + // security hotspots + | 'security_hotspots_reviewed' + | 'security_review_rating' + // coverage | 'coverage' diff --git a/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx index 6a94f7e8f4..37e978bbe9 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/RatingCard.tsx @@ -22,6 +22,7 @@ const useStyles = makeStyles(theme => { return { root: { margin: theme.spacing(1, 0), + minWidth: '140px', }, upper: { display: 'flex', diff --git a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx index a68a022717..c5e295a22b 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx @@ -26,6 +26,7 @@ import { Chip, Grid } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import BugReport from '@material-ui/icons/BugReport'; import LockOpen from '@material-ui/icons/LockOpen'; +import Security from '@material-ui/icons/Security'; import SentimentVeryDissatisfied from '@material-ui/icons/SentimentVeryDissatisfied'; import React, { useMemo } from 'react'; import { useAsync } from 'react-use'; @@ -205,6 +206,25 @@ export const SonarQubeCard = ({ leftSlot={} rightSlot={} /> + {value.metrics.security_review_rating && ( + } + title="Hotspots Reviewed" + link={value.getSecurityHotspotsUrl()} + leftSlot={ + + } + rightSlot={ + + } + /> + )}