diff --git a/src/runtime/xss_api.js b/src/runtime/xss_api.js index 087aa09..d4b3027 100644 --- a/src/runtime/xss_api.js +++ b/src/runtime/xss_api.js @@ -142,6 +142,41 @@ function sanitizeURL(url) { return ''; } +/** + * Sanitizes the specified attribute in the given array if present. + * + * @param {string} attribute the attribute to sanitize + * @param {*} attribs the attributes array to sanitize from + * @returns {object} the sanitized attribute and it's index in the array, or an empty object + */ +function sanitizeURLOnAttr(attribute, attribs) { + const index = attribs.indexOf(attribute); + if (index > -1) { + return { index, sanitizedUrl: sanitizeURL(attribs[index + 1]) || null }; + } + return {}; +} + +/** + * A sanitization policy that validates src/href attributes against the URI scheme. + * + * @param {string} tagName The name of the tag currently parsed + * @param {string[]} attribs An array of attribute names and values + * @returns {object} the resulting sanitized attributes + */ +function sanitizeURLPolicy(tagName, attribs) { + const initial = [].concat(attribs); + const result = sanitizer.makeTagPolicy()(tagName, attribs); + if (tagName === 'a') { + const { index, sanitizedUrl } = sanitizeURLOnAttr('href', initial); + result.attribs[index + 1] = sanitizedUrl; + } else if (tagName === 'img') { + const { index, sanitizedUrl } = sanitizeURLOnAttr('src', initial); + result.attribs[index + 1] = sanitizedUrl; + } + return result; +} + // function parseValidNumber(input) { // if (NUMBER_PATTERN.test(input)) { // return parseInt(input, 10); @@ -206,7 +241,7 @@ module.exports = { * @returns {String} */ filterHTML(input) { - return sanitizer.sanitize(input); + return sanitizer.sanitizeWithPolicy(input, sanitizeURLPolicy); }, /** diff --git a/test/runtime_test.js b/test/runtime_test.js index 78f51b6..ae9fa7c 100644 --- a/test/runtime_test.js +++ b/test/runtime_test.js @@ -49,6 +49,7 @@ const GLOBALS = { /* eslint-disable no-script-url, no-tabs */ xss: { aTag: 'XSS Link', + aTag2: 'Non XSS Link', url1: 'javascript:alert(0)', url2: 'javascript://%0Dalert(0)', // js comment & return char url3: 'javascript:/*-->', // js comment & break out of html tag @@ -61,7 +62,7 @@ const GLOBALS = { imgTag2: '', imgTag3: '', // grave accent quotes imgTag4: '', // embedded tab - imgTag5: '', // embedded encoded tab + imgTag5: '', // embedded encoded tab scriptTag1: '', scriptTag2: '', scriptTag3: '', // protocol resolution bypass diff --git a/test/templates/xss.htl b/test/templates/xss.htl index f4f9666..1280e18 100644 --- a/test/templates/xss.htl +++ b/test/templates/xss.htl @@ -18,6 +18,7 @@