From 33e301cccca0ce2fd6811dbbd07e083e22c592ee Mon Sep 17 00:00:00 2001 From: Steve Hobbs Date: Thu, 10 Jun 2021 13:28:38 +0100 Subject: [PATCH 1/2] Implement a DOMPurify hook to enable target='_blank' --- src/core.js | 5 +++++ src/sanitizer.js | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/sanitizer.js diff --git a/src/core.js b/src/core.js index 86d8bfb6d..4265694fd 100644 --- a/src/core.js +++ b/src/core.js @@ -2,6 +2,8 @@ import { EventEmitter } from 'events'; import { getEntity, observe, read } from './store/index'; import { remove, render } from './ui/box'; import webAPI from './core/web_api'; +import { initSanitizer } from './sanitizer'; + import { closeLock, resumeAuth, @@ -62,10 +64,13 @@ export default class Base extends EventEmitter { this.id = idu.incremental(); this.engine = engine; + const hookRunner = ::this.runHook; const emitEventFn = this.emit.bind(this); const handleEventFn = this.on.bind(this); + go(this.id); + initSanitizer(); let m = setupLock(this.id, clientID, domain, options, hookRunner, emitEventFn, handleEventFn); diff --git a/src/sanitizer.js b/src/sanitizer.js new file mode 100644 index 000000000..efb087254 --- /dev/null +++ b/src/sanitizer.js @@ -0,0 +1,22 @@ +import { addHook } from 'dompurify'; + +export function initSanitizer() { + // Extracted from the example at + // https://github.com/cure53/DOMPurify/blob/main/demos/hooks-target-blank-demo.html + addHook('afterSanitizeAttributes', function(node) { + // set all elements owning target to target=_blank + if ('target' in node) { + node.setAttribute('target', '_blank'); + // prevent https://www.owasp.org/index.php/Reverse_Tabnabbing + node.setAttribute('rel', 'noopener noreferrer'); + } + + // set non-HTML/MathML links to xlink:show=new + if ( + !node.hasAttribute('target') && + (node.hasAttribute('xlink:href') || node.hasAttribute('href')) + ) { + node.setAttribute('xlink:show', 'new'); + } + }); +} From df37d80f3d1081c026330761a4e23d4d5ad18bc8 Mon Sep 17 00:00:00 2001 From: Steve Hobbs Date: Thu, 10 Jun 2021 13:55:21 +0100 Subject: [PATCH 2/2] Add test for target attribute sanitization --- src/__tests__/i18n.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/__tests__/i18n.test.js b/src/__tests__/i18n.test.js index bfe299507..b5854f4ae 100644 --- a/src/__tests__/i18n.test.js +++ b/src/__tests__/i18n.test.js @@ -6,6 +6,7 @@ import esDictionary from '../i18n/es'; import * as sync from '../sync'; import * as l from '../core/index'; +import { initSanitizer } from '../sanitizer'; describe('i18n', () => { let syncSpy; @@ -61,5 +62,22 @@ describe('i18n', () => { expect(html.props.dangerouslySetInnerHTML.__html).not.toMatch(/javascript:alert/); expect(html.props.dangerouslySetInnerHTML.__html).toEqual(''); }); + + it('should allow target=_blank with noopener noreferrer attributes', () => { + initSanitizer(); + + const i18n = require('../i18n'); + + const strings = { + test: 'link' + }; + + const m = Immutable.fromJS({ i18n: { strings } }); + const html = i18n.html(m, 'test'); + + expect(html.props.dangerouslySetInnerHTML.__html).toEqual( + 'link' + ); + }); }); });