Commit 3e4abff5 authored by Kev's avatar Kev Committed by Olena Horal-Koretska

Replace v-html with v-safe-html in dismissal_note.vue

parent e52642f4
<script> <script>
/* eslint-disable vue/no-v-html */
import { escape } from 'lodash';
import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue'; import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue';
import { GlButton } from '@gitlab/ui'; import { GlButton, GlSprintf, GlLink } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __ } from '~/locale';
export default { export default {
components: { components: {
EventItem, EventItem,
GlButton, GlButton,
GlSprintf,
GlLink,
}, },
props: { props: {
feedback: { feedback: {
...@@ -42,33 +42,23 @@ export default { ...@@ -42,33 +42,23 @@ export default {
}, },
}, },
computed: { computed: {
pipeline() {
return this.feedback?.pipeline;
},
eventText() { eventText() {
const { project, feedback } = this; const { project, pipeline } = this;
const { pipeline } = feedback;
const pipelineLink =
pipeline && pipeline.path && pipeline.id
? `<a href="${pipeline.path}">#${pipeline.id}</a>`
: null;
const projectLink = const hasPipeline = Boolean(pipeline?.path && pipeline?.id);
project && project.url && project.value const hasProject = Boolean(project?.url && project?.value);
? `<a href="${escape(project.url)}">${escape(project.value)}</a>`
: null;
if (pipelineLink && projectLink) { if (hasPipeline && hasProject) {
return sprintf( return __('Dismissed on pipeline %{pipelineLink} at %{projectLink}');
__('Dismissed on pipeline %{pipelineLink} at %{projectLink}'), } else if (hasPipeline) {
{ pipelineLink, projectLink }, return __('Dismissed on pipeline %{pipelineLink}');
false, } else if (hasProject) {
); return __('Dismissed at %{projectLink}');
}
if (pipelineLink && !projectLink) {
return sprintf(__('Dismissed on pipeline %{pipelineLink}'), { pipelineLink }, false);
}
if (!pipelineLink && projectLink) {
return sprintf(__('Dismissed at %{projectLink}'), { projectLink }, false);
} }
return __('Dismissed'); return __('Dismissed');
}, },
commentDetails() { commentDetails() {
...@@ -100,7 +90,16 @@ export default { ...@@ -100,7 +90,16 @@ export default {
icon-name="cancel" icon-name="cancel"
icon-class="ci-status-icon-pending" icon-class="ci-status-icon-pending"
> >
<div v-if="feedback.created_at" v-html="eventText"></div> <div v-if="feedback.created_at">
<gl-sprintf :message="eventText">
<template v-if="pipeline" #pipelineLink>
<gl-link :href="pipeline.path">#{{ pipeline.id }}</gl-link>
</template>
<template v-if="project" #projectLink>
<gl-link :href="project.value">{{ project.value }}</gl-link>
</template>
</gl-sprintf>
</div>
</event-item> </event-item>
<template v-if="commentDetails && !isCommentingOnDismissal"> <template v-if="commentDetails && !isCommentingOnDismissal">
<hr class="my-3" /> <hr class="my-3" />
......
---
title: Replace v-html with v-safe-html in dismissal_note.vue
merge_request: 41137
author: Kev @KevSlashNull
type: other
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { GlSprintf } from '@gitlab/ui';
import component from 'ee/vue_shared/security_reports/components/dismissal_note.vue'; import component from 'ee/vue_shared/security_reports/components/dismissal_note.vue';
import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue'; import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue';
...@@ -22,7 +23,7 @@ describe('dismissal note', () => { ...@@ -22,7 +23,7 @@ describe('dismissal note', () => {
let wrapper; let wrapper;
const mountComponent = (options, mountFn = shallowMount) => { const mountComponent = (options, mountFn = shallowMount) => {
wrapper = mountFn(component, options); wrapper = mountFn(component, { ...options, stubs: { GlSprintf } });
}; };
describe('with no attached project or pipeline', () => { describe('with no attached project or pipeline', () => {
...@@ -97,13 +98,8 @@ describe('dismissal note', () => { ...@@ -97,13 +98,8 @@ describe('dismissal note', () => {
}); });
it('should escape the project name', () => { it('should escape the project name', () => {
// Note: We have to check the computed prop here because // wrapper.text() is the text string, if the tag was parsed then it would be missing <script> from the string.
// vue test utils unescapes the result of wrapper.text() expect(wrapper.text()).toContain(unsafeProject.value);
expect(wrapper.vm.eventText).not.toContain(project.value);
expect(wrapper.vm.eventText).toContain(
'Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;',
);
}); });
}); });
......
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