| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 'use strict'; | |
| 6 | |
| 7 /** | |
| 8 * Whitelist of tag names allowed in parseHtmlSubset. | |
| 9 * @type {[string]} | |
| 10 */ | |
| 11 var allowedTags = ['A', 'B', 'STRONG']; | |
| 12 | |
| 13 /** | |
| 14 * Parse a very small subset of HTML. | |
| 15 * @param {string} s The string to parse. | |
| 16 * @throws {Error} In case of non supported markup. | |
| 17 * @return {DocumentFragment} A document fragment containing the DOM tree. | |
| 18 */ | |
| 19 var allowedAttributes = { | |
| 20 'href': function(node, value) { | |
| 21 // Only allow a[href] starting with http:// and https:// | |
| 22 return node.tagName == 'A' && (value.indexOf('http://') == 0 || | |
| 23 value.indexOf('https://') == 0); | |
| 24 }, | |
| 25 'target': function(node, value) { | |
| 26 // Allow a[target] but reset the value to "". | |
| 27 if (node.tagName != 'A') | |
| 28 return false; | |
| 29 node.setAttribute('target', ''); | |
| 30 return true; | |
| 31 }, | |
| 32 }; | |
| 33 | |
| 34 /** | 5 /** |
| 35 * Parse a very small subset of HTML. This ensures that insecure HTML / | 6 * Parse a very small subset of HTML. This ensures that insecure HTML / |
| 36 * javascript cannot be injected into the new tab page. | 7 * javascript cannot be injected into the new tab page. |
| 37 * @param {string} s The string to parse. | 8 * @param {string} s The string to parse. |
| 38 * @param {array=} extraTags Extra allowed tags. | 9 * @param {array=} extraTags Extra allowed tags. |
| 39 * @param {object=} extraAttrs Extra allowed attributes (all tags are run | 10 * @param {object=} extraAttrs Extra allowed attributes (all tags are run |
| 40 * through these). | 11 * through these). |
| 41 * @throws {Error} In case of non supported markup. | 12 * @throws {Error} In case of non supported markup. |
| 42 * @return {DocumentFragment} A document fragment containing the DOM tree. | 13 * @return {DocumentFragment} A document fragment containing the DOM tree. |
| 43 */ | 14 */ |
| 44 function parseHtmlSubset(s, extraTags, extraAttrs) { | 15 var parseHtmlSubset = (function() { |
| 16 'use strict'; |
| 17 |
| 18 var allowedAttributes = { |
| 19 'href': function(node, value) { |
| 20 // Only allow a[href] starting with http:// and https:// |
| 21 return node.tagName == 'A' && (value.indexOf('http://') == 0 || |
| 22 value.indexOf('https://') == 0); |
| 23 }, |
| 24 'target': function(node, value) { |
| 25 // Allow a[target] but reset the value to "". |
| 26 if (node.tagName != 'A') |
| 27 return false; |
| 28 node.setAttribute('target', ''); |
| 29 return true; |
| 30 } |
| 31 }; |
| 32 |
| 33 /** |
| 34 * Whitelist of tag names allowed in parseHtmlSubset. |
| 35 * @type {[string]} |
| 36 */ |
| 37 var allowedTags = ['A', 'B', 'STRONG']; |
| 38 |
| 45 function merge() { | 39 function merge() { |
| 46 var clone = {}; | 40 var clone = {}; |
| 47 for (var i = 0; i < arguments.length; ++i) { | 41 for (var i = 0; i < arguments.length; ++i) { |
| 48 if (typeof arguments[i] == 'object') { | 42 if (typeof arguments[i] == 'object') { |
| 49 for (var key in arguments[i]) { | 43 for (var key in arguments[i]) { |
| 50 if (arguments[i].hasOwnProperty(key)) | 44 if (arguments[i].hasOwnProperty(key)) |
| 51 clone[key] = arguments[i][key]; | 45 clone[key] = arguments[i][key]; |
| 52 } | 46 } |
| 53 } | 47 } |
| 54 } | 48 } |
| 55 return clone; | 49 return clone; |
| 56 } | 50 } |
| 57 | 51 |
| 58 function walk(n, f) { | 52 function walk(n, f) { |
| 59 f(n); | 53 f(n); |
| 60 for (var i = 0; i < n.childNodes.length; i++) { | 54 for (var i = 0; i < n.childNodes.length; i++) { |
| 61 walk(n.childNodes[i], f); | 55 walk(n.childNodes[i], f); |
| 62 } | 56 } |
| 63 } | 57 } |
| 64 | 58 |
| 65 function assertElement(node) { | 59 function assertElement(tags, node) { |
| 66 if (tags.indexOf(node.tagName) == -1) | 60 if (tags.indexOf(node.tagName) == -1) |
| 67 throw Error(node.tagName + ' is not supported'); | 61 throw Error(node.tagName + ' is not supported'); |
| 68 } | 62 } |
| 69 | 63 |
| 70 function assertAttribute(attrNode, node) { | 64 function assertAttribute(attrs, attrNode, node) { |
| 71 var n = attrNode.nodeName; | 65 var n = attrNode.nodeName; |
| 72 var v = attrNode.nodeValue; | 66 var v = attrNode.nodeValue; |
| 73 if (!attrs.hasOwnProperty(n) || !attrs[n](node, v)) | 67 if (!attrs.hasOwnProperty(n) || !attrs[n](node, v)) |
| 74 throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported'); | 68 throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported'); |
| 75 } | 69 } |
| 76 | 70 |
| 77 var tags = allowedTags.concat(extraTags); | 71 return function(s, extraTags, extraAttrs) { |
| 78 var attrs = merge(allowedAttributes, extraAttrs); | 72 var tags = allowedTags.concat(extraTags); |
| 73 var attrs = merge(allowedAttributes, extraAttrs); |
| 79 | 74 |
| 80 var r = document.createRange(); | 75 var r = document.createRange(); |
| 81 r.selectNode(document.body); | 76 r.selectNode(document.body); |
| 82 // This does not execute any scripts. | 77 // This does not execute any scripts. |
| 83 var df = r.createContextualFragment(s); | 78 var df = r.createContextualFragment(s); |
| 84 walk(df, function(node) { | 79 walk(df, function(node) { |
| 85 switch (node.nodeType) { | 80 switch (node.nodeType) { |
| 86 case Node.ELEMENT_NODE: | 81 case Node.ELEMENT_NODE: |
| 87 assertElement(node); | 82 assertElement(tags, node); |
| 88 var attrs = node.attributes; | 83 var nodeAttrs = node.attributes; |
| 89 for (var i = 0; i < attrs.length; ++i) { | 84 for (var i = 0; i < nodeAttrs.length; ++i) { |
| 90 assertAttribute(attrs[i], node); | 85 assertAttribute(attrs, nodeAttrs[i], node); |
| 91 } | 86 } |
| 92 break; | 87 break; |
| 93 | 88 |
| 94 case Node.COMMENT_NODE: | 89 case Node.COMMENT_NODE: |
| 95 case Node.DOCUMENT_FRAGMENT_NODE: | 90 case Node.DOCUMENT_FRAGMENT_NODE: |
| 96 case Node.TEXT_NODE: | 91 case Node.TEXT_NODE: |
| 97 break; | 92 break; |
| 98 | 93 |
| 99 default: | 94 default: |
| 100 throw Error('Node type ' + node.nodeType + ' is not supported'); | 95 throw Error('Node type ' + node.nodeType + ' is not supported'); |
| 101 } | 96 } |
| 102 }); | 97 }); |
| 103 return df; | 98 return df; |
| 104 } | 99 }; |
| 100 })(); |
| OLD | NEW |