Commit b9712539 authored by Mark Florian's avatar Mark Florian

Duplicate vulnerability-details component

This duplicates the existing `vulnerability-details` component, and
replaces its `details` prop  with a `vulnerability` prop.

To achieve this, the data transformations which turned a `vulnerability`
object into a `details` object have been naively copied into this new
component.

The main data transformation is copied mostly verbatim from the
[SET_MODAL_DATA mutation][1] in the Security Dashboard store, rather
than from [SET_ISSUE_MODAL_DATA][2] mutation in the security reports
store in the Merge Request page. This is because the former is slightly
more complete than the latter:

- it includes the project the vulnerability belongs to
- it includes the report type of the vulnerability
- it includes line number information for the `file`, if it exists

The latter, however, includes the method in which the vulnerability was
detected, if it exists.

All this is to say that in addition to rendering everything the old
component did, the new component:

- in the MR context:
  - includes the project the vulnerability belongs to (if available)
  - includes the report type of the vulnerability (if available)
  - includes line number information for the `file` (if available)
- in the Security Dashboard context:
  - includes the method in which the vulnerability was detected (if
    available)

The [secondary transformation `valuedFields`][3] is copied almost
verbatim from the containing modal.

[1]: https://gitlab.com/gitlab-org/gitlab/blob/v12.8.1-ee/ee/app/assets/javascripts/security_dashboard/store/modules/vulnerabilities/mutations.js#L76-158
[2]: https://gitlab.com/gitlab-org/gitlab/blob/v12.8.1-ee/ee/app/assets/javascripts/vue_shared/security_reports/store/mutations.js#L135-172
[3]: https://gitlab.com/gitlab-org/gitlab/blob/v12.8.1-ee/ee/app/assets/javascripts/vue_shared/security_reports/components/modal.vue#L123-138
parent 29693e43
<script>
import { GlFriendlyWrap } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import SafeLink from 'ee/vue_shared/components/safe_link.vue';
import Icon from '~/vue_shared/components/icon.vue';
import SeverityBadge from './severity_badge.vue';
import getFileLocation from '../store/utils/get_file_location';
export default {
name: 'VulnerabilityDetails2',
components: {
GlFriendlyWrap,
Icon,
SafeLink,
SeverityBadge,
},
props: {
vulnerability: {
type: Object,
required: true,
},
},
methods: {
hasMoreValues(index, values) {
return index < values.length - 1;
},
hasValue(field) {
return field.value && field.value.length > 0;
},
hasInstances(key) {
return key === 'instances';
},
hasIdentifiers(key) {
return key === 'identifiers';
},
hasLinks(key) {
return key === 'links';
},
hasSeverity(key) {
return key === 'severity';
},
valuedFields(details) {
const result = {};
Object.keys(details).forEach(key => {
if (details[key].value && details[key].value.length) {
result[key] = details[key];
if (key === 'file' && this.vulnerability.blob_path) {
result[key].isLink = true;
result[key].url = this.vulnerability.blob_path;
}
}
});
return result;
},
},
computed: {
details() {
const { vulnerability } = this;
const { location } = vulnerability;
const details = {
description: {
text: s__('Vulnerability|Description'),
value: vulnerability.description,
},
project: {
text: s__('Vulnerability|Project'),
isLink: true,
value: vulnerability.project?.full_name,
url: vulnerability.project?.full_path,
},
url: { text: __('URL'), isLink: true },
file: { text: s__('Vulnerability|File') },
identifiers: {
text: s__('Vulnerability|Identifiers'),
value: vulnerability.identifiers.length && vulnerability.identifiers,
},
severity: {
text: s__('Vulnerability|Severity'),
value: vulnerability.severity,
},
reportType: {
text: s__('Vulnerability|Report Type'),
value: vulnerability.report_type,
},
className: { text: s__('Vulnerability|Class') },
methodName: { text: s__('Vulnerability|Method') },
image: { text: s__('Vulnerability|Image') },
namespace: { text: s__('Vulnerability|Namespace') },
links: { text: s__('Vulnerability|Links') },
instances: { text: s__('Vulnerability|Instances') },
};
if (location) {
const {
file,
start_line: startLine,
end_line: endLine,
image,
operating_system: namespace,
class: className,
method: methodName,
} = location;
const fileLocation = getFileLocation(location);
let lineSuffix = '';
if (startLine) {
lineSuffix += `:${startLine}`;
if (endLine && startLine !== endLine) {
lineSuffix += `-${endLine}`;
}
}
details.className.value = className;
details.methodName.value = methodName;
details.file.value = file ? `${file}${lineSuffix}` : null;
details.image.value = image;
details.namespace.value = namespace;
details.url.value = fileLocation;
details.url.url = fileLocation;
}
if (vulnerability.instances && vulnerability.instances.length) {
details.instances.value = vulnerability.instances;
} else {
details.instances.value = null;
}
if (vulnerability.links && vulnerability.links.length) {
details.links.value = vulnerability.links;
} else {
details.links.value = null;
}
return this.valuedFields(details);
},
},
};
</script>
<template>
<div class="border-white mb-0 px-3">
<div v-for="(field, key, index) in details" :key="index" class="d-sm-flex my-sm-2 my-4">
<label class="col-sm-2 text-sm-right font-weight-bold pl-0">{{ field.text }}:</label>
<div class="col-sm-10 pl-0 text-secondary">
<template v-if="hasValue(field)">
<div v-if="hasInstances(key)" class="info-well">
<ul class="report-block-list">
<li v-for="(instance, i) in field.value" :key="i" class="report-block-list-issue">
<div class="report-block-list-icon append-right-5 failed">
<icon :size="32" name="status_failed_borderless" />
</div>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text">
{{ instance.method }}
</div>
<div class="report-block-list-issue-description-link">
<safe-link
:href="instance.uri"
target="_blank"
rel="noopener noreferrer nofollow"
class="break-link"
>
{{ instance.uri }}
</safe-link>
</div>
<expand-button v-if="instance.evidence">
<pre
slot="expanded"
class="block report-block-dast-code prepend-top-10 report-block-issue-code"
>
{{ instance.evidence }}</pre
>
</expand-button>
</div>
</li>
</ul>
</div>
<template v-else-if="hasIdentifiers(key)">
<span v-for="(identifier, i) in field.value" :key="i">
<safe-link
v-if="identifier.url"
:class="`js-link-${key}`"
:href="identifier.url"
target="_blank"
rel="noopener noreferrer"
>
{{ identifier.name }}
</safe-link>
<span v-else> {{ identifier.name }} </span>
<span v-if="hasMoreValues(i, field.value)">,&nbsp;</span>
</span>
</template>
<template v-else-if="hasLinks(key)">
<span v-for="(link, i) in field.value" :key="i">
<safe-link
:class="`js-link-${key}`"
:href="link.url"
target="_blank"
rel="noopener noreferrer"
>
{{ link.value || link.url }}
</safe-link>
<span v-if="hasMoreValues(i, field.value)">,&nbsp;</span>
</span>
</template>
<template v-else-if="hasSeverity(key)">
<severity-badge :severity="field.value" class="d-inline" />
</template>
<template v-else>
<safe-link
v-if="field.isLink"
:class="`js-link-${key}`"
:href="field.url"
target="_blank"
>
<gl-friendly-wrap :text="field.value" />
</safe-link>
<gl-friendly-wrap v-else :text="field.value" />
</template>
</template>
</div>
</div>
</div>
</template>
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