Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_shared/directives/safe_html_spec.js')
-rw-r--r--spec/frontend/vue_shared/directives/safe_html_spec.js116
1 files changed, 116 insertions, 0 deletions
diff --git a/spec/frontend/vue_shared/directives/safe_html_spec.js b/spec/frontend/vue_shared/directives/safe_html_spec.js
new file mode 100644
index 00000000000..ba1de8e4596
--- /dev/null
+++ b/spec/frontend/vue_shared/directives/safe_html_spec.js
@@ -0,0 +1,116 @@
+import { shallowMount } from '@vue/test-utils';
+import safeHtml from '~/vue_shared/directives/safe_html';
+import { defaultConfig } from '~/lib/dompurify';
+/* eslint-disable no-script-url */
+const invalidProtocolUrls = [
+ 'javascript:alert(1)',
+ 'jAvascript:alert(1)',
+ 'data:text/html,<script>alert(1);</script>',
+ ' javascript:',
+ 'javascript :',
+];
+/* eslint-enable no-script-url */
+const validProtocolUrls = ['slack://open', 'x-devonthink-item://90909', 'x-devonthink-item:90909'];
+
+describe('safe html directive', () => {
+ let wrapper;
+
+ const createComponent = ({ template, html, config } = {}) => {
+ const defaultTemplate = `<div v-safe-html="rawHtml"></div>`;
+ const defaultHtml = 'hello <script>alert(1)</script>world';
+
+ const component = {
+ directives: {
+ safeHtml,
+ },
+ data() {
+ return {
+ rawHtml: html || defaultHtml,
+ config: config || {},
+ };
+ },
+ template: template || defaultTemplate,
+ };
+
+ wrapper = shallowMount(component);
+ };
+
+ describe('default', () => {
+ it('should remove the script tag', () => {
+ createComponent();
+
+ expect(wrapper.html()).toEqual('<div>hello world</div>');
+ });
+
+ it('should remove javascript hrefs', () => {
+ createComponent({ html: '<a href="javascript:prompt(1)">click here</a>' });
+
+ expect(wrapper.html()).toEqual('<div><a>click here</a></div>');
+ });
+
+ it('should remove any existing children', () => {
+ createComponent({
+ template: `<div v-safe-html="rawHtml">foo <i>bar</i></div>`,
+ });
+
+ expect(wrapper.html()).toEqual('<div>hello world</div>');
+ });
+
+ describe('with non-http links', () => {
+ it.each(validProtocolUrls)('should allow %s', (url) => {
+ createComponent({
+ html: `<a href="${url}">internal link</a>`,
+ });
+ expect(wrapper.html()).toContain(`<a href="${url}">internal link</a>`);
+ });
+
+ it.each(invalidProtocolUrls)('should not allow %s', (url) => {
+ createComponent({
+ html: `<a href="${url}">internal link</a>`,
+ });
+ expect(wrapper.html()).toContain(`<a>internal link</a>`);
+ });
+ });
+
+ describe('handles data attributes correctly', () => {
+ const allowedDataAttrs = ['data-safe', 'data-random'];
+
+ it.each(defaultConfig.FORBID_ATTR)('removes dangerous `%s` attribute', (attr) => {
+ const html = `<a ${attr}="true"></a>`;
+ createComponent({ html });
+
+ expect(wrapper.html()).not.toContain(html);
+ });
+
+ it.each(allowedDataAttrs)('does not remove allowed `%s` attribute', (attr) => {
+ const html = `<a ${attr}="true"></a>`;
+ createComponent({ html });
+
+ expect(wrapper.html()).toContain(html);
+ });
+ });
+ });
+
+ describe('advance config', () => {
+ const template = '<div v-safe-html:[config]="rawHtml"></div>';
+ it('should only allow <b> tags', () => {
+ createComponent({
+ template,
+ html: '<a href="javascript:prompt(1)"><b>click here</b></a>',
+ config: { ALLOWED_TAGS: ['b'] },
+ });
+
+ expect(wrapper.html()).toEqual('<div><b>click here</b></div>');
+ });
+
+ it('should strip all html tags', () => {
+ createComponent({
+ template,
+ html: '<a href="javascript:prompt(1)"><u>click here</u></a>',
+ config: { ALLOWED_TAGS: [] },
+ });
+
+ expect(wrapper.html()).toEqual('<div>click here</div>');
+ });
+ });
+});