Commit d619f468 authored by Phil Hughes's avatar Phil Hughes

Merge branch '10450-fix-file-path-overflow' into 'master'

Wrap file paths in vulnerability details modal

Closes #10450

See merge request gitlab-org/gitlab-ee!10606
parents a5aec1d0 f58ce22f
<script> <script>
import { GlFriendlyWrap } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import SafeLink from 'ee/vue_shared/components/safe_link.vue'; import SafeLink from 'ee/vue_shared/components/safe_link.vue';
import SeverityBadge from './severity_badge.vue'; import SeverityBadge from './severity_badge.vue';
...@@ -6,6 +7,7 @@ import SeverityBadge from './severity_badge.vue'; ...@@ -6,6 +7,7 @@ import SeverityBadge from './severity_badge.vue';
export default { export default {
name: 'VulnerabilityDetails', name: 'VulnerabilityDetails',
components: { components: {
GlFriendlyWrap,
Icon, Icon,
SafeLink, SafeLink,
SeverityBadge, SeverityBadge,
...@@ -23,17 +25,17 @@ export default { ...@@ -23,17 +25,17 @@ export default {
hasValue(field) { hasValue(field) {
return field.value && field.value.length > 0; return field.value && field.value.length > 0;
}, },
hasInstances(field, key) { hasInstances(key) {
return key === 'instances' && this.hasValue(field); return key === 'instances';
}, },
hasIdentifiers(field, key) { hasIdentifiers(key) {
return key === 'identifiers' && this.hasValue(field); return key === 'identifiers';
}, },
hasLinks(field, key) { hasLinks(key) {
return key === 'links' && this.hasValue(field); return key === 'links';
}, },
hasSeverity(field, key) { hasSeverity(key) {
return key === 'severity' && this.hasValue(field); return key === 'severity';
}, },
}, },
}; };
...@@ -44,7 +46,8 @@ export default { ...@@ -44,7 +46,8 @@ export default {
<div v-for="(field, key, index) in details" :key="index" class="d-flex my-2"> <div v-for="(field, key, index) in details" :key="index" class="d-flex my-2">
<label class="col-2 text-right font-weight-bold pl-0">{{ field.text }}:</label> <label class="col-2 text-right font-weight-bold pl-0">{{ field.text }}:</label>
<div class="col-10 pl-0 text-secondary"> <div class="col-10 pl-0 text-secondary">
<div v-if="hasInstances(field, key)" class="info-well"> <template v-if="hasValue(field)">
<div v-if="hasInstances(key)" class="info-well">
<ul class="report-block-list"> <ul class="report-block-list">
<li v-for="(instance, i) in field.value" :key="i" class="report-block-list-issue"> <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"> <div class="report-block-list-icon append-right-5 failed">
...@@ -76,7 +79,7 @@ export default { ...@@ -76,7 +79,7 @@ export default {
</li> </li>
</ul> </ul>
</div> </div>
<template v-else-if="hasIdentifiers(field, key)"> <template v-else-if="hasIdentifiers(key)">
<span v-for="(identifier, i) in field.value" :key="i"> <span v-for="(identifier, i) in field.value" :key="i">
<safe-link <safe-link
v-if="identifier.url" v-if="identifier.url"
...@@ -91,7 +94,7 @@ export default { ...@@ -91,7 +94,7 @@ export default {
<span v-if="hasMoreValues(i, field.value)">,&nbsp;</span> <span v-if="hasMoreValues(i, field.value)">,&nbsp;</span>
</span> </span>
</template> </template>
<template v-else-if="hasLinks(field, key)"> <template v-else-if="hasLinks(key)">
<span v-for="(link, i) in field.value" :key="i"> <span v-for="(link, i) in field.value" :key="i">
<safe-link <safe-link
:class="`js-link-${key}`" :class="`js-link-${key}`"
...@@ -104,7 +107,7 @@ export default { ...@@ -104,7 +107,7 @@ export default {
<span v-if="hasMoreValues(i, field.value)">,&nbsp;</span> <span v-if="hasMoreValues(i, field.value)">,&nbsp;</span>
</span> </span>
</template> </template>
<template v-else-if="hasSeverity(field, key)"> <template v-else-if="hasSeverity(key)">
<severity-badge :severity="field.value" class="d-inline" /> <severity-badge :severity="field.value" class="d-inline" />
</template> </template>
<template v-else> <template v-else>
...@@ -114,11 +117,14 @@ export default { ...@@ -114,11 +117,14 @@ export default {
:href="field.url" :href="field.url"
target="_blank" target="_blank"
> >
{{ field.value }} <gl-friendly-wrap :text="field.value" />
</safe-link> </safe-link>
<span v-else :class="{ 'text-capitalize': key === 'confidence' }"> <gl-friendly-wrap
{{ field.value }} v-else
</span> :text="field.value"
:class="{ 'text-capitalize': key === 'confidence' }"
/>
</template>
</template> </template>
</div> </div>
</div> </div>
......
---
title: Prevent files paths from overflowing in vulnerability info modal
merge_request: 10606
author:
type: fixed
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
import createState from 'ee/vue_shared/security_reports/store/state'; import createState from 'ee/vue_shared/security_reports/store/state';
import VulnerabilityDetails from 'ee/vue_shared/security_reports/components/vulnerability_details.vue'; import VulnerabilityDetails from 'ee/vue_shared/security_reports/components/vulnerability_details.vue';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue'; import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
...@@ -10,7 +10,7 @@ describe('VulnerabilityDetails component', () => { ...@@ -10,7 +10,7 @@ describe('VulnerabilityDetails component', () => {
const localVue = createLocalVue(); const localVue = createLocalVue();
const componentFactory = (options = {}) => { const componentFactory = (options = {}) => {
wrapper = shallowMount(localVue.extend(VulnerabilityDetails), { wrapper = mount(localVue.extend(VulnerabilityDetails), {
...options, ...options,
localVue, localVue,
sync: false, sync: false,
...@@ -48,6 +48,34 @@ describe('VulnerabilityDetails component', () => { ...@@ -48,6 +48,34 @@ describe('VulnerabilityDetails component', () => {
}); });
}); });
it('renders wrapped file paths', () => {
const value = '/some/file/path';
const valueWrapped = '/<wbr>some/<wbr>file/<wbr>path';
const details = {
file: {
value,
url: `${TEST_HOST}/bar`,
isLink: true,
},
};
componentFactory({ propsData: { details } });
expect(wrapper.find(SafeLink).html()).toMatch(valueWrapped);
});
it('escapes wrapped file paths', () => {
const value = '/unsafe/path<script></script>';
const valueWrapped = '/<wbr>unsafe/<wbr>path&lt;script&gt;&lt;/<wbr>script&gt;';
const details = {
file: {
value,
url: `${TEST_HOST}/bar`,
isLink: true,
},
};
componentFactory({ propsData: { details } });
expect(wrapper.find(SafeLink).html()).toMatch(valueWrapped);
});
describe('does not render XSS links', () => { describe('does not render XSS links', () => {
// eslint-disable-next-line no-script-url // eslint-disable-next-line no-script-url
const badUrl = 'javascript:alert("")'; const badUrl = 'javascript:alert("")';
......
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