Commit 13bb4008 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '217552-evidence-summary-no-longer-appears-on-the-vulnerability-finding' into 'master'

Add evidence and scanner fields to standalone vulnerability page

See merge request gitlab-org/gitlab!34498
parents f2a49133 92bc1bbd
...@@ -9,10 +9,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -9,10 +9,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13561) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13561) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0.
Each security vulnerability in the [Vulnerability List](../dependency_list/index.md) has its own standalone Each security vulnerability in the [Security Dashboard](../security_dashboard/index.md#project-security-dashboard) has its own standalone
page. page.
![Standalone vulnerability page](img/standalone_vulnerability_page_v12_10.png) ![Standalone vulnerability page](img/standalone_vulnerability_page_v13_1.png)
On the standalone vulnerability page, you can interact with the vulnerability in On the standalone vulnerability page, you can interact with the vulnerability in
several different ways: several different ways:
......
<script> <script>
import { GlLink } from '@gitlab/ui'; import { GlLink, GlSprintf } from '@gitlab/ui';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue'; import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import SafeLink from 'ee/vue_shared/components/safe_link.vue';
import DetailItem from './detail_item.vue'; import DetailItem from './detail_item.vue';
export default { export default {
name: 'VulnerabilityDetails', name: 'VulnerabilityDetails',
components: { GlLink, SeverityBadge, DetailItem }, components: { GlLink, SeverityBadge, DetailItem, SafeLink, GlSprintf },
props: { props: {
vulnerability: { vulnerability: {
type: Object, type: Object,
...@@ -20,6 +21,9 @@ export default { ...@@ -20,6 +21,9 @@ export default {
location() { location() {
return this.finding.location || {}; return this.finding.location || {};
}, },
scanner() {
return this.finding.scanner || {};
},
fileText() { fileText() {
return (this.location.file || '') + (this.lineNumber ? `:${this.lineNumber}` : ''); return (this.location.file || '') + (this.lineNumber ? `:${this.lineNumber}` : '');
}, },
...@@ -30,6 +34,9 @@ export default { ...@@ -30,6 +34,9 @@ export default {
const { start_line: start, end_line: end } = this.location; const { start_line: start, end_line: end } = this.location;
return end > start ? `${start}-${end}` : start; return end > start ? `${start}-${end}` : start;
}, },
scannerUrl() {
return this.scanner.url || '';
},
}, },
}; };
</script> </script>
...@@ -44,12 +51,34 @@ export default { ...@@ -44,12 +51,34 @@ export default {
<detail-item :sprintf-message="__('%{labelStart}Severity:%{labelEnd} %{severity}')"> <detail-item :sprintf-message="__('%{labelStart}Severity:%{labelEnd} %{severity}')">
<severity-badge :severity="vulnerability.severity" class="gl-display-inline ml-1" /> <severity-badge :severity="vulnerability.severity" class="gl-display-inline ml-1" />
</detail-item> </detail-item>
<detail-item :sprintf-message="__('%{labelStart}Confidence:%{labelEnd} %{confidence}')" <detail-item
>{{ vulnerability.confidence }} v-if="finding.evidence"
:sprintf-message="__('%{labelStart}Evidence:%{labelEnd} %{evidence}')"
>{{ finding.evidence }}
</detail-item> </detail-item>
<detail-item :sprintf-message="__('%{labelStart}Report Type:%{labelEnd} %{reportType}')" <detail-item :sprintf-message="__('%{labelStart}Report Type:%{labelEnd} %{reportType}')"
>{{ vulnerability.report_type }} >{{ vulnerability.report_type }}
</detail-item> </detail-item>
<detail-item
v-if="scanner.name"
:sprintf-message="__('%{labelStart}Scanner:%{labelEnd} %{scanner}')"
>
<safe-link
:href="scannerUrl"
target="_blank"
rel="noopener noreferrer"
data-testid="scannerSafeLink"
>
<gl-sprintf
v-if="scanner.version"
:message="s__('Vulnerability|%{scannerName} (version %{scannerVersion})')"
>
<template #scannerName>{{ scanner.name }}</template>
<template #scannerVersion>{{ scanner.version }}</template>
</gl-sprintf>
<template v-else>{{ scanner.name }}</template>
</safe-link>
</detail-item>
<detail-item <detail-item
v-if="location.image" v-if="location.image"
:sprintf-message="__('%{labelStart}Image:%{labelEnd} %{image}')" :sprintf-message="__('%{labelStart}Image:%{labelEnd} %{image}')"
......
...@@ -43,7 +43,9 @@ module VulnerabilitiesHelper ...@@ -43,7 +43,9 @@ module VulnerabilitiesHelper
:issue_feedback, :issue_feedback,
:merge_request_feedback, :merge_request_feedback,
:project, :project,
:remediations :remediations,
:evidence,
:scanner
).merge( ).merge(
solution: remediation ? remediation['summary'] : finding[:solution] solution: remediation ? remediation['summary'] : finding[:solution]
) )
......
---
title: Add evidence and scanner fields to standalone vulnerability page
merge_request: 34498
author:
type: changed
...@@ -39,14 +39,15 @@ describe('Vulnerability Details', () => { ...@@ -39,14 +39,15 @@ describe('Vulnerability Details', () => {
expect(getText('title')).toBe(vulnerability.title); expect(getText('title')).toBe(vulnerability.title);
expect(getText('description')).toBe(finding.description); expect(getText('description')).toBe(finding.description);
expect(wrapper.find(SeverityBadge).props('severity')).toBe(vulnerability.severity); expect(wrapper.find(SeverityBadge).props('severity')).toBe(vulnerability.severity);
expect(getText('confidence')).toBe(`Confidence: ${vulnerability.confidence}`);
expect(getText('reportType')).toBe(`Report Type: ${vulnerability.report_type}`); expect(getText('reportType')).toBe(`Report Type: ${vulnerability.report_type}`);
expect(getById('image').exists()).toBeFalsy(); expect(getById('image').exists()).toBe(false);
expect(getById('os').exists()).toBeFalsy(); expect(getById('os').exists()).toBe(false);
expect(getById('file').exists()).toBeFalsy(); expect(getById('file').exists()).toBe(false);
expect(getById('class').exists()).toBeFalsy(); expect(getById('class').exists()).toBe(false);
expect(getById('method').exists()).toBeFalsy(); expect(getById('method').exists()).toBe(false);
expect(getById('evidence').exists()).toBe(false);
expect(getById('scanner').exists()).toBe(false);
expect(getAllById('link')).toHaveLength(0); expect(getAllById('link')).toHaveLength(0);
expect(getAllById('identifier')).toHaveLength(0); expect(getAllById('identifier')).toHaveLength(0);
}); });
...@@ -71,6 +72,11 @@ describe('Vulnerability Details', () => { ...@@ -71,6 +72,11 @@ describe('Vulnerability Details', () => {
expect(getText('method')).toBe(`Method: method name`); expect(getText('method')).toBe(`Method: method name`);
}); });
it('shows the evidence if it exists', () => {
createWrapper({ evidence: 'some evidence' });
expect(getText('evidence')).toBe(`Evidence: some evidence`);
});
it('shows the links if they exist', () => { it('shows the links if they exist', () => {
createWrapper({ links: [{ url: '0' }, { url: '1' }, { url: '2' }] }); createWrapper({ links: [{ url: '0' }, { url: '1' }, { url: '2' }] });
const links = getAllById('link'); const links = getAllById('link');
...@@ -138,4 +144,33 @@ describe('Vulnerability Details', () => { ...@@ -138,4 +144,33 @@ describe('Vulnerability Details', () => {
expect(file().text()).toBe('test.txt:24'); expect(file().text()).toBe('test.txt:24');
}); });
}); });
describe('scanner', () => {
const link = () => getById('scannerSafeLink');
const scannerText = () => getById('scanner').text();
it('shows the scanner name only but no link', () => {
createWrapper({ scanner: { name: 'some scanner' } });
expect(scannerText()).toBe('Scanner: some scanner');
expect(link().vm.$el instanceof HTMLSpanElement).toBe(true);
});
it('shows the scanner name and version but no link', () => {
createWrapper({ scanner: { name: 'some scanner', version: '1.2.3' } });
expect(scannerText()).toBe('Scanner: some scanner (version 1.2.3)');
expect(link().vm.$el instanceof HTMLSpanElement).toBe(true);
});
it('shows the scanner name only with a link', () => {
createWrapper({ scanner: { name: 'some scanner', url: '//link' } });
expect(scannerText()).toBe('Scanner: some scanner');
expect(link().props('href')).toBe('//link');
});
it('shows the scanner name and version with a link', () => {
createWrapper({ scanner: { name: 'some scanner', version: '1.2.3', url: '//link' } });
expect(scannerText()).toBe('Scanner: some scanner (version 1.2.3)');
expect(link().props('href')).toBe('//link');
});
});
}); });
...@@ -116,7 +116,9 @@ RSpec.describe VulnerabilitiesHelper do ...@@ -116,7 +116,9 @@ RSpec.describe VulnerabilitiesHelper do
name: finding.name, name: finding.name,
project: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder), project: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
remediations: finding.remediations, remediations: finding.remediations,
solution: kind_of(String) solution: kind_of(String),
evidence: kind_of(String),
scanner: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder)
) )
expect(subject[:location]['blob_path']).to match(kind_of(String)) expect(subject[:location]['blob_path']).to match(kind_of(String))
......
...@@ -395,7 +395,7 @@ msgstr "" ...@@ -395,7 +395,7 @@ msgstr ""
msgid "%{labelStart}Class:%{labelEnd} %{class}" msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "" msgstr ""
msgid "%{labelStart}Confidence:%{labelEnd} %{confidence}" msgid "%{labelStart}Evidence:%{labelEnd} %{evidence}"
msgstr "" msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}" msgid "%{labelStart}File:%{labelEnd} %{file}"
...@@ -413,6 +413,9 @@ msgstr "" ...@@ -413,6 +413,9 @@ msgstr ""
msgid "%{labelStart}Report Type:%{labelEnd} %{reportType}" msgid "%{labelStart}Report Type:%{labelEnd} %{reportType}"
msgstr "" msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
msgid "%{labelStart}Severity:%{labelEnd} %{severity}" msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment