Commit 92bc1bbd authored by Daniel Tian's avatar Daniel Tian

Add evidence and scanner fields to vuln details

Add evidence and scanner fields to vulnerability details on standalone
vulnerability page
parent 33ddf71a
......@@ -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.
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.
![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
several different ways:
......
<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 SafeLink from 'ee/vue_shared/components/safe_link.vue';
import DetailItem from './detail_item.vue';
export default {
name: 'VulnerabilityDetails',
components: { GlLink, SeverityBadge, DetailItem },
components: { GlLink, SeverityBadge, DetailItem, SafeLink, GlSprintf },
props: {
vulnerability: {
type: Object,
......@@ -20,6 +21,9 @@ export default {
location() {
return this.finding.location || {};
},
scanner() {
return this.finding.scanner || {};
},
fileText() {
return (this.location.file || '') + (this.lineNumber ? `:${this.lineNumber}` : '');
},
......@@ -30,6 +34,9 @@ export default {
const { start_line: start, end_line: end } = this.location;
return end > start ? `${start}-${end}` : start;
},
scannerUrl() {
return this.scanner.url || '';
},
},
};
</script>
......@@ -44,12 +51,34 @@ export default {
<detail-item :sprintf-message="__('%{labelStart}Severity:%{labelEnd} %{severity}')">
<severity-badge :severity="vulnerability.severity" class="gl-display-inline ml-1" />
</detail-item>
<detail-item :sprintf-message="__('%{labelStart}Confidence:%{labelEnd} %{confidence}')"
>{{ vulnerability.confidence }}
<detail-item
v-if="finding.evidence"
:sprintf-message="__('%{labelStart}Evidence:%{labelEnd} %{evidence}')"
>{{ finding.evidence }}
</detail-item>
<detail-item :sprintf-message="__('%{labelStart}Report Type:%{labelEnd} %{reportType}')"
>{{ vulnerability.report_type }}
</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
v-if="location.image"
:sprintf-message="__('%{labelStart}Image:%{labelEnd} %{image}')"
......
......@@ -43,7 +43,9 @@ module VulnerabilitiesHelper
:issue_feedback,
:merge_request_feedback,
:project,
:remediations
:remediations,
:evidence,
:scanner
).merge(
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', () => {
expect(getText('title')).toBe(vulnerability.title);
expect(getText('description')).toBe(finding.description);
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(getById('image').exists()).toBeFalsy();
expect(getById('os').exists()).toBeFalsy();
expect(getById('file').exists()).toBeFalsy();
expect(getById('class').exists()).toBeFalsy();
expect(getById('method').exists()).toBeFalsy();
expect(getById('image').exists()).toBe(false);
expect(getById('os').exists()).toBe(false);
expect(getById('file').exists()).toBe(false);
expect(getById('class').exists()).toBe(false);
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('identifier')).toHaveLength(0);
});
......@@ -71,6 +72,11 @@ describe('Vulnerability Details', () => {
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', () => {
createWrapper({ links: [{ url: '0' }, { url: '1' }, { url: '2' }] });
const links = getAllById('link');
......@@ -138,4 +144,33 @@ describe('Vulnerability Details', () => {
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
name: finding.name,
project: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
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))
......
......@@ -386,7 +386,7 @@ msgstr ""
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
msgid "%{labelStart}Confidence:%{labelEnd} %{confidence}"
msgid "%{labelStart}Evidence:%{labelEnd} %{evidence}"
msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
......@@ -404,6 +404,9 @@ msgstr ""
msgid "%{labelStart}Report Type:%{labelEnd} %{reportType}"
msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
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