diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js index 4959550e27390511037ae54163cfc403daa6c982..a01c6df00039720aec363b18a67657a2e1f4e83e 100644 --- a/app/assets/javascripts/lib/dompurify.js +++ b/app/assets/javascripts/lib/dompurify.js @@ -8,6 +8,7 @@ const defaultConfig = { // See https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1421 FORBID_ATTR: ['data-remote', 'data-url', 'data-type', 'data-method'], FORBID_TAGS: ['style', 'mstyle'], + ALLOW_UNKNOWN_PROTOCOLS: true, }; // Only icons urls from `gon` are allowed diff --git a/spec/frontend/lib/dompurify_spec.js b/spec/frontend/lib/dompurify_spec.js index 34325dad6a168b864e4390b0eac705d79ea24655..b585c69e9118d2833fc738990fe17c4a8e52bbce 100644 --- a/spec/frontend/lib/dompurify_spec.js +++ b/spec/frontend/lib/dompurify_spec.js @@ -34,6 +34,17 @@ const unsafeUrls = [ `${absoluteGon.sprite_file_icons}/../../https://evil.url`, ]; +/* 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']; + const forbiddenDataAttrs = ['data-remote', 'data-url', 'data-type', 'data-method']; const acceptedDataAttrs = ['data-random', 'data-custom']; @@ -150,4 +161,16 @@ describe('~/lib/dompurify', () => { expect(sanitize(htmlHref)).toBe(`<a ${attrWithValue}>hello</a>`); }); }); + + describe('with non-http links', () => { + it.each(validProtocolUrls)('should allow %s', (url) => { + const html = `<a href="${url}">internal link</a>`; + expect(sanitize(html)).toBe(`<a href="${url}">internal link</a>`); + }); + + it.each(invalidProtocolUrls)('should not allow %s', (url) => { + const html = `<a href="${url}">internal link</a>`; + expect(sanitize(html)).toBe(`<a>internal link</a>`); + }); + }); });