Skip to content

Commit a4fb73b

Browse files
JordanMilnehonestbleeps
authored andcommitted
some cleanup to html stuff
1 parent 6b5c6eb commit a4fb73b

7 files changed

Lines changed: 210 additions & 31 deletions

File tree

Chrome/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"jquery-fieldselection.min.js",
2626
"tinycon.js",
2727
"jquery.tokeninput.js",
28+
"HTMLPasteurizer.js",
2829
"snuownd.js",
2930
"utils.js",
3031
"browsersupport.js",

OperaBlink/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"jquery-fieldselection.min.js",
2626
"tinycon.js",
2727
"jquery.tokeninput.js",
28+
"HTMLPasteurizer.js",
2829
"snuownd.js",
2930
"utils.js",
3031
"browsersupport.js",

RES.safariextension/Info.plist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<string>jquery-fieldselection.min.js</string>
3636
<string>tinycon.js</string>
3737
<string>jquery.tokeninput.js</string>
38+
<string>HTMLPasteurizer.js</string>
3839
<string>snuownd.js</string>
3940
<string>hogan-2.0.0.js</string>
4041
<string>utils.js</string>

XPI/lib/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ pageMod.PageMod({
170170
self.data.url('jquery-fieldselection.min.js'),
171171
self.data.url('tinycon.js'),
172172
self.data.url('jquery.tokeninput.js'),
173+
self.data.url('HTMLPasteurizer.js'),
173174
self.data.url('snuownd.js'),
174175
self.data.url('utils.js'),
175176
self.data.url('browsersupport.js'),

lib/HTMLPasteurizer.js

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
* HTMLPasteurizer
3+
* Copyright 2014 Jordan Milne
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
(function(window, $) {
19+
"use strict";
20+
21+
var Pasteurizer = {};
22+
window.Pasteurizer = Pasteurizer;
23+
24+
// Some older browsers allow whitespace in protocols, but ignore
25+
// it during processing. Strip any weirdness out.
26+
var SCHEME_FILTER = /(:(?!$)|[^:a-z0-9\.\-\+])/ig;
27+
28+
Pasteurizer.DEFAULT_CONFIG = {
29+
elemWhitelist: [
30+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'div', 'code',
31+
'br', 'hr', 'p', 'a', 'img', 'pre', 'blockquote', 'table',
32+
'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'strong', 'em',
33+
'i', 'b', 'u', 'ul', 'ol', 'li', 'dl', 'dt', 'dd',
34+
'font', 'center', 'small', 's', 'q', 'sub', 'sup', 'del'
35+
],
36+
// global attribute whitelist
37+
attrWhitelist: [
38+
'title', 'colspan', 'rowspan', 'cellspacing', 'cellpadding',
39+
'scope', 'face', 'color', 'size', 'bgcolor', 'align'
40+
],
41+
// tag-specific attribute whitelists
42+
tagAttrWhitelist: {
43+
'img': ['src', 'alt'],
44+
'a': ['href']
45+
},
46+
// Which schemes may be linked to
47+
schemeWhitelist: [
48+
"http:", "https:", "ftp:", "mailto:",
49+
"git:", "steam:", "irc:", "news:", "mumble:",
50+
"ssh:", "ircs:", "ts3server:", ":"
51+
],
52+
// Whether or not to hoist the contents of removed nodes up the tree.
53+
hoistOrphanedContents: true,
54+
55+
// Tags that should *not* have their contents hoisted
56+
hoistBlacklist: ["script", "style"]
57+
};
58+
59+
Pasteurizer.scrubNode = function(node, config) {
60+
var jNode = $(node);
61+
var nodeName = node.nodeName.toLowerCase();
62+
var nodeType = node.nodeType;
63+
64+
var validNode = false;
65+
66+
if(nodeType === 1) {
67+
validNode = config.elemWhitelist.indexOf(nodeName) !== -1;
68+
} else if(nodeType < 6 || nodeType === 9 || nodeType == 11) {
69+
validNode = true;
70+
}
71+
72+
if(validNode && node.nodeType === 1) {
73+
// Kill anchor tags with invalid hrefs.
74+
if(nodeName === "a") {
75+
if(node.protocol !== undefined) {
76+
var scrubbedProto = node.protocol.replace(SCHEME_FILTER, "");
77+
78+
// Only allow non-whitelisted schemes unless the document was served via
79+
// the same scheme.
80+
if(config.schemeWhitelist.indexOf(scrubbedProto) === -1 &&
81+
scrubbedProto !== document.location.protocol) {
82+
validNode = false;
83+
}
84+
} else {
85+
// TODO: Handle UAs that don't support a.protocol?
86+
// we may need to bundle URL.js.
87+
}
88+
}
89+
}
90+
91+
if(validNode && node.nodeType === 1) {
92+
// Let's not invalidate any iterators, collect all attribute names.
93+
var attrs = $.map(node.attributes, function(attr){
94+
return attr.nodeName;
95+
});
96+
97+
// Remove unwanted attributes
98+
attrs.forEach(function(attrName) {
99+
100+
// Is this attr allowed on any node?
101+
if(config.attrWhitelist.indexOf(attrName) !== -1) {
102+
return;
103+
}
104+
105+
// is this attr allowed on *this* node?
106+
if(nodeName in config.tagAttrWhitelist &&
107+
config.tagAttrWhitelist[nodeName].indexOf(attrName) !== -1) {
108+
return;
109+
}
110+
111+
// jQuery.removeAttr chokes on attribute names containing quotes
112+
node.removeAttribute(attrName);
113+
});
114+
}
115+
116+
var canHoist = (config.hoistOrphanedContents &&
117+
config.hoistBlacklist.indexOf(nodeName) === -1);
118+
119+
// Cut out early if we don't need the contents
120+
if(!validNode && !canHoist) {
121+
jNode.remove();
122+
return;
123+
}
124+
125+
jNode.contents().each(function(i, child) {
126+
Pasteurizer.scrubNode(child, config);
127+
});
128+
129+
if(!validNode) {
130+
// remove the node and put its remaining contents in its place.
131+
jNode.contents().detach().insertAfter(jNode);
132+
jNode.remove();
133+
}
134+
};
135+
136+
Pasteurizer.safeParseHTML = function(html, config) {
137+
138+
if(!config || $.isEmptyObject(config)) {
139+
config = Pasteurizer.DEFAULT_CONFIG;
140+
}
141+
142+
143+
// DOMParser behaves similarly to jQuery.parseHTML, but it won't make any
144+
// requests at parse time.
145+
var parser = new DOMParser();
146+
147+
//TODO: handle <parsererror>
148+
var parsed = parser.parseFromString(html, "text/html");
149+
150+
// DOMParser wraps HTML fragments in body tags
151+
var body = $(parsed).find('body').first();
152+
153+
body.contents().each(function(i, node) {
154+
Pasteurizer.scrubNode(node, config);
155+
});
156+
return body.contents();
157+
};
158+
159+
}(window, jQuery));
160+
161+
162+
163+
/*
164+
* DOMParser HTML extension
165+
* 2012-09-04
166+
*
167+
* By Eli Grey, http://eligrey.com
168+
* Public domain.
169+
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
170+
*/
171+
172+
/*! @source https://gist.github.com/1129031 */
173+
/*global document, DOMParser*/
174+
175+
(function(DOMParser) {
176+
"use strict";
177+
178+
var DOMParser_proto = DOMParser.prototype;
179+
var real_parseFromString = DOMParser_proto.parseFromString;
180+
181+
// Firefox/Opera/IE throw errors on unsupported types
182+
try {
183+
// WebKit returns null on unsupported types
184+
if ((new DOMParser).parseFromString("", "text/html")) {
185+
// text/html parsing is natively supported
186+
return;
187+
}
188+
} catch (ex) {}
189+
190+
DOMParser_proto.parseFromString = function(markup, type) {
191+
if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
192+
var doc = document.implementation.createHTMLDocument("");
193+
if (markup.toLowerCase().indexOf('<!doctype') > -1) {
194+
doc.documentElement.innerHTML = markup;
195+
}
196+
else {
197+
doc.body.innerHTML = markup;
198+
}
199+
return doc;
200+
} else {
201+
return real_parseFromString.apply(this, arguments);
202+
}
203+
};
204+
}(DOMParser));

lib/reddit_enhancement_suite.user.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var RESVersion = "4.3.2";
22

3-
var jQuery, $, guiders, Tinycon, SnuOwnd;
3+
var jQuery, $, guiders, Tinycon, SnuOwnd, Pasteurizer;
44

55
/*
66
Reddit Enhancement Suite - a suite of tools to enhance Reddit

lib/utils.js

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -499,36 +499,7 @@ RESUtils.stripHTML = function(str) {
499499
return str;
500500
};
501501
RESUtils.sanitizeHTML = function(htmlStr) {
502-
if (!this.sanitizer) {
503-
var SnuOwnd = window.SnuOwnd;
504-
var redditCallbacks = SnuOwnd.getRedditCallbacks();
505-
var callbacks = SnuOwnd.createCustomCallbacks({
506-
paragraph: function(out, text, options) {
507-
if (text) out.s += text.s;
508-
},
509-
autolink: redditCallbacks.autolink,
510-
raw_html_tag: redditCallbacks.raw_html_tag
511-
});
512-
var rendererConfig = SnuOwnd.defaultRenderState();
513-
rendererConfig.flags = SnuOwnd.DEFAULT_WIKI_FLAGS;
514-
rendererConfig.html_element_whitelist = [
515-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'div', 'code',
516-
'br', 'hr', 'p', 'a', 'img', 'pre', 'blockquote', 'table',
517-
'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'strong', 'em',
518-
'i', 'b', 'u', 'ul', 'ol', 'li', 'dl', 'dt', 'dd',
519-
'font', 'center', 'small', 's', 'q', 'sub', 'sup', 'del'
520-
];
521-
rendererConfig.html_attr_whitelist = [
522-
'href', 'title', 'src', 'alt', 'colspan',
523-
'rowspan', 'cellspacing', 'cellpadding', 'scope',
524-
'face', 'color', 'size', 'bgcolor', 'align'
525-
];
526-
this.sanitizer = SnuOwnd.getParser({
527-
callbacks: callbacks,
528-
context: rendererConfig
529-
});
530-
}
531-
return this.sanitizer.render(htmlStr);
502+
return Pasteurizer.safeParseHTML(htmlStr).wrapAll('<div></div>').parent().html();
532503
};
533504
RESUtils.firstValid = function() {
534505
for (var i = 0, len = arguments.length; i < len; i++) {

0 commit comments

Comments
 (0)