diff options
author | 7PH <b.raymond@protonmail.com> | 2024-09-04 14:01:21 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-12 20:02:55 +0000 |
commit | 4674af96f13e9f48a6a371ddd07c21ae2859c66d (patch) | |
tree | 29bdff5a8254d815bb1d8da1454d949cd88c9b9c /server/sonar-web/src/main/js/apps | |
parent | 7ffdf0a08ac0557bcf1c29035500bc0f7cabed3a (diff) | |
download | sonarqube-4674af96f13e9f48a6a371ddd07c21ae2859c66d.tar.gz sonarqube-4674af96f13e9f48a6a371ddd07c21ae2859c66d.zip |
SONAR-22914 Render CVE information in Issues and Security Hotspots pages
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
11 files changed, 176 insertions, 49 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx index 09cb5e4f844..d341530762b 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx @@ -24,12 +24,13 @@ import React from 'react'; import { byRole, byText } from '~sonar-aligned/helpers/testSelector'; import { ISSUE_101 } from '../../../api/mocks/data/ids'; import { TabKeys } from '../../../components/rules/RuleTabViewer'; -import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; +import { mockCurrentUser, mockCve, mockLoggedInUser } from '../../../helpers/testMocks'; import { Feature } from '../../../types/features'; import { RestUserDetailed } from '../../../types/users'; import { branchHandler, componentsHandler, + cveHandler, issuesHandler, renderIssueApp, renderProjectIssuesApp, @@ -63,6 +64,7 @@ jest.mock('../../../components/common/ScreenPositionHelper', () => ({ beforeEach(() => { issuesHandler.reset(); + cveHandler.reset(); componentsHandler.reset(); branchHandler.reset(); usersHandler.reset(); @@ -211,6 +213,52 @@ describe('issue app', () => { expect(screen.getByRole('heading', { name: 'Defense-In-Depth', level: 3 })).toBeInTheDocument(); }); + it('should render CVE details', async () => { + const user = userEvent.setup(); + renderProjectIssuesApp('project/issues?issues=issue2&open=issue2&id=myproject'); + + await user.click( + await screen.findByRole('tab', { name: 'coding_rules.description_section.title.root_cause' }), + ); + + await user.click(screen.getByRole('radio', { name: 'coding_rules.description_context.other' })); + + expect(await screen.findByRole('heading', { name: 'CVE-2021-12345' })).toBeInTheDocument(); + + const rows = byRole('row').getAll(ui.cveTable.get()); + expect(rows).toHaveLength(4); + expect(byText('CWE-79, CWE-89').get(rows[0])).toBeInTheDocument(); + expect(byText('rule.cve_details.epss_score.value.20.56').get(rows[1])).toBeInTheDocument(); + expect(byText('0.3').get(rows[2])).toBeInTheDocument(); + expect(byText('Oct 04, 2021').get(rows[3])).toBeInTheDocument(); + }); + + it('should not render CVE CVSS and CWEs when not set', async () => { + const user = userEvent.setup(); + cveHandler.setCveList([ + mockCve({ + cvssScore: undefined, + cwes: [], + }), + ]); + renderProjectIssuesApp('project/issues?issues=issue2&open=issue2&id=myproject'); + + await user.click( + await screen.findByRole('tab', { name: 'coding_rules.description_section.title.root_cause' }), + ); + + await user.click( + await screen.findByRole('radio', { name: 'coding_rules.description_context.other' }), + ); + + expect(await screen.findByRole('heading', { name: 'CVE-2021-12345' })).toBeInTheDocument(); + + const rows = byRole('row').getAll(ui.cveTable.get()); + expect(rows).toHaveLength(2); + expect(byText('rule.cve_details.epss_score.value.20.56').get(rows[0])).toBeInTheDocument(); + expect(byText('Oct 04, 2021').get(rows[1])).toBeInTheDocument(); + }); + it('should be able to change the issue status', async () => { const user = userEvent.setup(); issuesHandler.setIsAdmin(true); diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx index 2ce9343bb76..30af209bcb4 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx @@ -41,6 +41,7 @@ import { getBranchLikeQuery, isPullRequest } from '~sonar-aligned/helpers/branch import { isPortfolioLike } from '~sonar-aligned/helpers/component'; import { ComponentQualifier } from '~sonar-aligned/types/component'; import { Location, RawQuery, Router } from '~sonar-aligned/types/router'; +import { getCve } from '../../../api/cves'; import { listIssues, searchIssues } from '../../../api/issues'; import { getRuleDetails } from '../../../api/rules'; import withComponentContext from '../../../app/components/componentContext/withComponentContext'; @@ -66,6 +67,7 @@ import { serializeDate } from '../../../helpers/query'; import { withBranchLikes } from '../../../queries/branch'; import { BranchLike } from '../../../types/branch-like'; import { isProject } from '../../../types/component'; +import { Cve } from '../../../types/cves'; import { ASSIGNEE_ME, Facet, @@ -122,6 +124,7 @@ export interface State { bulkChangeModal: boolean; checkAll?: boolean; checked: string[]; + cve?: Cve; effortTotal?: number; facets: Dict<Facet>; issues: Issue[]; @@ -383,8 +386,13 @@ export class App extends React.PureComponent<Props, State> { .then((response) => response.rule) .catch(() => undefined); + let cve: Cve | undefined; + if (typeof openIssue.cveId === 'string') { + cve = await getCve(openIssue.cveId); + } + if (this.mounted) { - this.setState({ loadingRule: false, openRuleDetails }); + this.setState({ loadingRule: false, openRuleDetails, cve }); } } @@ -1211,7 +1219,7 @@ export class App extends React.PureComponent<Props, State> { } renderPage() { - const { openRuleDetails, checkAll, issues, loading, openIssue, paging, loadingRule } = + const { openRuleDetails, cve, checkAll, issues, loading, openIssue, paging, loadingRule } = this.state; return ( @@ -1259,6 +1267,7 @@ export class App extends React.PureComponent<Props, State> { onIssueChange={this.handleIssueChange} ruleDescriptionContextKey={openIssue.ruleDescriptionContextKey} ruleDetails={openRuleDetails} + cve={cve} selectedFlowIndex={this.state.selectedFlowIndex} selectedLocationIndex={this.state.selectedLocationIndex} /> diff --git a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx index 995d63c8259..3918108f48b 100644 --- a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx +++ b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx @@ -23,6 +23,7 @@ import { Outlet, Route } from 'react-router-dom'; import { byPlaceholderText, byRole, byTestId, byText } from '~sonar-aligned/helpers/testSelector'; import BranchesServiceMock from '../../api/mocks/BranchesServiceMock'; import ComponentsServiceMock from '../../api/mocks/ComponentsServiceMock'; +import CveServiceMock from '../../api/mocks/CveServiceMock'; import FixIssueServiceMock from '../../api/mocks/FixIssueServiceMock'; import IssuesServiceMock from '../../api/mocks/IssuesServiceMock'; import SourcesServiceMock from '../../api/mocks/SourcesServiceMock'; @@ -43,6 +44,7 @@ import { projectIssuesRoutes } from './routes'; export const usersHandler = new UsersServiceMock(); export const issuesHandler = new IssuesServiceMock(usersHandler); +export const cveHandler = new CveServiceMock(); export const componentsHandler = new ComponentsServiceMock(); export const sourcesHandler = new SourcesServiceMock(); export const branchHandler = new BranchesServiceMock(); @@ -142,6 +144,8 @@ export const ui = { vulnerabilityIssueTypeFilter: byRole('checkbox', { name: 'issue.type.VULNERABILITY' }), prioritizedRuleFilter: byRole('checkbox', { name: 'issues.facet.prioritized_rule' }), + cveTable: byRole('table', { name: 'rule.cve_details' }), + bulkChangeComment: byRole('textbox', { name: /issue_bulk_change.resolution_comment/ }), clearAllFilters: byRole('button', { name: 'clear_all_filters' }), diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx index 0380c756328..9d64bf4a634 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx @@ -244,6 +244,7 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe <HotspotViewer component={component} hotspotKey={selectedHotspot.key} + cveId={selectedHotspot.cveId} hotspotsReviewedMeasure={hotspotsReviewedMeasure} onLocationClick={props.onLocationClick} onSwitchStatusFilter={props.onSwitchStatusFilter} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx index 4fef8864a5f..8e4e74463e8 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx @@ -25,13 +25,14 @@ import { byDisplayValue, byRole, byTestId, byText } from '~sonar-aligned/helpers import { MetricKey } from '~sonar-aligned/types/metrics'; import BranchesServiceMock from '../../../api/mocks/BranchesServiceMock'; import CodingRulesServiceMock from '../../../api/mocks/CodingRulesServiceMock'; +import CveServiceMock from '../../../api/mocks/CveServiceMock'; import SecurityHotspotServiceMock from '../../../api/mocks/SecurityHotspotServiceMock'; import { getSecurityHotspots, setSecurityHotspotStatus } from '../../../api/security-hotspots'; import { getUsers } from '../../../api/users'; import { mockComponent } from '../../../helpers/mocks/component'; import { openHotspot, probeSonarLintServers } from '../../../helpers/sonarlint'; import { get, save } from '../../../helpers/storage'; -import { mockLoggedInUser } from '../../../helpers/testMocks'; +import { mockCve, mockLoggedInUser } from '../../../helpers/testMocks'; import { renderAppWithComponentContext } from '../../../helpers/testReactTestingUtils'; import { ComponentContextShape } from '../../../types/component'; import SecurityHotspotsApp from '../SecurityHotspotsApp'; @@ -85,6 +86,7 @@ const ui = { filterToReview: byRole('radio', { name: 'hotspot.filters.status.to_review' }), fixContent: byText('This is how to fix'), fixTab: byRole('tab', { name: /hotspots.tabs.fix_recommendations/ }), + cveTable: byRole('table', { name: 'rule.cve_details' }), hotpostListTitle: byText('hotspots.list_title'), hotspotCommentBox: byRole('textbox', { name: 'hotspots.comment.field' }), hotspotStatus: byRole('heading', { name: 'status: hotspots.status_option.FIXED' }), @@ -107,6 +109,7 @@ const ui = { const originalScrollTo = window.scrollTo; const hotspotsHandler = new SecurityHotspotServiceMock(); +const cveHandler = new CveServiceMock(); const rulesHandles = new CodingRulesServiceMock(); const branchHandler = new BranchesServiceMock(); @@ -147,6 +150,7 @@ beforeEach(() => { afterEach(() => { hotspotsHandler.reset(); + cveHandler.reset(); rulesHandles.reset(); branchHandler.reset(); }); @@ -187,6 +191,46 @@ describe('rendering', () => { expect(await ui.reviewButton.findAll()).toHaveLength(2); }); + + it('should render CVE details', async () => { + const user = userEvent.setup(); + + renderSecurityHotspotsApp( + 'security_hotspots?id=guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed&hotspots=test-cve', + ); + + await user.click(await ui.riskTab.find()); + expect(await screen.findByRole('heading', { name: 'CVE-2021-12345' })).toBeInTheDocument(); + + const rows = byRole('row').getAll(ui.cveTable.get()); + expect(rows).toHaveLength(4); + expect(byText('CWE-79, CWE-89').get(rows[0])).toBeInTheDocument(); + expect(byText('rule.cve_details.epss_score.value.20.56').get(rows[1])).toBeInTheDocument(); + expect(byText('0.3').get(rows[2])).toBeInTheDocument(); + expect(byText('Oct 04, 2021').get(rows[3])).toBeInTheDocument(); + }); + + it('should not render CVE CVSS and CWEs when not set', async () => { + const user = userEvent.setup(); + cveHandler.setCveList([ + mockCve({ + cvssScore: undefined, + cwes: [], + }), + ]); + + renderSecurityHotspotsApp( + 'security_hotspots?id=guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed&hotspots=test-cve', + ); + + await user.click(await ui.riskTab.find()); + expect(await screen.findByRole('heading', { name: 'CVE-2021-12345' })).toBeInTheDocument(); + + const rows = byRole('row').getAll(ui.cveTable.get()); + expect(rows).toHaveLength(2); + expect(byText('rule.cve_details.epss_score.value.20.56').get(rows[0])).toBeInTheDocument(); + expect(byText('Oct 04, 2021').get(rows[1])).toBeInTheDocument(); + }); }); describe('CRUD', () => { diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx index 8f301649c64..247f2f0bd60 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx @@ -197,7 +197,7 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat }; render() { - const { hotspot, selectedHotspotLocation } = this.props; + const { branchLike, component, hotspot, selectedHotspotLocation } = this.props; const { highlightedSymbols, lastLine, loading, sourceLines, secondaryLocations } = this.state; const locations = locationsByLine([hotspot]); @@ -206,6 +206,8 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat return ( <HotspotSnippetContainerRenderer + component={component} + branchLike={branchLike} highlightedSymbols={highlightedSymbols} hotspot={hotspot} loading={loading} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx index 43179dd7c44..bca90e9df1b 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx @@ -19,11 +19,14 @@ */ import { withTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { Spinner, themeColor } from 'design-system'; +import { Spinner } from '@sonarsource/echoes-react'; +import { FlagMessage, themeColor } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; +import { BranchLike } from '../../../types/branch-like'; import { Hotspot } from '../../../types/security-hotspots'; import { + Component, ExpandDirection, FlowLocation, LinearIssueLocation, @@ -32,8 +35,11 @@ import { } from '../../../types/types'; import SnippetViewer from '../../issues/crossComponentSourceViewer/SnippetViewer'; import HotspotPrimaryLocationBox from './HotspotPrimaryLocationBox'; +import HotspotSnippetHeader from './HotspotSnippetHeader'; export interface HotspotSnippetContainerRendererProps { + branchLike?: BranchLike; + component: Component; highlightedSymbols: string[]; hotspot: Hotspot; loading: boolean; @@ -113,6 +119,8 @@ export default function HotspotSnippetContainerRenderer( selectedHotspotLocation, sourceLines, sourceViewerFile, + component, + branchLike, } = props; const scrollableRef = React.useRef<HTMLDivElement>(null); @@ -139,37 +147,38 @@ export default function HotspotSnippetContainerRenderer( : undefined; return ( - <> - {!loading && sourceLines.length === 0 && ( - <p className="sw-my-4">{translate('hotspots.no_associated_lines')}</p> + <Spinner isLoading={loading}> + {sourceLines.length === 0 && ( + <FlagMessage variant="info">{translate('hotspots.no_associated_lines')}</FlagMessage> )} - <SourceFileWrapper className="sw-box-border sw-w-full sw-rounded-1" ref={scrollableRef}> - <Spinner className="sw-m-4" loading={loading} /> - - {!loading && sourceLines.length > 0 && ( - <SnippetViewer - component={sourceViewerFile} - displayLineNumberOptions={false} - displaySCM={false} - expandBlock={(_i, direction) => - animateExpansion(scrollableRef, props.onExpandBlock, direction) - } - handleSymbolClick={props.onSymbolClick} - highlightedLocationMessage={highlightedLocation} - highlightedSymbols={highlightedSymbols} - index={0} - locations={secondaryLocations} - locationsByLine={primaryLocations} - onLocationSelect={props.onLocationSelect} - renderAdditionalChildInLine={renderHotspotBoxInLine} - renderDuplicationPopup={noop} - snippet={sourceLines} - hideLocationIndex={secondaryLocations.length !== 0} - /> - )} - </SourceFileWrapper> - </> + {sourceLines.length > 0 && ( + <> + <HotspotSnippetHeader hotspot={hotspot} component={component} branchLike={branchLike} /> + <SourceFileWrapper className="sw-box-border sw-w-full sw-rounded-1" ref={scrollableRef}> + <SnippetViewer + component={sourceViewerFile} + displayLineNumberOptions={false} + displaySCM={false} + expandBlock={(_i, direction) => + animateExpansion(scrollableRef, props.onExpandBlock, direction) + } + handleSymbolClick={props.onSymbolClick} + highlightedLocationMessage={highlightedLocation} + highlightedSymbols={highlightedSymbols} + index={0} + locations={secondaryLocations} + locationsByLine={primaryLocations} + onLocationSelect={props.onLocationSelect} + renderAdditionalChildInLine={renderHotspotBoxInLine} + renderDuplicationPopup={noop} + snippet={sourceLines} + hideLocationIndex={secondaryLocations.length !== 0} + /> + </SourceFileWrapper> + </> + )} + </Spinner> ); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetHeader.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetHeader.tsx index 29add9012d1..84db10765de 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetHeader.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetHeader.tsx @@ -52,7 +52,7 @@ function HotspotSnippetHeader(props: HotspotSnippetHeaderProps) { return ( <StyledHeader - className={`sw-box-border sw-flex sw-gap-2 sw-justify-between -sw-mb-4 sw-mt-6 sw-px-4 + className={`sw-box-border sw-flex sw-gap-2 sw-justify-between sw-mt-6 sw-px-4 sw-py-3`} > <Note className="sw-flex sw-flex-1 sw-flex-wrap sw-gap-2 sw-items-center sw-my-1/2"> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx index 2940d29cd44..30efacc1fdd 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx @@ -18,9 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { getCve } from '../../../api/cves'; import { getRuleDetails } from '../../../api/rules'; import { getSecurityHotspotDetails } from '../../../api/security-hotspots'; import { get } from '../../../helpers/storage'; +import { Cve } from '../../../types/cves'; import { Standards } from '../../../types/security'; import { Hotspot, @@ -35,6 +37,7 @@ import HotspotViewerRenderer from './HotspotViewerRenderer'; interface Props { component: Component; + cveId?: string; hotspotKey: string; hotspotsReviewedMeasure?: string; onLocationClick: (index: number) => void; @@ -45,6 +48,7 @@ interface Props { } interface State { + cve?: Cve; hotspot?: Hotspot; lastStatusChangedTo?: HotspotStatusOption; loading: boolean; @@ -83,6 +87,10 @@ export default class HotspotViewer extends React.PureComponent<Props, State> { try { const hotspot = await getSecurityHotspotDetails(this.props.hotspotKey); const ruleDetails = await getRuleDetails({ key: hotspot.rule.key }).then((r) => r.rule); + let cve; + if (typeof this.props.cveId === 'string') { + cve = await getCve(this.props.cveId); + } if (this.mounted) { this.setState({ @@ -90,6 +98,7 @@ export default class HotspotViewer extends React.PureComponent<Props, State> { loading: false, ruleLanguage: ruleDetails.lang, ruleDescriptionSections: ruleDetails.descriptionSections, + cve, }); } } catch (error) { @@ -132,6 +141,7 @@ export default class HotspotViewer extends React.PureComponent<Props, State> { hotspot, ruleDescriptionSections, ruleLanguage, + cve, loading, showStatusUpdateSuccessModal, lastStatusChangedTo, @@ -150,6 +160,7 @@ export default class HotspotViewer extends React.PureComponent<Props, State> { onUpdateHotspot={this.handleHotspotUpdate} ruleDescriptionSections={ruleDescriptionSections} ruleLanguage={ruleLanguage} + cve={cve} selectedHotspotLocation={selectedHotspotLocation} showStatusUpdateSuccessModal={showStatusUpdateSuccessModal} standards={standards} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx index 1ed1a6b62ec..acc3a9e4fdd 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx @@ -26,6 +26,7 @@ import { Component } from '../../../types/types'; import { HotspotHeader } from './HotspotHeader'; import { Spinner } from 'design-system'; +import { Cve } from '../../../types/cves'; import { CurrentUser } from '../../../types/users'; import { RuleDescriptionSection } from '../../coding-rules/rule'; import HotspotReviewHistoryAndComments from './HotspotReviewHistoryAndComments'; @@ -37,6 +38,7 @@ import StatusUpdateSuccessModal from './StatusUpdateSuccessModal'; export interface HotspotViewerRendererProps { component: Component; currentUser: CurrentUser; + cve?: Cve; hotspot?: Hotspot; hotspotsReviewedMeasure?: string; lastStatusChangedTo?: HotspotStatusOption; @@ -62,6 +64,7 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { loading, ruleDescriptionSections, ruleLanguage, + cve, selectedHotspotLocation, showStatusUpdateSuccessModal, standards, @@ -99,8 +102,6 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { onCommentUpdate={props.onUpdateHotspot} /> } - branchLike={branchLike} - component={component} codeTabContent={ <HotspotSnippetContainer branchLike={branchLike} @@ -114,6 +115,7 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { onUpdateHotspot={props.onUpdateHotspot} ruleDescriptionSections={ruleDescriptionSections} ruleLanguage={ruleLanguage} + cve={cve} /> </div> )} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx index 5044e1aba97..a6e43d589fa 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx @@ -34,19 +34,16 @@ import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers'; import { KeyboardKeys } from '../../../helpers/keycodes'; import { translate } from '../../../helpers/l10n'; import { useRefreshBranchStatus } from '../../../queries/branch'; -import { BranchLike } from '../../../types/branch-like'; +import { Cve } from '../../../types/cves'; import { Hotspot, HotspotStatusOption } from '../../../types/security-hotspots'; -import { Component } from '../../../types/types'; import { RuleDescriptionSection, RuleDescriptionSections } from '../../coding-rules/rule'; import useStickyDetection from '../hooks/useStickyDetection'; -import HotspotSnippetHeader from './HotspotSnippetHeader'; import StatusReviewButton from './status/StatusReviewButton'; interface Props { activityTabContent: React.ReactNode; - branchLike?: BranchLike; codeTabContent: React.ReactNode; - component: Component; + cve: Cve | undefined; hotspot: Hotspot; onUpdateHotspot: (statusUpdate?: boolean, statusOption?: HotspotStatusOption) => Promise<void>; ruleDescriptionSections?: RuleDescriptionSection[]; @@ -76,8 +73,7 @@ export default function HotspotViewerTabs(props: Props) { hotspot, ruleDescriptionSections, ruleLanguage, - component, - branchLike, + cve, } = props; const refreshBranchStatus = useRefreshBranchStatus(component.key); @@ -206,9 +202,6 @@ export default function HotspotViewerTabs(props: Props) { /> {isSticky && <StatusReviewButton hotspot={hotspot} onStatusChange={handleStatusChange} />} </div> - {currentTab.value === TabKeys.Code && codeTabContent && ( - <HotspotSnippetHeader hotspot={hotspot} component={component} branchLike={branchLike} /> - )} </StickyTabs> <div aria-labelledby={getTabId(currentTab.value)} @@ -219,7 +212,11 @@ export default function HotspotViewerTabs(props: Props) { {currentTab.value === TabKeys.Code && codeTabContent} {currentTab.value === TabKeys.RiskDescription && rootCauseDescriptionSections && ( - <RuleDescription language={ruleLanguage} sections={rootCauseDescriptionSections} /> + <RuleDescription + language={ruleLanguage} + sections={rootCauseDescriptionSections} + cve={cve} + /> )} {currentTab.value === TabKeys.VulnerabilityDescription && |