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 @@