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' + ); + }); }); }); 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'); + } + }); +}