create internal trustedTypes policy only if not specified via config object

This commit is contained in:
dejang
2023-05-02 23:43:47 -04:00
parent 8d4a26772a
commit 70e05d7e12
10 changed files with 268 additions and 179 deletions
+19 -9
View File
@@ -202,13 +202,13 @@ const getGlobal = () => typeof window === 'undefined' ? null : window;
* Creates a no-op policy for internal use only.
* Don't export this function outside this module!
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
* @param {Document} document The document object (to determine policy name suffix)
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
* are not supported).
* are not supported or creating the policy failed).
*/
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
return null;
} // Allow the callers to control the unique policy name
@@ -219,8 +219,8 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
let suffix = null;
const ATTR_NAME = 'data-tt-policy-suffix';
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
suffix = document.currentScript.getAttribute(ATTR_NAME);
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
suffix = purifyHostElement.getAttribute(ATTR_NAME);
}
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
@@ -271,6 +271,7 @@ function createDOMPurify() {
}
const originalDocument = window.document;
const currentScript = originalDocument.currentScript;
let {
document
} = window;
@@ -304,9 +305,8 @@ function createDOMPurify() {
}
}
let trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
let emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
let trustedTypesPolicy;
let emptyHTML = '';
const {
implementation,
createNodeIterator,
@@ -680,9 +680,19 @@ function createDOMPurify() {
} // Overwrite existing TrustedTypes policy.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables in case using the internal policy did not succeed.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables required by `sanitize`.
emptyHTML = trustedTypesPolicy.createHTML('');
} else {
// Uninitialized policy, attempt to initialize the internal dompurify policy.
if (trustedTypesPolicy === undefined) {
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
} // If creating the internal policy succeeded sign internal variables.
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
emptyHTML = trustedTypesPolicy.createHTML('');
}
} // Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
+1 -1
View File
File diff suppressed because one or more lines are too long
+19 -9
View File
@@ -200,13 +200,13 @@ const getGlobal = () => typeof window === 'undefined' ? null : window;
* Creates a no-op policy for internal use only.
* Don't export this function outside this module!
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
* @param {Document} document The document object (to determine policy name suffix)
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
* are not supported).
* are not supported or creating the policy failed).
*/
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
return null;
} // Allow the callers to control the unique policy name
@@ -217,8 +217,8 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
let suffix = null;
const ATTR_NAME = 'data-tt-policy-suffix';
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
suffix = document.currentScript.getAttribute(ATTR_NAME);
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
suffix = purifyHostElement.getAttribute(ATTR_NAME);
}
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
@@ -269,6 +269,7 @@ function createDOMPurify() {
}
const originalDocument = window.document;
const currentScript = originalDocument.currentScript;
let {
document
} = window;
@@ -302,9 +303,8 @@ function createDOMPurify() {
}
}
let trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
let emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
let trustedTypesPolicy;
let emptyHTML = '';
const {
implementation,
createNodeIterator,
@@ -678,9 +678,19 @@ function createDOMPurify() {
} // Overwrite existing TrustedTypes policy.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables in case using the internal policy did not succeed.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables required by `sanitize`.
emptyHTML = trustedTypesPolicy.createHTML('');
} else {
// Uninitialized policy, attempt to initialize the internal dompurify policy.
if (trustedTypesPolicy === undefined) {
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
} // If creating the internal policy succeeded sign internal variables.
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
emptyHTML = trustedTypesPolicy.createHTML('');
}
} // Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
+1 -1
View File
File diff suppressed because one or more lines are too long
+19 -9
View File
@@ -206,13 +206,13 @@
* Creates a no-op policy for internal use only.
* Don't export this function outside this module!
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
* @param {Document} document The document object (to determine policy name suffix)
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
* are not supported).
* are not supported or creating the policy failed).
*/
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
return null;
} // Allow the callers to control the unique policy name
@@ -223,8 +223,8 @@
let suffix = null;
const ATTR_NAME = 'data-tt-policy-suffix';
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
suffix = document.currentScript.getAttribute(ATTR_NAME);
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
suffix = purifyHostElement.getAttribute(ATTR_NAME);
}
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
@@ -275,6 +275,7 @@
}
const originalDocument = window.document;
const currentScript = originalDocument.currentScript;
let {
document
} = window;
@@ -308,9 +309,8 @@
}
}
let trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
let emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
let trustedTypesPolicy;
let emptyHTML = '';
const {
implementation,
createNodeIterator,
@@ -684,9 +684,19 @@
} // Overwrite existing TrustedTypes policy.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables in case using the internal policy did not succeed.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables required by `sanitize`.
emptyHTML = trustedTypesPolicy.createHTML('');
} else {
// Uninitialized policy, attempt to initialize the internal dompurify policy.
if (trustedTypesPolicy === undefined) {
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
} // If creating the internal policy succeeded sign internal variables.
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
emptyHTML = trustedTypesPolicy.createHTML('');
}
} // Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+23 -14
View File
@@ -26,11 +26,11 @@ const getGlobal = () => (typeof window === 'undefined' ? null : window);
* Creates a no-op policy for internal use only.
* Don't export this function outside this module!
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
* @param {Document} document The document object (to determine policy name suffix)
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
* are not supported).
* are not supported or creating the policy failed).
*/
const _createTrustedTypesPolicy = function (trustedTypes, document) {
const _createTrustedTypesPolicy = function (trustedTypes, purifyHostElement) {
if (
typeof trustedTypes !== 'object' ||
typeof trustedTypes.createPolicy !== 'function'
@@ -43,11 +43,8 @@ const _createTrustedTypesPolicy = function (trustedTypes, document) {
// Policy creation with duplicate names throws in Trusted Types.
let suffix = null;
const ATTR_NAME = 'data-tt-policy-suffix';
if (
document.currentScript &&
document.currentScript.hasAttribute(ATTR_NAME)
) {
suffix = document.currentScript.getAttribute(ATTR_NAME);
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
suffix = purifyHostElement.getAttribute(ATTR_NAME);
}
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
@@ -96,6 +93,7 @@ function createDOMPurify(window = getGlobal()) {
}
const originalDocument = window.document;
const currentScript = originalDocument.currentScript;
let { document } = window;
const {
@@ -130,11 +128,8 @@ function createDOMPurify(window = getGlobal()) {
}
}
let trustedTypesPolicy = _createTrustedTypesPolicy(
trustedTypes,
originalDocument
);
let emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
let trustedTypesPolicy;
let emptyHTML = '';
const {
implementation,
@@ -603,8 +598,22 @@ function createDOMPurify(window = getGlobal()) {
// Overwrite existing TrustedTypes policy.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
// Sign local variables in case using the internal policy did not succeed.
// Sign local variables required by `sanitize`.
emptyHTML = trustedTypesPolicy.createHTML('');
} else {
// Uninitialized policy, attempt to initialize the internal dompurify policy.
if (trustedTypesPolicy === undefined) {
trustedTypesPolicy = _createTrustedTypesPolicy(
trustedTypes,
currentScript
);
}
// If creating the internal policy succeeded sign internal variables.
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
emptyHTML = trustedTypesPolicy.createHTML('');
}
}
// Prevent further manipulation of configuration.
+183 -133
View File
@@ -11,7 +11,13 @@ module.exports = function (JSDOM) {
}
}
function loadDOMPurify(assert, addScriptAttribute, setup, onload) {
function loadDOMPurify(
assert,
addScriptAttribute,
setup,
beforeOnLoad,
onload
) {
const testDone = assert.async();
const { window } = new JSDOM('<head></head>', {
runScripts: 'dangerously',
@@ -30,68 +36,95 @@ module.exports = function (JSDOM) {
window.document.body.appendChild(scriptEl);
assert.ok(window.DOMPurify.sanitize);
// Sanity check
assert.equal(
window.DOMPurify.sanitize('<img src=x onerror=alert(1)>'),
'<img src="x">'
);
if (beforeOnLoad) {
beforeOnLoad(window);
}
if (onload) {
onload(window);
}
testDone();
}
QUnit.test('works in a non-Trusted Type environment', function (assert) {
let policyCreated;
function loadDOMPurifyWithSanityCheck(
assert,
addScriptAttribute,
setup,
onload
) {
const beforeOnLoadSanityCheck = function (window) {
assert.equal(
window.DOMPurify.sanitize('<img src=x onerror=alert(1)>'),
'<img src="x">'
);
};
loadDOMPurify(
assert,
false,
function setup(window) {
delete window.trustedTypes;
},
function onload(window) {
const output = window.DOMPurify.sanitize('<img>');
assert.ok(typeof output === 'string');
}
addScriptAttribute,
setup,
beforeOnLoadSanityCheck,
onload
);
});
QUnit.test('works in a Trusted Type environment', function (assert) {
let policyCreated;
loadDOMPurify(
assert,
false,
function setup(window) {
window.trustedTypes = {
createPolicy(name, rules) {
policyCreated = name;
return {
createHTML(s) {
return new StringWrapper(rules.createHTML(s));
},
};
},
};
},
function onload(window) {
assert.equal(policyCreated, 'dompurify');
const output = window.DOMPurify.sanitize('<img>', {
RETURN_TRUSTED_TYPE: true,
});
assert.equal(output, '<img>');
assert.ok(output instanceof StringWrapper);
}
);
});
}
QUnit.test(
'supports configuring the policy suffix via an attribute',
'sanity check: works in a non-Trusted Type environment',
function (assert) {
let policyCreated;
loadDOMPurify(
loadDOMPurifyWithSanityCheck(
assert,
false,
function setup(window) {
delete window.trustedTypes;
},
function onload(window) {
const output = window.DOMPurify.sanitize('<img>');
assert.ok(typeof output === 'string');
}
);
}
);
QUnit.test(
'sanity check: works in a Trusted Type environment',
function (assert) {
let policyCreated;
loadDOMPurifyWithSanityCheck(
assert,
false,
function setup(window) {
window.trustedTypes = {
createPolicy(name, rules) {
policyCreated = name;
return {
createHTML(s) {
return new StringWrapper(rules.createHTML(s));
},
};
},
};
},
function onload(window) {
assert.equal(policyCreated, 'dompurify');
const output = window.DOMPurify.sanitize('<img>', {
RETURN_TRUSTED_TYPE: true,
});
assert.equal(output, '<img>');
assert.ok(output instanceof StringWrapper);
}
);
}
);
QUnit.test(
'sanity check: supports configuring the policy suffix via an attribute',
function (assert) {
let policyCreated;
loadDOMPurifyWithSanityCheck(
assert,
true,
function setup(window) {
@@ -109,76 +142,56 @@ module.exports = function (JSDOM) {
}
);
QUnit.test(
'supports TRUSTED_TYPES_POLICY via sanitize()',
function (assert) {
loadDOMPurify(
assert,
false,
function setup(window) {
window.trustedTypes = {
createPolicy(name, rules) {
policyCreated = name;
return {
createHTML(s) {
return new StringWrapper(rules.createHTML(s));
},
};
QUnit.test('supports TRUSTED_TYPES_POLICY via sanitize()', function (assert) {
loadDOMPurify(assert, false, undefined, undefined, function onload(window) {
let validationError;
try {
window.DOMPurify.sanitize('<img>', {
TRUSTED_TYPES_POLICY: {
createScript(s) {
return s;
},
};
},
function onload(window) {
assert.equal(policyCreated, 'dompurify');
var validationError;
try {
window.DOMPurify.sanitize('<img>', {
TRUSTED_TYPES_POLICY: {
createScript(s) {
return s;
},
},
});
} catch (e) {
validationError = e;
}
assert.equal(
validationError.message,
'TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'
);
try {
window.DOMPurify.sanitize('<img>', {
TRUSTED_TYPES_POLICY: {
createHTML(s) {
return s;
},
},
});
} catch (e) {
validationError = e;
}
assert.equal(
validationError.message,
'TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'
);
var suppliedPolicyCallCount = 0;
window.DOMPurify.sanitize('<img>', {
TRUSTED_TYPES_POLICY: {
createHTML(s) {
suppliedPolicyCallCount += 1;
return new StringWrapper(s);
},
createScriptURL(s) {
return new StringWrapper(s);
},
},
});
assert.equal(suppliedPolicyCallCount, 2);
}
},
});
} catch (e) {
validationError = e;
}
assert.equal(
validationError.message,
'TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'
);
}
);
try {
window.DOMPurify.sanitize('<img>', {
TRUSTED_TYPES_POLICY: {
createHTML(s) {
return s;
},
},
});
} catch (e) {
validationError = e;
}
assert.equal(
validationError.message,
'TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'
);
let suppliedPolicyCallCount = 0;
window.DOMPurify.sanitize('<img>', {
TRUSTED_TYPES_POLICY: {
createHTML(s) {
suppliedPolicyCallCount += 1;
return new StringWrapper(s);
},
createScriptURL(s) {
return new StringWrapper(s);
},
},
});
assert.equal(suppliedPolicyCallCount, 2);
});
});
QUnit.test(
'supports TRUSTED_TYPES_POLICY via setConfig() on a new instance',
@@ -186,24 +199,13 @@ module.exports = function (JSDOM) {
loadDOMPurify(
assert,
false,
function setup(window) {
window.trustedTypes = {
createPolicy(name, rules) {
policyCreated = name;
return {
createHTML(s) {
return new StringWrapper(rules.createHTML(s));
},
};
},
};
},
undefined,
undefined,
function onload(window) {
assert.equal(policyCreated, 'dompurify');
var purify = window.DOMPurify();
let purify = window.DOMPurify();
assert.notEqual(purify, window.DOMPurify);
var suppliedPolicyCallCount = 0;
let suppliedPolicyCallCount = 0;
purify.setConfig({
TRUSTED_TYPES_POLICY: {
createHTML(s) {
@@ -222,4 +224,52 @@ module.exports = function (JSDOM) {
);
}
);
QUnit.test(
'internal TrustedTypes policy is not created when TRUSTED_TYPES_POLICY option is provided',
function (assert) {
const createdPolicies = [];
loadDOMPurify(
assert,
false,
function setup(window) {
window.trustedTypes = {
createPolicy(name, rules) {
createdPolicies.push(name);
return {
createHTML(s) {
return new StringWrapper(rules.createHTML(s));
},
createScriptURL(s) {
return new StringWrapper(rules.createScriptURL(s));
},
};
},
};
},
undefined,
function onload(window) {
assert.equal(createdPolicies.length, 0);
const testPolicy = window.trustedTypes.createPolicy('test', {
createHTML(s) {
return s;
},
createScriptURL(s) {
return s;
},
});
window.DOMPurify.sanitize('<img />', {
TRUSTED_TYPES_POLICY: testPolicy,
});
assert.equal(createdPolicies.length, 1);
assert.equal(createdPolicies[0], 'test');
// Subsequent calls to `sanitize` even without supplying a configuration
// should recognize previously set Trusted Types policy.
window.DOMPurify.sanitize('<img />');
assert.equal(createdPolicies.length, 1);
assert.equal(createdPolicies[0], 'test');
}
);
}
);
};