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 = { | |
arv (Not doing code reviews)
2012/05/01 16:56:08
The initial idea was that these were part of the "
| |
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 }); |
arv (Not doing code reviews)
2012/05/01 16:56:08
This indentation is wrong.
Kevin Greer
2012/05/01 17:12:35
Fixed.
On 2012/05/01 16:56:08, arv wrote:
| |
103 return df; | 98 return df; |
104 } | 99 };})(); |
OLD | NEW |