@deepmetier/xss 0.0.0-stage → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +23 -2
- package/README.zh.md +24 -0
- package/bin/xss +67 -0
- package/dist/xss.js +1784 -0
- package/dist/xss.min.js +1 -0
- package/lib/cli.js +45 -0
- package/lib/default.js +468 -0
- package/lib/index.js +118 -0
- package/lib/parser.js +257 -0
- package/lib/util.js +34 -0
- package/lib/xss.js +237 -0
- package/package.json +63 -4
- package/typings/xss.d.ts +219 -0
package/dist/xss.js
ADDED
|
@@ -0,0 +1,1784 @@
|
|
|
1
|
+
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
|
2
|
+
/**
|
|
3
|
+
* default settings
|
|
4
|
+
*
|
|
5
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
var FilterCSS = require("cssfilter").FilterCSS;
|
|
9
|
+
var getDefaultCSSWhiteList = require("cssfilter").getDefaultWhiteList;
|
|
10
|
+
var _ = require("./util");
|
|
11
|
+
|
|
12
|
+
function getDefaultWhiteList() {
|
|
13
|
+
return {
|
|
14
|
+
a: ["target", "href", "title"],
|
|
15
|
+
abbr: ["title"],
|
|
16
|
+
address: [],
|
|
17
|
+
area: ["shape", "coords", "href", "alt"],
|
|
18
|
+
article: [],
|
|
19
|
+
aside: [],
|
|
20
|
+
audio: [
|
|
21
|
+
"autoplay",
|
|
22
|
+
"controls",
|
|
23
|
+
"crossorigin",
|
|
24
|
+
"loop",
|
|
25
|
+
"muted",
|
|
26
|
+
"preload",
|
|
27
|
+
"src",
|
|
28
|
+
],
|
|
29
|
+
b: [],
|
|
30
|
+
bdi: ["dir"],
|
|
31
|
+
bdo: ["dir"],
|
|
32
|
+
big: [],
|
|
33
|
+
blockquote: ["cite"],
|
|
34
|
+
br: [],
|
|
35
|
+
caption: [],
|
|
36
|
+
center: [],
|
|
37
|
+
cite: [],
|
|
38
|
+
code: [],
|
|
39
|
+
col: ["align", "valign", "span", "width"],
|
|
40
|
+
colgroup: ["align", "valign", "span", "width"],
|
|
41
|
+
dd: [],
|
|
42
|
+
del: ["datetime"],
|
|
43
|
+
details: ["open"],
|
|
44
|
+
div: [],
|
|
45
|
+
dl: [],
|
|
46
|
+
dt: [],
|
|
47
|
+
em: [],
|
|
48
|
+
figcaption: [],
|
|
49
|
+
figure: [],
|
|
50
|
+
font: ["color", "size", "face"],
|
|
51
|
+
footer: [],
|
|
52
|
+
h1: [],
|
|
53
|
+
h2: [],
|
|
54
|
+
h3: [],
|
|
55
|
+
h4: [],
|
|
56
|
+
h5: [],
|
|
57
|
+
h6: [],
|
|
58
|
+
header: [],
|
|
59
|
+
hr: [],
|
|
60
|
+
i: [],
|
|
61
|
+
img: ["src", "alt", "title", "width", "height", "loading"],
|
|
62
|
+
ins: ["datetime"],
|
|
63
|
+
kbd: [],
|
|
64
|
+
li: [],
|
|
65
|
+
mark: [],
|
|
66
|
+
nav: [],
|
|
67
|
+
ol: [],
|
|
68
|
+
p: [],
|
|
69
|
+
pre: [],
|
|
70
|
+
s: [],
|
|
71
|
+
section: [],
|
|
72
|
+
small: [],
|
|
73
|
+
span: [],
|
|
74
|
+
sub: [],
|
|
75
|
+
summary: [],
|
|
76
|
+
sup: [],
|
|
77
|
+
strong: [],
|
|
78
|
+
strike: [],
|
|
79
|
+
table: ["width", "border", "align", "valign"],
|
|
80
|
+
tbody: ["align", "valign"],
|
|
81
|
+
td: ["width", "rowspan", "colspan", "align", "valign"],
|
|
82
|
+
tfoot: ["align", "valign"],
|
|
83
|
+
th: ["width", "rowspan", "colspan", "align", "valign"],
|
|
84
|
+
thead: ["align", "valign"],
|
|
85
|
+
tr: ["rowspan", "align", "valign"],
|
|
86
|
+
tt: [],
|
|
87
|
+
u: [],
|
|
88
|
+
ul: [],
|
|
89
|
+
video: [
|
|
90
|
+
"autoplay",
|
|
91
|
+
"controls",
|
|
92
|
+
"crossorigin",
|
|
93
|
+
"loop",
|
|
94
|
+
"muted",
|
|
95
|
+
"playsinline",
|
|
96
|
+
"poster",
|
|
97
|
+
"preload",
|
|
98
|
+
"src",
|
|
99
|
+
"height",
|
|
100
|
+
"width",
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var defaultCSSFilter = new FilterCSS();
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* default onTag function
|
|
109
|
+
*
|
|
110
|
+
* @param {String} tag
|
|
111
|
+
* @param {String} html
|
|
112
|
+
* @param {Object} options
|
|
113
|
+
* @return {String}
|
|
114
|
+
*/
|
|
115
|
+
function onTag(tag, html, options) {
|
|
116
|
+
// do nothing
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* default onIgnoreTag function
|
|
121
|
+
*
|
|
122
|
+
* @param {String} tag
|
|
123
|
+
* @param {String} html
|
|
124
|
+
* @param {Object} options
|
|
125
|
+
* @return {String}
|
|
126
|
+
*/
|
|
127
|
+
function onIgnoreTag(tag, html, options) {
|
|
128
|
+
// do nothing
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* default onTagAttr function
|
|
133
|
+
*
|
|
134
|
+
* @param {String} tag
|
|
135
|
+
* @param {String} name
|
|
136
|
+
* @param {String} value
|
|
137
|
+
* @return {String}
|
|
138
|
+
*/
|
|
139
|
+
function onTagAttr(tag, name, value) {
|
|
140
|
+
// do nothing
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* default onIgnoreTagAttr function
|
|
145
|
+
*
|
|
146
|
+
* @param {String} tag
|
|
147
|
+
* @param {String} name
|
|
148
|
+
* @param {String} value
|
|
149
|
+
* @return {String}
|
|
150
|
+
*/
|
|
151
|
+
function onIgnoreTagAttr(tag, name, value) {
|
|
152
|
+
// do nothing
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* default escapeHtml function
|
|
157
|
+
*
|
|
158
|
+
* @param {String} html
|
|
159
|
+
*/
|
|
160
|
+
function escapeHtml(html) {
|
|
161
|
+
return html.replace(REGEXP_LT, "<").replace(REGEXP_GT, ">");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* default safeAttrValue function
|
|
166
|
+
*
|
|
167
|
+
* @param {String} tag
|
|
168
|
+
* @param {String} name
|
|
169
|
+
* @param {String} value
|
|
170
|
+
* @param {Object} cssFilter
|
|
171
|
+
* @return {String}
|
|
172
|
+
*/
|
|
173
|
+
function safeAttrValue(tag, name, value, cssFilter) {
|
|
174
|
+
// unescape attribute value firstly
|
|
175
|
+
value = friendlyAttrValue(value);
|
|
176
|
+
|
|
177
|
+
if (name === "href" || name === "src") {
|
|
178
|
+
// filter `href` and `src` attribute
|
|
179
|
+
// only allow the value that starts with `http://` | `https://` | `mailto:` | `/` | `#`
|
|
180
|
+
value = _.trim(value);
|
|
181
|
+
if (value === "#") return "#";
|
|
182
|
+
if (
|
|
183
|
+
!(
|
|
184
|
+
value.substr(0, 7) === "http://" ||
|
|
185
|
+
value.substr(0, 8) === "https://" ||
|
|
186
|
+
value.substr(0, 7) === "mailto:" ||
|
|
187
|
+
value.substr(0, 4) === "tel:" ||
|
|
188
|
+
value.substr(0, 11) === "data:image/" ||
|
|
189
|
+
value.substr(0, 6) === "ftp://" ||
|
|
190
|
+
value.substr(0, 2) === "./" ||
|
|
191
|
+
value.substr(0, 3) === "../" ||
|
|
192
|
+
value[0] === "#" ||
|
|
193
|
+
value[0] === "/"
|
|
194
|
+
)
|
|
195
|
+
) {
|
|
196
|
+
return "";
|
|
197
|
+
}
|
|
198
|
+
} else if (name === "action" || name === "formaction") {
|
|
199
|
+
// Only allow HTTP(S) or relative form URLs; the href/src data:image/ exception does not apply.
|
|
200
|
+
// A relative URL's first segment cannot contain a colon; colons in queries and fragments are not schemes.
|
|
201
|
+
value = _.trim(value);
|
|
202
|
+
if (!/^https?:\/\//i.test(value) && /^[^/?#]*:/.test(value)) {
|
|
203
|
+
return "";
|
|
204
|
+
}
|
|
205
|
+
} else if (name === "background") {
|
|
206
|
+
// filter `background` attribute (maybe no use)
|
|
207
|
+
// `javascript:`
|
|
208
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
|
209
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
} else if (name === "style") {
|
|
213
|
+
// `expression()`
|
|
214
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex = 0;
|
|
215
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)) {
|
|
216
|
+
return "";
|
|
217
|
+
}
|
|
218
|
+
// `url()`
|
|
219
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;
|
|
220
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {
|
|
221
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
|
222
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
|
223
|
+
return "";
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (cssFilter !== false) {
|
|
227
|
+
cssFilter = cssFilter || defaultCSSFilter;
|
|
228
|
+
value = cssFilter.process(value);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// escape `<>"` before returns
|
|
233
|
+
value = escapeAttrValue(value);
|
|
234
|
+
return value;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// RegExp list
|
|
238
|
+
var REGEXP_LT = /</g;
|
|
239
|
+
var REGEXP_GT = />/g;
|
|
240
|
+
var REGEXP_QUOTE = /"/g;
|
|
241
|
+
var REGEXP_QUOTE_2 = /"/g;
|
|
242
|
+
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/gim;
|
|
243
|
+
var REGEXP_ATTR_VALUE_COLON = /:?/gim;
|
|
244
|
+
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/gim;
|
|
245
|
+
// var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//gm;
|
|
246
|
+
var REGEXP_DEFAULT_ON_TAG_ATTR_4 =
|
|
247
|
+
/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a):/gi;
|
|
248
|
+
// var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;
|
|
249
|
+
// var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;
|
|
250
|
+
var REGEXP_DEFAULT_ON_TAG_ATTR_7 =
|
|
251
|
+
/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;
|
|
252
|
+
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/gi;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* escape double quote
|
|
256
|
+
*
|
|
257
|
+
* @param {String} str
|
|
258
|
+
* @return {String} str
|
|
259
|
+
*/
|
|
260
|
+
function escapeQuote(str) {
|
|
261
|
+
return str.replace(REGEXP_QUOTE, """);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* unescape double quote
|
|
266
|
+
*
|
|
267
|
+
* @param {String} str
|
|
268
|
+
* @return {String} str
|
|
269
|
+
*/
|
|
270
|
+
function unescapeQuote(str) {
|
|
271
|
+
return str.replace(REGEXP_QUOTE_2, '"');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* escape html entities
|
|
276
|
+
*
|
|
277
|
+
* @param {String} str
|
|
278
|
+
* @return {String}
|
|
279
|
+
*/
|
|
280
|
+
function escapeHtmlEntities(str) {
|
|
281
|
+
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode(str, code) {
|
|
282
|
+
return code[0] === "x" || code[0] === "X"
|
|
283
|
+
? String.fromCharCode(parseInt(code.substr(1), 16))
|
|
284
|
+
: String.fromCharCode(parseInt(code, 10));
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* escape html5 new danger entities
|
|
290
|
+
*
|
|
291
|
+
* @param {String} str
|
|
292
|
+
* @return {String}
|
|
293
|
+
*/
|
|
294
|
+
function escapeDangerHtml5Entities(str) {
|
|
295
|
+
return str
|
|
296
|
+
.replace(REGEXP_ATTR_VALUE_COLON, ":")
|
|
297
|
+
.replace(REGEXP_ATTR_VALUE_NEWLINE, " ");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* clear nonprintable characters
|
|
302
|
+
*
|
|
303
|
+
* @param {String} str
|
|
304
|
+
* @return {String}
|
|
305
|
+
*/
|
|
306
|
+
function clearNonPrintableCharacter(str) {
|
|
307
|
+
var str2 = "";
|
|
308
|
+
for (var i = 0, len = str.length; i < len; i++) {
|
|
309
|
+
str2 += str.charCodeAt(i) < 32 ? " " : str.charAt(i);
|
|
310
|
+
}
|
|
311
|
+
return _.trim(str2);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* get friendly attribute value
|
|
316
|
+
*
|
|
317
|
+
* @param {String} str
|
|
318
|
+
* @return {String}
|
|
319
|
+
*/
|
|
320
|
+
function friendlyAttrValue(str) {
|
|
321
|
+
str = unescapeQuote(str);
|
|
322
|
+
str = escapeHtmlEntities(str);
|
|
323
|
+
str = escapeDangerHtml5Entities(str);
|
|
324
|
+
str = clearNonPrintableCharacter(str);
|
|
325
|
+
return str;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* unescape attribute value
|
|
330
|
+
*
|
|
331
|
+
* @param {String} str
|
|
332
|
+
* @return {String}
|
|
333
|
+
*/
|
|
334
|
+
function escapeAttrValue(str) {
|
|
335
|
+
str = escapeQuote(str);
|
|
336
|
+
str = escapeHtml(str);
|
|
337
|
+
return str;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* `onIgnoreTag` function for removing all the tags that are not in whitelist
|
|
342
|
+
*/
|
|
343
|
+
function onIgnoreTagStripAll() {
|
|
344
|
+
return "";
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* remove tag body
|
|
349
|
+
* specify a `tags` list, if the tag is not in the `tags` list then process by the specify function (optional)
|
|
350
|
+
*
|
|
351
|
+
* @param {array} tags
|
|
352
|
+
* @param {function} next
|
|
353
|
+
*/
|
|
354
|
+
function StripTagBody(tags, next) {
|
|
355
|
+
if (typeof next !== "function") {
|
|
356
|
+
next = function () {};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
var isRemoveAllTag = !Array.isArray(tags);
|
|
360
|
+
function isRemoveTag(tag) {
|
|
361
|
+
if (isRemoveAllTag) return true;
|
|
362
|
+
return _.indexOf(tags, tag) !== -1;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
var removeList = [];
|
|
366
|
+
var posStart = false;
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
onIgnoreTag: function (tag, html, options) {
|
|
370
|
+
if (isRemoveTag(tag)) {
|
|
371
|
+
if (options.isClosing) {
|
|
372
|
+
var ret = "[/removed]";
|
|
373
|
+
var end = options.position + ret.length;
|
|
374
|
+
removeList.push([
|
|
375
|
+
posStart !== false ? posStart : options.position,
|
|
376
|
+
end,
|
|
377
|
+
]);
|
|
378
|
+
posStart = false;
|
|
379
|
+
return ret;
|
|
380
|
+
} else {
|
|
381
|
+
if (!posStart) {
|
|
382
|
+
posStart = options.position;
|
|
383
|
+
}
|
|
384
|
+
return "[removed]";
|
|
385
|
+
}
|
|
386
|
+
} else {
|
|
387
|
+
return next(tag, html, options);
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
remove: function (html) {
|
|
391
|
+
var rethtml = "";
|
|
392
|
+
var lastPos = 0;
|
|
393
|
+
_.forEach(removeList, function (pos) {
|
|
394
|
+
rethtml += html.slice(lastPos, pos[0]);
|
|
395
|
+
lastPos = pos[1];
|
|
396
|
+
});
|
|
397
|
+
rethtml += html.slice(lastPos);
|
|
398
|
+
return rethtml;
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* remove html comments
|
|
405
|
+
*
|
|
406
|
+
* @param {String} html
|
|
407
|
+
* @return {String}
|
|
408
|
+
*/
|
|
409
|
+
function stripCommentTag(html) {
|
|
410
|
+
var retHtml = "";
|
|
411
|
+
var lastPos = 0;
|
|
412
|
+
while (lastPos < html.length) {
|
|
413
|
+
var i = html.indexOf("<!--", lastPos);
|
|
414
|
+
if (i === -1) {
|
|
415
|
+
retHtml += html.slice(lastPos);
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
retHtml += html.slice(lastPos, i);
|
|
419
|
+
var j = html.indexOf("-->", i);
|
|
420
|
+
if (j === -1) {
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
lastPos = j + 3;
|
|
424
|
+
}
|
|
425
|
+
return retHtml;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* remove invisible characters
|
|
430
|
+
*
|
|
431
|
+
* @param {String} html
|
|
432
|
+
* @return {String}
|
|
433
|
+
*/
|
|
434
|
+
function stripBlankChar(html) {
|
|
435
|
+
var chars = html.split("");
|
|
436
|
+
chars = chars.filter(function (char) {
|
|
437
|
+
var c = char.charCodeAt(0);
|
|
438
|
+
if (c === 127) return false;
|
|
439
|
+
if (c <= 31) {
|
|
440
|
+
if (c === 10 || c === 13) return true;
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
});
|
|
445
|
+
return chars.join("");
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
exports.whiteList = getDefaultWhiteList();
|
|
449
|
+
exports.getDefaultWhiteList = getDefaultWhiteList;
|
|
450
|
+
exports.onTag = onTag;
|
|
451
|
+
exports.onIgnoreTag = onIgnoreTag;
|
|
452
|
+
exports.onTagAttr = onTagAttr;
|
|
453
|
+
exports.onIgnoreTagAttr = onIgnoreTagAttr;
|
|
454
|
+
exports.safeAttrValue = safeAttrValue;
|
|
455
|
+
exports.escapeHtml = escapeHtml;
|
|
456
|
+
exports.escapeQuote = escapeQuote;
|
|
457
|
+
exports.unescapeQuote = unescapeQuote;
|
|
458
|
+
exports.escapeHtmlEntities = escapeHtmlEntities;
|
|
459
|
+
exports.escapeDangerHtml5Entities = escapeDangerHtml5Entities;
|
|
460
|
+
exports.clearNonPrintableCharacter = clearNonPrintableCharacter;
|
|
461
|
+
exports.friendlyAttrValue = friendlyAttrValue;
|
|
462
|
+
exports.escapeAttrValue = escapeAttrValue;
|
|
463
|
+
exports.onIgnoreTagStripAll = onIgnoreTagStripAll;
|
|
464
|
+
exports.StripTagBody = StripTagBody;
|
|
465
|
+
exports.stripCommentTag = stripCommentTag;
|
|
466
|
+
exports.stripBlankChar = stripBlankChar;
|
|
467
|
+
exports.attributeWrapSign = '"';
|
|
468
|
+
exports.cssFilter = defaultCSSFilter;
|
|
469
|
+
exports.getDefaultCSSWhiteList = getDefaultCSSWhiteList;
|
|
470
|
+
|
|
471
|
+
},{"./util":4,"cssfilter":8}],2:[function(require,module,exports){
|
|
472
|
+
/**
|
|
473
|
+
* xss
|
|
474
|
+
*
|
|
475
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
476
|
+
*/
|
|
477
|
+
|
|
478
|
+
var DEFAULT = require("./default");
|
|
479
|
+
var parser = require("./parser");
|
|
480
|
+
var FilterXSS = require("./xss");
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* filter xss function
|
|
484
|
+
*
|
|
485
|
+
* @param {String} html
|
|
486
|
+
* @param {Object} options { whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml }
|
|
487
|
+
* @return {String}
|
|
488
|
+
*/
|
|
489
|
+
function filterXSS(html, options) {
|
|
490
|
+
var xss = new FilterXSS(options);
|
|
491
|
+
return xss.process(html);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Filter XSS and return both the sanitized HTML and a list of removed elements.
|
|
496
|
+
* Addresses feature request: https://github.com/leizongmin/js-xss/issues/284
|
|
497
|
+
*
|
|
498
|
+
* @param {String} html - dirty HTML string to sanitize
|
|
499
|
+
* @param {Object} options - same options as filterXSS()
|
|
500
|
+
* @return {Object} { html: String, removed: Array }
|
|
501
|
+
*
|
|
502
|
+
* Each item in `removed` is one of:
|
|
503
|
+
* { type: "tag", tag: "script", html: "<script>", isClosing: false }
|
|
504
|
+
* { type: "attr", tag: "a", attr: "onclick", value: "evil()" }
|
|
505
|
+
*/
|
|
506
|
+
function filterXSSWithResult(html, options) {
|
|
507
|
+
var removed = [];
|
|
508
|
+
|
|
509
|
+
// Copy options so we don't mutate the caller's object
|
|
510
|
+
var opts = {};
|
|
511
|
+
if (options) {
|
|
512
|
+
for (var key in options) {
|
|
513
|
+
opts[key] = options[key];
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Save any hooks the user may have already provided
|
|
518
|
+
var originalOnIgnoreTag = opts.onIgnoreTag;
|
|
519
|
+
var originalOnIgnoreTagAttr = opts.onIgnoreTagAttr;
|
|
520
|
+
|
|
521
|
+
// Override onIgnoreTag to record removed tags
|
|
522
|
+
opts.onIgnoreTag = function (tag, tagHtml, tagOptions) {
|
|
523
|
+
removed.push({
|
|
524
|
+
type: "tag",
|
|
525
|
+
tag: tag,
|
|
526
|
+
html: tagHtml,
|
|
527
|
+
isClosing: !!(tagOptions && tagOptions.isClosing),
|
|
528
|
+
});
|
|
529
|
+
// Still call the user's original hook if they had one
|
|
530
|
+
if (originalOnIgnoreTag) {
|
|
531
|
+
return originalOnIgnoreTag(tag, tagHtml, tagOptions);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// Override onIgnoreTagAttr to record removed attributes
|
|
536
|
+
opts.onIgnoreTagAttr = function (tag, name, value, isWhiteAttr) {
|
|
537
|
+
removed.push({
|
|
538
|
+
type: "attr",
|
|
539
|
+
tag: tag,
|
|
540
|
+
attr: name,
|
|
541
|
+
value: value,
|
|
542
|
+
});
|
|
543
|
+
// Still call the user's original hook if they had one
|
|
544
|
+
if (originalOnIgnoreTagAttr) {
|
|
545
|
+
return originalOnIgnoreTagAttr(tag, name, value, isWhiteAttr);
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
var cleanHtml = filterXSS(html, opts);
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
html: cleanHtml,
|
|
553
|
+
removed: removed,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
exports = module.exports = filterXSS;
|
|
557
|
+
exports.filterXSS = filterXSS;
|
|
558
|
+
exports.filterXSSWithResult = filterXSSWithResult;
|
|
559
|
+
exports.FilterXSS = FilterXSS;
|
|
560
|
+
|
|
561
|
+
(function () {
|
|
562
|
+
for (var i in DEFAULT) {
|
|
563
|
+
if (Object.prototype.hasOwnProperty.call(DEFAULT, i)) {
|
|
564
|
+
exports[i] = DEFAULT[i];
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
for (var j in parser) {
|
|
568
|
+
if (Object.prototype.hasOwnProperty.call(parser, j)) {
|
|
569
|
+
exports[j] = parser[j];
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
})();
|
|
573
|
+
|
|
574
|
+
// using `xss` on the browser, output `filterXSS` to the globals
|
|
575
|
+
if (typeof window !== "undefined") {
|
|
576
|
+
window.filterXSS = module.exports;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// using `xss` on the WebWorker, output `filterXSS` to the globals
|
|
580
|
+
function isWorkerEnv() {
|
|
581
|
+
return (
|
|
582
|
+
typeof self !== "undefined" &&
|
|
583
|
+
typeof DedicatedWorkerGlobalScope !== "undefined" &&
|
|
584
|
+
self instanceof DedicatedWorkerGlobalScope
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
if (isWorkerEnv()) {
|
|
588
|
+
self.filterXSS = module.exports;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
},{"./default":1,"./parser":3,"./xss":5}],3:[function(require,module,exports){
|
|
592
|
+
/**
|
|
593
|
+
* Simple HTML Parser
|
|
594
|
+
*
|
|
595
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
596
|
+
*/
|
|
597
|
+
|
|
598
|
+
var _ = require("./util");
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* get tag name
|
|
602
|
+
*
|
|
603
|
+
* @param {String} html e.g. '<a hef="#">'
|
|
604
|
+
* @return {String}
|
|
605
|
+
*/
|
|
606
|
+
function getTagName(html) {
|
|
607
|
+
var i = _.spaceIndex(html);
|
|
608
|
+
var tagName;
|
|
609
|
+
if (i === -1) {
|
|
610
|
+
tagName = html.slice(1, -1);
|
|
611
|
+
} else {
|
|
612
|
+
tagName = html.slice(1, i + 1);
|
|
613
|
+
}
|
|
614
|
+
tagName = _.trim(tagName).toLowerCase();
|
|
615
|
+
if (tagName.slice(0, 1) === "/") tagName = tagName.slice(1);
|
|
616
|
+
if (tagName.slice(-1) === "/") tagName = tagName.slice(0, -1);
|
|
617
|
+
return tagName;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* is close tag?
|
|
622
|
+
*
|
|
623
|
+
* @param {String} html 如:'<a hef="#">'
|
|
624
|
+
* @return {Boolean}
|
|
625
|
+
*/
|
|
626
|
+
function isClosing(html) {
|
|
627
|
+
return html.slice(0, 2) === "</";
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* parse input html and returns processed html
|
|
632
|
+
*
|
|
633
|
+
* @param {String} html
|
|
634
|
+
* @param {Function} onTag e.g. function (sourcePosition, position, tag, html, isClosing)
|
|
635
|
+
* @param {Function} escapeHtml
|
|
636
|
+
* @return {String}
|
|
637
|
+
*/
|
|
638
|
+
function parseTag(html, onTag, escapeHtml) {
|
|
639
|
+
"use strict";
|
|
640
|
+
|
|
641
|
+
var rethtml = "";
|
|
642
|
+
var lastPos = 0;
|
|
643
|
+
var tagStart = false;
|
|
644
|
+
var quoteStart = false;
|
|
645
|
+
var currentPos = 0;
|
|
646
|
+
var len = html.length;
|
|
647
|
+
var currentTagName = "";
|
|
648
|
+
var currentHtml = "";
|
|
649
|
+
|
|
650
|
+
chariterator: for (currentPos = 0; currentPos < len; currentPos++) {
|
|
651
|
+
var c = html.charAt(currentPos);
|
|
652
|
+
if (tagStart === false) {
|
|
653
|
+
if (c === "<") {
|
|
654
|
+
tagStart = currentPos;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
if (quoteStart === false) {
|
|
659
|
+
if (c === "<") {
|
|
660
|
+
rethtml += escapeHtml(html.slice(lastPos, currentPos));
|
|
661
|
+
tagStart = currentPos;
|
|
662
|
+
lastPos = currentPos;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
if (c === ">" || currentPos === len - 1) {
|
|
666
|
+
rethtml += escapeHtml(html.slice(lastPos, tagStart));
|
|
667
|
+
currentHtml = html.slice(tagStart, currentPos + 1);
|
|
668
|
+
currentTagName = getTagName(currentHtml);
|
|
669
|
+
rethtml += onTag(
|
|
670
|
+
tagStart,
|
|
671
|
+
rethtml.length,
|
|
672
|
+
currentTagName,
|
|
673
|
+
currentHtml,
|
|
674
|
+
isClosing(currentHtml)
|
|
675
|
+
);
|
|
676
|
+
lastPos = currentPos + 1;
|
|
677
|
+
tagStart = false;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (c === '"' || c === "'") {
|
|
681
|
+
var i = 1;
|
|
682
|
+
var ic = html.charAt(currentPos - i);
|
|
683
|
+
|
|
684
|
+
while (ic.trim() === "" || ic === "=") {
|
|
685
|
+
if (ic === "=") {
|
|
686
|
+
quoteStart = c;
|
|
687
|
+
continue chariterator;
|
|
688
|
+
}
|
|
689
|
+
ic = html.charAt(currentPos - ++i);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
} else {
|
|
693
|
+
if (c === quoteStart) {
|
|
694
|
+
quoteStart = false;
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (lastPos < len) {
|
|
701
|
+
rethtml += escapeHtml(html.substr(lastPos));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return rethtml;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9\\_:.-]/gim;
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* parse input attributes and returns processed attributes
|
|
711
|
+
*
|
|
712
|
+
* @param {String} html e.g. `href="#" target="_blank"`
|
|
713
|
+
* @param {Function} onAttr e.g. `function (name, value)`
|
|
714
|
+
* @return {String}
|
|
715
|
+
*/
|
|
716
|
+
function parseAttr(html, onAttr) {
|
|
717
|
+
"use strict";
|
|
718
|
+
|
|
719
|
+
var lastPos = 0;
|
|
720
|
+
var lastMarkPos = 0;
|
|
721
|
+
var retAttrs = [];
|
|
722
|
+
var tmpName = false;
|
|
723
|
+
var len = html.length;
|
|
724
|
+
|
|
725
|
+
function addAttr(name, value) {
|
|
726
|
+
name = _.trim(name);
|
|
727
|
+
name = name.replace(REGEXP_ILLEGAL_ATTR_NAME, "").toLowerCase();
|
|
728
|
+
if (name.length < 1) return;
|
|
729
|
+
var ret = onAttr(name, value || "");
|
|
730
|
+
if (ret) retAttrs.push(ret);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// 逐个分析字符
|
|
734
|
+
for (var i = 0; i < len; i++) {
|
|
735
|
+
var c = html.charAt(i);
|
|
736
|
+
var v, j;
|
|
737
|
+
if (tmpName === false && c === "=") {
|
|
738
|
+
tmpName = html.slice(lastPos, i);
|
|
739
|
+
lastPos = i + 1;
|
|
740
|
+
lastMarkPos = html.charAt(lastPos) === '"' || html.charAt(lastPos) === "'" ? lastPos : findNextQuotationMark(html, i + 1);
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if (tmpName !== false) {
|
|
744
|
+
if (
|
|
745
|
+
i === lastMarkPos
|
|
746
|
+
) {
|
|
747
|
+
j = html.indexOf(c, i + 1);
|
|
748
|
+
if (j === -1) {
|
|
749
|
+
break;
|
|
750
|
+
} else {
|
|
751
|
+
v = _.trim(html.slice(lastMarkPos + 1, j));
|
|
752
|
+
addAttr(tmpName, v);
|
|
753
|
+
tmpName = false;
|
|
754
|
+
i = j;
|
|
755
|
+
lastPos = i + 1;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (/\s|\n|\t/.test(c)) {
|
|
761
|
+
html = html.replace(/\s|\n|\t/g, " ");
|
|
762
|
+
if (tmpName === false) {
|
|
763
|
+
j = findNextEqual(html, i);
|
|
764
|
+
if (j === -1) {
|
|
765
|
+
v = _.trim(html.slice(lastPos, i));
|
|
766
|
+
addAttr(v);
|
|
767
|
+
tmpName = false;
|
|
768
|
+
lastPos = i + 1;
|
|
769
|
+
continue;
|
|
770
|
+
} else {
|
|
771
|
+
i = j - 1;
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
} else {
|
|
775
|
+
j = findBeforeEqual(html, i - 1);
|
|
776
|
+
if (j === -1) {
|
|
777
|
+
v = _.trim(html.slice(lastPos, i));
|
|
778
|
+
v = stripQuoteWrap(v);
|
|
779
|
+
addAttr(tmpName, v);
|
|
780
|
+
tmpName = false;
|
|
781
|
+
lastPos = i + 1;
|
|
782
|
+
continue;
|
|
783
|
+
} else {
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (lastPos < html.length) {
|
|
791
|
+
if (tmpName === false) {
|
|
792
|
+
addAttr(html.slice(lastPos));
|
|
793
|
+
} else {
|
|
794
|
+
addAttr(tmpName, stripQuoteWrap(_.trim(html.slice(lastPos))));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return _.trim(retAttrs.join(" "));
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function findNextEqual(str, i) {
|
|
802
|
+
for (; i < str.length; i++) {
|
|
803
|
+
var c = str[i];
|
|
804
|
+
if (c === " ") continue;
|
|
805
|
+
if (c === "=") return i;
|
|
806
|
+
return -1;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function findNextQuotationMark(str, i) {
|
|
811
|
+
for (; i < str.length; i++) {
|
|
812
|
+
var c = str[i];
|
|
813
|
+
if (c === " ") continue;
|
|
814
|
+
if (c === "'" || c === '"') return i;
|
|
815
|
+
return -1;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function findBeforeEqual(str, i) {
|
|
820
|
+
for (; i > 0; i--) {
|
|
821
|
+
var c = str[i];
|
|
822
|
+
if (c === " ") continue;
|
|
823
|
+
if (c === "=") return i;
|
|
824
|
+
return -1;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function isQuoteWrapString(text) {
|
|
829
|
+
if (
|
|
830
|
+
(text[0] === '"' && text[text.length - 1] === '"') ||
|
|
831
|
+
(text[0] === "'" && text[text.length - 1] === "'")
|
|
832
|
+
) {
|
|
833
|
+
return true;
|
|
834
|
+
} else {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function stripQuoteWrap(text) {
|
|
840
|
+
if (isQuoteWrapString(text)) {
|
|
841
|
+
return text.substr(1, text.length - 2);
|
|
842
|
+
} else {
|
|
843
|
+
return text;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
exports.parseTag = parseTag;
|
|
848
|
+
exports.parseAttr = parseAttr;
|
|
849
|
+
|
|
850
|
+
},{"./util":4}],4:[function(require,module,exports){
|
|
851
|
+
module.exports = {
|
|
852
|
+
indexOf: function (arr, item) {
|
|
853
|
+
var i, j;
|
|
854
|
+
if (Array.prototype.indexOf) {
|
|
855
|
+
return arr.indexOf(item);
|
|
856
|
+
}
|
|
857
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
858
|
+
if (arr[i] === item) {
|
|
859
|
+
return i;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return -1;
|
|
863
|
+
},
|
|
864
|
+
forEach: function (arr, fn, scope) {
|
|
865
|
+
var i, j;
|
|
866
|
+
if (Array.prototype.forEach) {
|
|
867
|
+
return arr.forEach(fn, scope);
|
|
868
|
+
}
|
|
869
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
870
|
+
fn.call(scope, arr[i], i, arr);
|
|
871
|
+
}
|
|
872
|
+
},
|
|
873
|
+
trim: function (str) {
|
|
874
|
+
if (String.prototype.trim) {
|
|
875
|
+
return str.trim();
|
|
876
|
+
}
|
|
877
|
+
return str.replace(/(^\s*)|(\s*$)/g, "");
|
|
878
|
+
},
|
|
879
|
+
spaceIndex: function (str) {
|
|
880
|
+
var reg = /\s|\n|\t/;
|
|
881
|
+
var match = reg.exec(str);
|
|
882
|
+
return match ? match.index : -1;
|
|
883
|
+
},
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
},{}],5:[function(require,module,exports){
|
|
887
|
+
/**
|
|
888
|
+
* filter xss
|
|
889
|
+
*
|
|
890
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
891
|
+
*/
|
|
892
|
+
|
|
893
|
+
var FilterCSS = require("cssfilter").FilterCSS;
|
|
894
|
+
var DEFAULT = require("./default");
|
|
895
|
+
var parser = require("./parser");
|
|
896
|
+
var parseTag = parser.parseTag;
|
|
897
|
+
var parseAttr = parser.parseAttr;
|
|
898
|
+
var _ = require("./util");
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* returns `true` if the input value is `undefined` or `null`
|
|
902
|
+
*
|
|
903
|
+
* @param {Object} obj
|
|
904
|
+
* @return {Boolean}
|
|
905
|
+
*/
|
|
906
|
+
function isNull(obj) {
|
|
907
|
+
return obj === undefined || obj === null;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* get attributes for a tag
|
|
912
|
+
*
|
|
913
|
+
* @param {String} html
|
|
914
|
+
* @return {Object}
|
|
915
|
+
* - {String} html
|
|
916
|
+
* - {Boolean} closing
|
|
917
|
+
*/
|
|
918
|
+
function getAttrs(html) {
|
|
919
|
+
var i = _.spaceIndex(html);
|
|
920
|
+
if (i === -1) {
|
|
921
|
+
return {
|
|
922
|
+
html: "",
|
|
923
|
+
closing: html[html.length - 2] === "/",
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
html = _.trim(html.slice(i + 1, -1));
|
|
927
|
+
var isClosing = html[html.length - 1] === "/";
|
|
928
|
+
if (isClosing) html = _.trim(html.slice(0, -1));
|
|
929
|
+
return {
|
|
930
|
+
html: html,
|
|
931
|
+
closing: isClosing,
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* shallow copy
|
|
937
|
+
*
|
|
938
|
+
* @param {Object} obj
|
|
939
|
+
* @return {Object}
|
|
940
|
+
*/
|
|
941
|
+
function shallowCopyObject(obj) {
|
|
942
|
+
var ret = {};
|
|
943
|
+
for (var i in obj) {
|
|
944
|
+
if (Object.prototype.hasOwnProperty.call(obj, i)) {
|
|
945
|
+
ret[i] = obj[i];
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return ret;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function keysToLowerCase(obj) {
|
|
952
|
+
var ret = {};
|
|
953
|
+
for (var i in obj) {
|
|
954
|
+
if (Object.prototype.hasOwnProperty.call(obj, i)) {
|
|
955
|
+
if (Array.isArray(obj[i])) {
|
|
956
|
+
ret[i.toLowerCase()] = obj[i].map(function (item) {
|
|
957
|
+
return item.toLowerCase();
|
|
958
|
+
});
|
|
959
|
+
} else {
|
|
960
|
+
ret[i.toLowerCase()] = obj[i];
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return ret;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* FilterXSS class
|
|
969
|
+
*
|
|
970
|
+
* @param {Object} options
|
|
971
|
+
* whiteList (or allowList), onTag, onTagAttr, onIgnoreTag,
|
|
972
|
+
* onIgnoreTagAttr, safeAttrValue, escapeHtml
|
|
973
|
+
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
|
|
974
|
+
* css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
|
|
975
|
+
*/
|
|
976
|
+
function FilterXSS(options) {
|
|
977
|
+
options = shallowCopyObject(options || {});
|
|
978
|
+
|
|
979
|
+
if (Object.prototype.hasOwnProperty.call(options, 'stripIgnoreTag') && options.stripIgnoreTag) {
|
|
980
|
+
if (Object.prototype.hasOwnProperty.call(options, 'onIgnoreTag') && options.onIgnoreTag) {
|
|
981
|
+
console.error(
|
|
982
|
+
'Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;
|
|
986
|
+
}
|
|
987
|
+
if (Object.prototype.hasOwnProperty.call(options, 'whiteList') || Object.prototype.hasOwnProperty.call(options, 'allowList')) {
|
|
988
|
+
const wl = Object.prototype.hasOwnProperty.call(options, 'whiteList') ? options.whiteList : options.allowList;
|
|
989
|
+
options.whiteList = keysToLowerCase(wl);
|
|
990
|
+
} else {
|
|
991
|
+
options.whiteList = DEFAULT.whiteList;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
this.attributeWrapSign = Object.prototype.hasOwnProperty.call(options, 'singleQuotedAttributeValue') && options.singleQuotedAttributeValue === true ? "'" : DEFAULT.attributeWrapSign;
|
|
995
|
+
|
|
996
|
+
options.onTag = Object.prototype.hasOwnProperty.call(options, 'onTag') ? options.onTag : DEFAULT.onTag;
|
|
997
|
+
options.onTagAttr = Object.prototype.hasOwnProperty.call(options, 'onTagAttr') ? options.onTagAttr : DEFAULT.onTagAttr;
|
|
998
|
+
options.onIgnoreTag = Object.prototype.hasOwnProperty.call(options, 'onIgnoreTag') ? options.onIgnoreTag : DEFAULT.onIgnoreTag;
|
|
999
|
+
options.onIgnoreTagAttr = Object.prototype.hasOwnProperty.call(options, 'onIgnoreTagAttr') ? options.onIgnoreTagAttr : DEFAULT.onIgnoreTagAttr;
|
|
1000
|
+
options.safeAttrValue = Object.prototype.hasOwnProperty.call(options, 'safeAttrValue') ? options.safeAttrValue : DEFAULT.safeAttrValue;
|
|
1001
|
+
options.escapeHtml = Object.prototype.hasOwnProperty.call(options, 'escapeHtml') ? options.escapeHtml : DEFAULT.escapeHtml;
|
|
1002
|
+
this.options = options;
|
|
1003
|
+
|
|
1004
|
+
if (Object.prototype.hasOwnProperty.call(options, 'css') && options.css === false) {
|
|
1005
|
+
this.cssFilter = false;
|
|
1006
|
+
} else {
|
|
1007
|
+
options.css = Object.prototype.hasOwnProperty.call(options, 'css') ? options.css : {};
|
|
1008
|
+
this.cssFilter = new FilterCSS(options.css);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* start process and returns result
|
|
1014
|
+
*
|
|
1015
|
+
* @param {String} html
|
|
1016
|
+
* @return {String}
|
|
1017
|
+
*/
|
|
1018
|
+
FilterXSS.prototype.process = function (html) {
|
|
1019
|
+
// compatible with the input
|
|
1020
|
+
html = html || "";
|
|
1021
|
+
html = html.toString();
|
|
1022
|
+
if (!html) return "";
|
|
1023
|
+
|
|
1024
|
+
var me = this;
|
|
1025
|
+
var options = me.options;
|
|
1026
|
+
var whiteList = options.whiteList;
|
|
1027
|
+
var onTag = options.onTag;
|
|
1028
|
+
var onIgnoreTag = options.onIgnoreTag;
|
|
1029
|
+
var onTagAttr = options.onTagAttr;
|
|
1030
|
+
var onIgnoreTagAttr = options.onIgnoreTagAttr;
|
|
1031
|
+
var safeAttrValue = options.safeAttrValue;
|
|
1032
|
+
var escapeHtml = options.escapeHtml;
|
|
1033
|
+
var attributeWrapSign = me.attributeWrapSign;
|
|
1034
|
+
var cssFilter = me.cssFilter;
|
|
1035
|
+
|
|
1036
|
+
// remove invisible characters
|
|
1037
|
+
if (options.stripBlankChar) {
|
|
1038
|
+
html = DEFAULT.stripBlankChar(html);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// remove html comments
|
|
1042
|
+
if (!options.allowCommentTag) {
|
|
1043
|
+
html = DEFAULT.stripCommentTag(html);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// if enable stripIgnoreTagBody
|
|
1047
|
+
var stripIgnoreTagBody = false;
|
|
1048
|
+
if (options.stripIgnoreTagBody) {
|
|
1049
|
+
stripIgnoreTagBody = DEFAULT.StripTagBody(
|
|
1050
|
+
options.stripIgnoreTagBody,
|
|
1051
|
+
onIgnoreTag
|
|
1052
|
+
);
|
|
1053
|
+
onIgnoreTag = stripIgnoreTagBody.onIgnoreTag;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
var retHtml = parseTag(
|
|
1057
|
+
html,
|
|
1058
|
+
function (sourcePosition, position, tag, html, isClosing) {
|
|
1059
|
+
var info = {
|
|
1060
|
+
sourcePosition: sourcePosition,
|
|
1061
|
+
position: position,
|
|
1062
|
+
isClosing: isClosing,
|
|
1063
|
+
isWhite: Object.prototype.hasOwnProperty.call(whiteList, tag),
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
// call `onTag()`
|
|
1067
|
+
var ret = onTag(tag, html, info);
|
|
1068
|
+
if (!isNull(ret)) return ret;
|
|
1069
|
+
|
|
1070
|
+
if (info.isWhite) {
|
|
1071
|
+
if (info.isClosing) {
|
|
1072
|
+
return "</" + tag + ">";
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
var attrs = getAttrs(html);
|
|
1076
|
+
var whiteAttrList = whiteList[tag];
|
|
1077
|
+
var attrsHtml = parseAttr(attrs.html, function (name, value) {
|
|
1078
|
+
// call `onTagAttr()`
|
|
1079
|
+
var isWhiteAttr = _.indexOf(whiteAttrList, name) !== -1;
|
|
1080
|
+
var ret = onTagAttr(tag, name, value, isWhiteAttr);
|
|
1081
|
+
if (!isNull(ret)) return ret;
|
|
1082
|
+
|
|
1083
|
+
if (isWhiteAttr) {
|
|
1084
|
+
// call `safeAttrValue()`
|
|
1085
|
+
value = safeAttrValue(tag, name, value, cssFilter);
|
|
1086
|
+
if (value) {
|
|
1087
|
+
return name + '=' + attributeWrapSign + value + attributeWrapSign;
|
|
1088
|
+
} else {
|
|
1089
|
+
return name;
|
|
1090
|
+
}
|
|
1091
|
+
} else {
|
|
1092
|
+
// call `onIgnoreTagAttr()`
|
|
1093
|
+
ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
|
|
1094
|
+
if (!isNull(ret)) return ret;
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
// build new tag html
|
|
1100
|
+
html = "<" + tag;
|
|
1101
|
+
if (attrsHtml) html += " " + attrsHtml;
|
|
1102
|
+
if (attrs.closing) html += " /";
|
|
1103
|
+
html += ">";
|
|
1104
|
+
return html;
|
|
1105
|
+
} else {
|
|
1106
|
+
// call `onIgnoreTag()`
|
|
1107
|
+
ret = onIgnoreTag(tag, html, info);
|
|
1108
|
+
if (!isNull(ret)) return ret;
|
|
1109
|
+
return escapeHtml(html);
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
escapeHtml
|
|
1113
|
+
);
|
|
1114
|
+
|
|
1115
|
+
// if enable stripIgnoreTagBody
|
|
1116
|
+
if (stripIgnoreTagBody) {
|
|
1117
|
+
retHtml = stripIgnoreTagBody.remove(retHtml);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
return retHtml;
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
module.exports = FilterXSS;
|
|
1124
|
+
|
|
1125
|
+
},{"./default":1,"./parser":3,"./util":4,"cssfilter":8}],6:[function(require,module,exports){
|
|
1126
|
+
/**
|
|
1127
|
+
* cssfilter
|
|
1128
|
+
*
|
|
1129
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
1130
|
+
*/
|
|
1131
|
+
|
|
1132
|
+
var DEFAULT = require('./default');
|
|
1133
|
+
var parseStyle = require('./parser');
|
|
1134
|
+
var _ = require('./util');
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* 返回值是否为空
|
|
1139
|
+
*
|
|
1140
|
+
* @param {Object} obj
|
|
1141
|
+
* @return {Boolean}
|
|
1142
|
+
*/
|
|
1143
|
+
function isNull (obj) {
|
|
1144
|
+
return (obj === undefined || obj === null);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* 浅拷贝对象
|
|
1149
|
+
*
|
|
1150
|
+
* @param {Object} obj
|
|
1151
|
+
* @return {Object}
|
|
1152
|
+
*/
|
|
1153
|
+
function shallowCopyObject (obj) {
|
|
1154
|
+
var ret = {};
|
|
1155
|
+
for (var i in obj) {
|
|
1156
|
+
ret[i] = obj[i];
|
|
1157
|
+
}
|
|
1158
|
+
return ret;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* 创建CSS过滤器
|
|
1163
|
+
*
|
|
1164
|
+
* @param {Object} options
|
|
1165
|
+
* - {Object} whiteList
|
|
1166
|
+
* - {Function} onAttr
|
|
1167
|
+
* - {Function} onIgnoreAttr
|
|
1168
|
+
* - {Function} safeAttrValue
|
|
1169
|
+
*/
|
|
1170
|
+
function FilterCSS (options) {
|
|
1171
|
+
options = shallowCopyObject(options || {});
|
|
1172
|
+
options.whiteList = options.whiteList || DEFAULT.whiteList;
|
|
1173
|
+
options.onAttr = options.onAttr || DEFAULT.onAttr;
|
|
1174
|
+
options.onIgnoreAttr = options.onIgnoreAttr || DEFAULT.onIgnoreAttr;
|
|
1175
|
+
options.safeAttrValue = options.safeAttrValue || DEFAULT.safeAttrValue;
|
|
1176
|
+
this.options = options;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
FilterCSS.prototype.process = function (css) {
|
|
1180
|
+
// 兼容各种奇葩输入
|
|
1181
|
+
css = css || '';
|
|
1182
|
+
css = css.toString();
|
|
1183
|
+
if (!css) return '';
|
|
1184
|
+
|
|
1185
|
+
var me = this;
|
|
1186
|
+
var options = me.options;
|
|
1187
|
+
var whiteList = options.whiteList;
|
|
1188
|
+
var onAttr = options.onAttr;
|
|
1189
|
+
var onIgnoreAttr = options.onIgnoreAttr;
|
|
1190
|
+
var safeAttrValue = options.safeAttrValue;
|
|
1191
|
+
|
|
1192
|
+
var retCSS = parseStyle(css, function (sourcePosition, position, name, value, source) {
|
|
1193
|
+
|
|
1194
|
+
var check = whiteList[name];
|
|
1195
|
+
var isWhite = false;
|
|
1196
|
+
if (check === true) isWhite = check;
|
|
1197
|
+
else if (typeof check === 'function') isWhite = check(value);
|
|
1198
|
+
else if (check instanceof RegExp) isWhite = check.test(value);
|
|
1199
|
+
if (isWhite !== true) isWhite = false;
|
|
1200
|
+
|
|
1201
|
+
// 如果过滤后 value 为空则直接忽略
|
|
1202
|
+
value = safeAttrValue(name, value);
|
|
1203
|
+
if (!value) return;
|
|
1204
|
+
|
|
1205
|
+
var opts = {
|
|
1206
|
+
position: position,
|
|
1207
|
+
sourcePosition: sourcePosition,
|
|
1208
|
+
source: source,
|
|
1209
|
+
isWhite: isWhite
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
if (isWhite) {
|
|
1213
|
+
|
|
1214
|
+
var ret = onAttr(name, value, opts);
|
|
1215
|
+
if (isNull(ret)) {
|
|
1216
|
+
return name + ':' + value;
|
|
1217
|
+
} else {
|
|
1218
|
+
return ret;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
} else {
|
|
1222
|
+
|
|
1223
|
+
var ret = onIgnoreAttr(name, value, opts);
|
|
1224
|
+
if (!isNull(ret)) {
|
|
1225
|
+
return ret;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
return retCSS;
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
module.exports = FilterCSS;
|
|
1236
|
+
|
|
1237
|
+
},{"./default":7,"./parser":9,"./util":10}],7:[function(require,module,exports){
|
|
1238
|
+
/**
|
|
1239
|
+
* cssfilter
|
|
1240
|
+
*
|
|
1241
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
1242
|
+
*/
|
|
1243
|
+
|
|
1244
|
+
function getDefaultWhiteList () {
|
|
1245
|
+
// 白名单值说明:
|
|
1246
|
+
// true: 允许该属性
|
|
1247
|
+
// Function: function (val) { } 返回true表示允许该属性,其他值均表示不允许
|
|
1248
|
+
// RegExp: regexp.test(val) 返回true表示允许该属性,其他值均表示不允许
|
|
1249
|
+
// 除上面列出的值外均表示不允许
|
|
1250
|
+
var whiteList = {};
|
|
1251
|
+
|
|
1252
|
+
whiteList['align-content'] = false; // default: auto
|
|
1253
|
+
whiteList['align-items'] = false; // default: auto
|
|
1254
|
+
whiteList['align-self'] = false; // default: auto
|
|
1255
|
+
whiteList['alignment-adjust'] = false; // default: auto
|
|
1256
|
+
whiteList['alignment-baseline'] = false; // default: baseline
|
|
1257
|
+
whiteList['all'] = false; // default: depending on individual properties
|
|
1258
|
+
whiteList['anchor-point'] = false; // default: none
|
|
1259
|
+
whiteList['animation'] = false; // default: depending on individual properties
|
|
1260
|
+
whiteList['animation-delay'] = false; // default: 0
|
|
1261
|
+
whiteList['animation-direction'] = false; // default: normal
|
|
1262
|
+
whiteList['animation-duration'] = false; // default: 0
|
|
1263
|
+
whiteList['animation-fill-mode'] = false; // default: none
|
|
1264
|
+
whiteList['animation-iteration-count'] = false; // default: 1
|
|
1265
|
+
whiteList['animation-name'] = false; // default: none
|
|
1266
|
+
whiteList['animation-play-state'] = false; // default: running
|
|
1267
|
+
whiteList['animation-timing-function'] = false; // default: ease
|
|
1268
|
+
whiteList['azimuth'] = false; // default: center
|
|
1269
|
+
whiteList['backface-visibility'] = false; // default: visible
|
|
1270
|
+
whiteList['background'] = true; // default: depending on individual properties
|
|
1271
|
+
whiteList['background-attachment'] = true; // default: scroll
|
|
1272
|
+
whiteList['background-clip'] = true; // default: border-box
|
|
1273
|
+
whiteList['background-color'] = true; // default: transparent
|
|
1274
|
+
whiteList['background-image'] = true; // default: none
|
|
1275
|
+
whiteList['background-origin'] = true; // default: padding-box
|
|
1276
|
+
whiteList['background-position'] = true; // default: 0% 0%
|
|
1277
|
+
whiteList['background-repeat'] = true; // default: repeat
|
|
1278
|
+
whiteList['background-size'] = true; // default: auto
|
|
1279
|
+
whiteList['baseline-shift'] = false; // default: baseline
|
|
1280
|
+
whiteList['binding'] = false; // default: none
|
|
1281
|
+
whiteList['bleed'] = false; // default: 6pt
|
|
1282
|
+
whiteList['bookmark-label'] = false; // default: content()
|
|
1283
|
+
whiteList['bookmark-level'] = false; // default: none
|
|
1284
|
+
whiteList['bookmark-state'] = false; // default: open
|
|
1285
|
+
whiteList['border'] = true; // default: depending on individual properties
|
|
1286
|
+
whiteList['border-bottom'] = true; // default: depending on individual properties
|
|
1287
|
+
whiteList['border-bottom-color'] = true; // default: current color
|
|
1288
|
+
whiteList['border-bottom-left-radius'] = true; // default: 0
|
|
1289
|
+
whiteList['border-bottom-right-radius'] = true; // default: 0
|
|
1290
|
+
whiteList['border-bottom-style'] = true; // default: none
|
|
1291
|
+
whiteList['border-bottom-width'] = true; // default: medium
|
|
1292
|
+
whiteList['border-collapse'] = true; // default: separate
|
|
1293
|
+
whiteList['border-color'] = true; // default: depending on individual properties
|
|
1294
|
+
whiteList['border-image'] = true; // default: none
|
|
1295
|
+
whiteList['border-image-outset'] = true; // default: 0
|
|
1296
|
+
whiteList['border-image-repeat'] = true; // default: stretch
|
|
1297
|
+
whiteList['border-image-slice'] = true; // default: 100%
|
|
1298
|
+
whiteList['border-image-source'] = true; // default: none
|
|
1299
|
+
whiteList['border-image-width'] = true; // default: 1
|
|
1300
|
+
whiteList['border-left'] = true; // default: depending on individual properties
|
|
1301
|
+
whiteList['border-left-color'] = true; // default: current color
|
|
1302
|
+
whiteList['border-left-style'] = true; // default: none
|
|
1303
|
+
whiteList['border-left-width'] = true; // default: medium
|
|
1304
|
+
whiteList['border-radius'] = true; // default: 0
|
|
1305
|
+
whiteList['border-right'] = true; // default: depending on individual properties
|
|
1306
|
+
whiteList['border-right-color'] = true; // default: current color
|
|
1307
|
+
whiteList['border-right-style'] = true; // default: none
|
|
1308
|
+
whiteList['border-right-width'] = true; // default: medium
|
|
1309
|
+
whiteList['border-spacing'] = true; // default: 0
|
|
1310
|
+
whiteList['border-style'] = true; // default: depending on individual properties
|
|
1311
|
+
whiteList['border-top'] = true; // default: depending on individual properties
|
|
1312
|
+
whiteList['border-top-color'] = true; // default: current color
|
|
1313
|
+
whiteList['border-top-left-radius'] = true; // default: 0
|
|
1314
|
+
whiteList['border-top-right-radius'] = true; // default: 0
|
|
1315
|
+
whiteList['border-top-style'] = true; // default: none
|
|
1316
|
+
whiteList['border-top-width'] = true; // default: medium
|
|
1317
|
+
whiteList['border-width'] = true; // default: depending on individual properties
|
|
1318
|
+
whiteList['bottom'] = false; // default: auto
|
|
1319
|
+
whiteList['box-decoration-break'] = true; // default: slice
|
|
1320
|
+
whiteList['box-shadow'] = true; // default: none
|
|
1321
|
+
whiteList['box-sizing'] = true; // default: content-box
|
|
1322
|
+
whiteList['box-snap'] = true; // default: none
|
|
1323
|
+
whiteList['box-suppress'] = true; // default: show
|
|
1324
|
+
whiteList['break-after'] = true; // default: auto
|
|
1325
|
+
whiteList['break-before'] = true; // default: auto
|
|
1326
|
+
whiteList['break-inside'] = true; // default: auto
|
|
1327
|
+
whiteList['caption-side'] = false; // default: top
|
|
1328
|
+
whiteList['chains'] = false; // default: none
|
|
1329
|
+
whiteList['clear'] = true; // default: none
|
|
1330
|
+
whiteList['clip'] = false; // default: auto
|
|
1331
|
+
whiteList['clip-path'] = false; // default: none
|
|
1332
|
+
whiteList['clip-rule'] = false; // default: nonzero
|
|
1333
|
+
whiteList['color'] = true; // default: implementation dependent
|
|
1334
|
+
whiteList['color-interpolation-filters'] = true; // default: auto
|
|
1335
|
+
whiteList['column-count'] = false; // default: auto
|
|
1336
|
+
whiteList['column-fill'] = false; // default: balance
|
|
1337
|
+
whiteList['column-gap'] = false; // default: normal
|
|
1338
|
+
whiteList['column-rule'] = false; // default: depending on individual properties
|
|
1339
|
+
whiteList['column-rule-color'] = false; // default: current color
|
|
1340
|
+
whiteList['column-rule-style'] = false; // default: medium
|
|
1341
|
+
whiteList['column-rule-width'] = false; // default: medium
|
|
1342
|
+
whiteList['column-span'] = false; // default: none
|
|
1343
|
+
whiteList['column-width'] = false; // default: auto
|
|
1344
|
+
whiteList['columns'] = false; // default: depending on individual properties
|
|
1345
|
+
whiteList['contain'] = false; // default: none
|
|
1346
|
+
whiteList['content'] = false; // default: normal
|
|
1347
|
+
whiteList['counter-increment'] = false; // default: none
|
|
1348
|
+
whiteList['counter-reset'] = false; // default: none
|
|
1349
|
+
whiteList['counter-set'] = false; // default: none
|
|
1350
|
+
whiteList['crop'] = false; // default: auto
|
|
1351
|
+
whiteList['cue'] = false; // default: depending on individual properties
|
|
1352
|
+
whiteList['cue-after'] = false; // default: none
|
|
1353
|
+
whiteList['cue-before'] = false; // default: none
|
|
1354
|
+
whiteList['cursor'] = false; // default: auto
|
|
1355
|
+
whiteList['direction'] = false; // default: ltr
|
|
1356
|
+
whiteList['display'] = true; // default: depending on individual properties
|
|
1357
|
+
whiteList['display-inside'] = true; // default: auto
|
|
1358
|
+
whiteList['display-list'] = true; // default: none
|
|
1359
|
+
whiteList['display-outside'] = true; // default: inline-level
|
|
1360
|
+
whiteList['dominant-baseline'] = false; // default: auto
|
|
1361
|
+
whiteList['elevation'] = false; // default: level
|
|
1362
|
+
whiteList['empty-cells'] = false; // default: show
|
|
1363
|
+
whiteList['filter'] = false; // default: none
|
|
1364
|
+
whiteList['flex'] = false; // default: depending on individual properties
|
|
1365
|
+
whiteList['flex-basis'] = false; // default: auto
|
|
1366
|
+
whiteList['flex-direction'] = false; // default: row
|
|
1367
|
+
whiteList['flex-flow'] = false; // default: depending on individual properties
|
|
1368
|
+
whiteList['flex-grow'] = false; // default: 0
|
|
1369
|
+
whiteList['flex-shrink'] = false; // default: 1
|
|
1370
|
+
whiteList['flex-wrap'] = false; // default: nowrap
|
|
1371
|
+
whiteList['float'] = false; // default: none
|
|
1372
|
+
whiteList['float-offset'] = false; // default: 0 0
|
|
1373
|
+
whiteList['flood-color'] = false; // default: black
|
|
1374
|
+
whiteList['flood-opacity'] = false; // default: 1
|
|
1375
|
+
whiteList['flow-from'] = false; // default: none
|
|
1376
|
+
whiteList['flow-into'] = false; // default: none
|
|
1377
|
+
whiteList['font'] = true; // default: depending on individual properties
|
|
1378
|
+
whiteList['font-family'] = true; // default: implementation dependent
|
|
1379
|
+
whiteList['font-feature-settings'] = true; // default: normal
|
|
1380
|
+
whiteList['font-kerning'] = true; // default: auto
|
|
1381
|
+
whiteList['font-language-override'] = true; // default: normal
|
|
1382
|
+
whiteList['font-size'] = true; // default: medium
|
|
1383
|
+
whiteList['font-size-adjust'] = true; // default: none
|
|
1384
|
+
whiteList['font-stretch'] = true; // default: normal
|
|
1385
|
+
whiteList['font-style'] = true; // default: normal
|
|
1386
|
+
whiteList['font-synthesis'] = true; // default: weight style
|
|
1387
|
+
whiteList['font-variant'] = true; // default: normal
|
|
1388
|
+
whiteList['font-variant-alternates'] = true; // default: normal
|
|
1389
|
+
whiteList['font-variant-caps'] = true; // default: normal
|
|
1390
|
+
whiteList['font-variant-east-asian'] = true; // default: normal
|
|
1391
|
+
whiteList['font-variant-ligatures'] = true; // default: normal
|
|
1392
|
+
whiteList['font-variant-numeric'] = true; // default: normal
|
|
1393
|
+
whiteList['font-variant-position'] = true; // default: normal
|
|
1394
|
+
whiteList['font-weight'] = true; // default: normal
|
|
1395
|
+
whiteList['grid'] = false; // default: depending on individual properties
|
|
1396
|
+
whiteList['grid-area'] = false; // default: depending on individual properties
|
|
1397
|
+
whiteList['grid-auto-columns'] = false; // default: auto
|
|
1398
|
+
whiteList['grid-auto-flow'] = false; // default: none
|
|
1399
|
+
whiteList['grid-auto-rows'] = false; // default: auto
|
|
1400
|
+
whiteList['grid-column'] = false; // default: depending on individual properties
|
|
1401
|
+
whiteList['grid-column-end'] = false; // default: auto
|
|
1402
|
+
whiteList['grid-column-start'] = false; // default: auto
|
|
1403
|
+
whiteList['grid-row'] = false; // default: depending on individual properties
|
|
1404
|
+
whiteList['grid-row-end'] = false; // default: auto
|
|
1405
|
+
whiteList['grid-row-start'] = false; // default: auto
|
|
1406
|
+
whiteList['grid-template'] = false; // default: depending on individual properties
|
|
1407
|
+
whiteList['grid-template-areas'] = false; // default: none
|
|
1408
|
+
whiteList['grid-template-columns'] = false; // default: none
|
|
1409
|
+
whiteList['grid-template-rows'] = false; // default: none
|
|
1410
|
+
whiteList['hanging-punctuation'] = false; // default: none
|
|
1411
|
+
whiteList['height'] = true; // default: auto
|
|
1412
|
+
whiteList['hyphens'] = false; // default: manual
|
|
1413
|
+
whiteList['icon'] = false; // default: auto
|
|
1414
|
+
whiteList['image-orientation'] = false; // default: auto
|
|
1415
|
+
whiteList['image-resolution'] = false; // default: normal
|
|
1416
|
+
whiteList['ime-mode'] = false; // default: auto
|
|
1417
|
+
whiteList['initial-letters'] = false; // default: normal
|
|
1418
|
+
whiteList['inline-box-align'] = false; // default: last
|
|
1419
|
+
whiteList['justify-content'] = false; // default: auto
|
|
1420
|
+
whiteList['justify-items'] = false; // default: auto
|
|
1421
|
+
whiteList['justify-self'] = false; // default: auto
|
|
1422
|
+
whiteList['left'] = false; // default: auto
|
|
1423
|
+
whiteList['letter-spacing'] = true; // default: normal
|
|
1424
|
+
whiteList['lighting-color'] = true; // default: white
|
|
1425
|
+
whiteList['line-box-contain'] = false; // default: block inline replaced
|
|
1426
|
+
whiteList['line-break'] = false; // default: auto
|
|
1427
|
+
whiteList['line-grid'] = false; // default: match-parent
|
|
1428
|
+
whiteList['line-height'] = false; // default: normal
|
|
1429
|
+
whiteList['line-snap'] = false; // default: none
|
|
1430
|
+
whiteList['line-stacking'] = false; // default: depending on individual properties
|
|
1431
|
+
whiteList['line-stacking-ruby'] = false; // default: exclude-ruby
|
|
1432
|
+
whiteList['line-stacking-shift'] = false; // default: consider-shifts
|
|
1433
|
+
whiteList['line-stacking-strategy'] = false; // default: inline-line-height
|
|
1434
|
+
whiteList['list-style'] = true; // default: depending on individual properties
|
|
1435
|
+
whiteList['list-style-image'] = true; // default: none
|
|
1436
|
+
whiteList['list-style-position'] = true; // default: outside
|
|
1437
|
+
whiteList['list-style-type'] = true; // default: disc
|
|
1438
|
+
whiteList['margin'] = true; // default: depending on individual properties
|
|
1439
|
+
whiteList['margin-bottom'] = true; // default: 0
|
|
1440
|
+
whiteList['margin-left'] = true; // default: 0
|
|
1441
|
+
whiteList['margin-right'] = true; // default: 0
|
|
1442
|
+
whiteList['margin-top'] = true; // default: 0
|
|
1443
|
+
whiteList['marker-offset'] = false; // default: auto
|
|
1444
|
+
whiteList['marker-side'] = false; // default: list-item
|
|
1445
|
+
whiteList['marks'] = false; // default: none
|
|
1446
|
+
whiteList['mask'] = false; // default: border-box
|
|
1447
|
+
whiteList['mask-box'] = false; // default: see individual properties
|
|
1448
|
+
whiteList['mask-box-outset'] = false; // default: 0
|
|
1449
|
+
whiteList['mask-box-repeat'] = false; // default: stretch
|
|
1450
|
+
whiteList['mask-box-slice'] = false; // default: 0 fill
|
|
1451
|
+
whiteList['mask-box-source'] = false; // default: none
|
|
1452
|
+
whiteList['mask-box-width'] = false; // default: auto
|
|
1453
|
+
whiteList['mask-clip'] = false; // default: border-box
|
|
1454
|
+
whiteList['mask-image'] = false; // default: none
|
|
1455
|
+
whiteList['mask-origin'] = false; // default: border-box
|
|
1456
|
+
whiteList['mask-position'] = false; // default: center
|
|
1457
|
+
whiteList['mask-repeat'] = false; // default: no-repeat
|
|
1458
|
+
whiteList['mask-size'] = false; // default: border-box
|
|
1459
|
+
whiteList['mask-source-type'] = false; // default: auto
|
|
1460
|
+
whiteList['mask-type'] = false; // default: luminance
|
|
1461
|
+
whiteList['max-height'] = true; // default: none
|
|
1462
|
+
whiteList['max-lines'] = false; // default: none
|
|
1463
|
+
whiteList['max-width'] = true; // default: none
|
|
1464
|
+
whiteList['min-height'] = true; // default: 0
|
|
1465
|
+
whiteList['min-width'] = true; // default: 0
|
|
1466
|
+
whiteList['move-to'] = false; // default: normal
|
|
1467
|
+
whiteList['nav-down'] = false; // default: auto
|
|
1468
|
+
whiteList['nav-index'] = false; // default: auto
|
|
1469
|
+
whiteList['nav-left'] = false; // default: auto
|
|
1470
|
+
whiteList['nav-right'] = false; // default: auto
|
|
1471
|
+
whiteList['nav-up'] = false; // default: auto
|
|
1472
|
+
whiteList['object-fit'] = false; // default: fill
|
|
1473
|
+
whiteList['object-position'] = false; // default: 50% 50%
|
|
1474
|
+
whiteList['opacity'] = false; // default: 1
|
|
1475
|
+
whiteList['order'] = false; // default: 0
|
|
1476
|
+
whiteList['orphans'] = false; // default: 2
|
|
1477
|
+
whiteList['outline'] = false; // default: depending on individual properties
|
|
1478
|
+
whiteList['outline-color'] = false; // default: invert
|
|
1479
|
+
whiteList['outline-offset'] = false; // default: 0
|
|
1480
|
+
whiteList['outline-style'] = false; // default: none
|
|
1481
|
+
whiteList['outline-width'] = false; // default: medium
|
|
1482
|
+
whiteList['overflow'] = false; // default: depending on individual properties
|
|
1483
|
+
whiteList['overflow-wrap'] = false; // default: normal
|
|
1484
|
+
whiteList['overflow-x'] = false; // default: visible
|
|
1485
|
+
whiteList['overflow-y'] = false; // default: visible
|
|
1486
|
+
whiteList['padding'] = true; // default: depending on individual properties
|
|
1487
|
+
whiteList['padding-bottom'] = true; // default: 0
|
|
1488
|
+
whiteList['padding-left'] = true; // default: 0
|
|
1489
|
+
whiteList['padding-right'] = true; // default: 0
|
|
1490
|
+
whiteList['padding-top'] = true; // default: 0
|
|
1491
|
+
whiteList['page'] = false; // default: auto
|
|
1492
|
+
whiteList['page-break-after'] = false; // default: auto
|
|
1493
|
+
whiteList['page-break-before'] = false; // default: auto
|
|
1494
|
+
whiteList['page-break-inside'] = false; // default: auto
|
|
1495
|
+
whiteList['page-policy'] = false; // default: start
|
|
1496
|
+
whiteList['pause'] = false; // default: implementation dependent
|
|
1497
|
+
whiteList['pause-after'] = false; // default: implementation dependent
|
|
1498
|
+
whiteList['pause-before'] = false; // default: implementation dependent
|
|
1499
|
+
whiteList['perspective'] = false; // default: none
|
|
1500
|
+
whiteList['perspective-origin'] = false; // default: 50% 50%
|
|
1501
|
+
whiteList['pitch'] = false; // default: medium
|
|
1502
|
+
whiteList['pitch-range'] = false; // default: 50
|
|
1503
|
+
whiteList['play-during'] = false; // default: auto
|
|
1504
|
+
whiteList['position'] = false; // default: static
|
|
1505
|
+
whiteList['presentation-level'] = false; // default: 0
|
|
1506
|
+
whiteList['quotes'] = false; // default: text
|
|
1507
|
+
whiteList['region-fragment'] = false; // default: auto
|
|
1508
|
+
whiteList['resize'] = false; // default: none
|
|
1509
|
+
whiteList['rest'] = false; // default: depending on individual properties
|
|
1510
|
+
whiteList['rest-after'] = false; // default: none
|
|
1511
|
+
whiteList['rest-before'] = false; // default: none
|
|
1512
|
+
whiteList['richness'] = false; // default: 50
|
|
1513
|
+
whiteList['right'] = false; // default: auto
|
|
1514
|
+
whiteList['rotation'] = false; // default: 0
|
|
1515
|
+
whiteList['rotation-point'] = false; // default: 50% 50%
|
|
1516
|
+
whiteList['ruby-align'] = false; // default: auto
|
|
1517
|
+
whiteList['ruby-merge'] = false; // default: separate
|
|
1518
|
+
whiteList['ruby-position'] = false; // default: before
|
|
1519
|
+
whiteList['shape-image-threshold'] = false; // default: 0.0
|
|
1520
|
+
whiteList['shape-outside'] = false; // default: none
|
|
1521
|
+
whiteList['shape-margin'] = false; // default: 0
|
|
1522
|
+
whiteList['size'] = false; // default: auto
|
|
1523
|
+
whiteList['speak'] = false; // default: auto
|
|
1524
|
+
whiteList['speak-as'] = false; // default: normal
|
|
1525
|
+
whiteList['speak-header'] = false; // default: once
|
|
1526
|
+
whiteList['speak-numeral'] = false; // default: continuous
|
|
1527
|
+
whiteList['speak-punctuation'] = false; // default: none
|
|
1528
|
+
whiteList['speech-rate'] = false; // default: medium
|
|
1529
|
+
whiteList['stress'] = false; // default: 50
|
|
1530
|
+
whiteList['string-set'] = false; // default: none
|
|
1531
|
+
whiteList['tab-size'] = false; // default: 8
|
|
1532
|
+
whiteList['table-layout'] = false; // default: auto
|
|
1533
|
+
whiteList['text-align'] = true; // default: start
|
|
1534
|
+
whiteList['text-align-last'] = true; // default: auto
|
|
1535
|
+
whiteList['text-combine-upright'] = true; // default: none
|
|
1536
|
+
whiteList['text-decoration'] = true; // default: none
|
|
1537
|
+
whiteList['text-decoration-color'] = true; // default: currentColor
|
|
1538
|
+
whiteList['text-decoration-line'] = true; // default: none
|
|
1539
|
+
whiteList['text-decoration-skip'] = true; // default: objects
|
|
1540
|
+
whiteList['text-decoration-style'] = true; // default: solid
|
|
1541
|
+
whiteList['text-emphasis'] = true; // default: depending on individual properties
|
|
1542
|
+
whiteList['text-emphasis-color'] = true; // default: currentColor
|
|
1543
|
+
whiteList['text-emphasis-position'] = true; // default: over right
|
|
1544
|
+
whiteList['text-emphasis-style'] = true; // default: none
|
|
1545
|
+
whiteList['text-height'] = true; // default: auto
|
|
1546
|
+
whiteList['text-indent'] = true; // default: 0
|
|
1547
|
+
whiteList['text-justify'] = true; // default: auto
|
|
1548
|
+
whiteList['text-orientation'] = true; // default: mixed
|
|
1549
|
+
whiteList['text-overflow'] = true; // default: clip
|
|
1550
|
+
whiteList['text-shadow'] = true; // default: none
|
|
1551
|
+
whiteList['text-space-collapse'] = true; // default: collapse
|
|
1552
|
+
whiteList['text-transform'] = true; // default: none
|
|
1553
|
+
whiteList['text-underline-position'] = true; // default: auto
|
|
1554
|
+
whiteList['text-wrap'] = true; // default: normal
|
|
1555
|
+
whiteList['top'] = false; // default: auto
|
|
1556
|
+
whiteList['transform'] = false; // default: none
|
|
1557
|
+
whiteList['transform-origin'] = false; // default: 50% 50% 0
|
|
1558
|
+
whiteList['transform-style'] = false; // default: flat
|
|
1559
|
+
whiteList['transition'] = false; // default: depending on individual properties
|
|
1560
|
+
whiteList['transition-delay'] = false; // default: 0s
|
|
1561
|
+
whiteList['transition-duration'] = false; // default: 0s
|
|
1562
|
+
whiteList['transition-property'] = false; // default: all
|
|
1563
|
+
whiteList['transition-timing-function'] = false; // default: ease
|
|
1564
|
+
whiteList['unicode-bidi'] = false; // default: normal
|
|
1565
|
+
whiteList['vertical-align'] = false; // default: baseline
|
|
1566
|
+
whiteList['visibility'] = false; // default: visible
|
|
1567
|
+
whiteList['voice-balance'] = false; // default: center
|
|
1568
|
+
whiteList['voice-duration'] = false; // default: auto
|
|
1569
|
+
whiteList['voice-family'] = false; // default: implementation dependent
|
|
1570
|
+
whiteList['voice-pitch'] = false; // default: medium
|
|
1571
|
+
whiteList['voice-range'] = false; // default: medium
|
|
1572
|
+
whiteList['voice-rate'] = false; // default: normal
|
|
1573
|
+
whiteList['voice-stress'] = false; // default: normal
|
|
1574
|
+
whiteList['voice-volume'] = false; // default: medium
|
|
1575
|
+
whiteList['volume'] = false; // default: medium
|
|
1576
|
+
whiteList['white-space'] = false; // default: normal
|
|
1577
|
+
whiteList['widows'] = false; // default: 2
|
|
1578
|
+
whiteList['width'] = true; // default: auto
|
|
1579
|
+
whiteList['will-change'] = false; // default: auto
|
|
1580
|
+
whiteList['word-break'] = true; // default: normal
|
|
1581
|
+
whiteList['word-spacing'] = true; // default: normal
|
|
1582
|
+
whiteList['word-wrap'] = true; // default: normal
|
|
1583
|
+
whiteList['wrap-flow'] = false; // default: auto
|
|
1584
|
+
whiteList['wrap-through'] = false; // default: wrap
|
|
1585
|
+
whiteList['writing-mode'] = false; // default: horizontal-tb
|
|
1586
|
+
whiteList['z-index'] = false; // default: auto
|
|
1587
|
+
|
|
1588
|
+
return whiteList;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
/**
|
|
1593
|
+
* 匹配到白名单上的一个属性时
|
|
1594
|
+
*
|
|
1595
|
+
* @param {String} name
|
|
1596
|
+
* @param {String} value
|
|
1597
|
+
* @param {Object} options
|
|
1598
|
+
* @return {String}
|
|
1599
|
+
*/
|
|
1600
|
+
function onAttr (name, value, options) {
|
|
1601
|
+
// do nothing
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
* 匹配到不在白名单上的一个属性时
|
|
1606
|
+
*
|
|
1607
|
+
* @param {String} name
|
|
1608
|
+
* @param {String} value
|
|
1609
|
+
* @param {Object} options
|
|
1610
|
+
* @return {String}
|
|
1611
|
+
*/
|
|
1612
|
+
function onIgnoreAttr (name, value, options) {
|
|
1613
|
+
// do nothing
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
var REGEXP_URL_JAVASCRIPT = /javascript\s*\:/img;
|
|
1617
|
+
|
|
1618
|
+
/**
|
|
1619
|
+
* 过滤属性值
|
|
1620
|
+
*
|
|
1621
|
+
* @param {String} name
|
|
1622
|
+
* @param {String} value
|
|
1623
|
+
* @return {String}
|
|
1624
|
+
*/
|
|
1625
|
+
function safeAttrValue(name, value) {
|
|
1626
|
+
if (REGEXP_URL_JAVASCRIPT.test(value)) return '';
|
|
1627
|
+
return value;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
|
|
1631
|
+
exports.whiteList = getDefaultWhiteList();
|
|
1632
|
+
exports.getDefaultWhiteList = getDefaultWhiteList;
|
|
1633
|
+
exports.onAttr = onAttr;
|
|
1634
|
+
exports.onIgnoreAttr = onIgnoreAttr;
|
|
1635
|
+
exports.safeAttrValue = safeAttrValue;
|
|
1636
|
+
|
|
1637
|
+
},{}],8:[function(require,module,exports){
|
|
1638
|
+
/**
|
|
1639
|
+
* cssfilter
|
|
1640
|
+
*
|
|
1641
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
1642
|
+
*/
|
|
1643
|
+
|
|
1644
|
+
var DEFAULT = require('./default');
|
|
1645
|
+
var FilterCSS = require('./css');
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* XSS过滤
|
|
1650
|
+
*
|
|
1651
|
+
* @param {String} css 要过滤的CSS代码
|
|
1652
|
+
* @param {Object} options 选项:whiteList, onAttr, onIgnoreAttr
|
|
1653
|
+
* @return {String}
|
|
1654
|
+
*/
|
|
1655
|
+
function filterCSS (html, options) {
|
|
1656
|
+
var xss = new FilterCSS(options);
|
|
1657
|
+
return xss.process(html);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
|
|
1661
|
+
// 输出
|
|
1662
|
+
exports = module.exports = filterCSS;
|
|
1663
|
+
exports.FilterCSS = FilterCSS;
|
|
1664
|
+
for (var i in DEFAULT) exports[i] = DEFAULT[i];
|
|
1665
|
+
|
|
1666
|
+
// 在浏览器端使用
|
|
1667
|
+
if (typeof window !== 'undefined') {
|
|
1668
|
+
window.filterCSS = module.exports;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
},{"./css":6,"./default":7}],9:[function(require,module,exports){
|
|
1672
|
+
/**
|
|
1673
|
+
* cssfilter
|
|
1674
|
+
*
|
|
1675
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
1676
|
+
*/
|
|
1677
|
+
|
|
1678
|
+
var _ = require('./util');
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* 解析style
|
|
1683
|
+
*
|
|
1684
|
+
* @param {String} css
|
|
1685
|
+
* @param {Function} onAttr 处理属性的函数
|
|
1686
|
+
* 参数格式: function (sourcePosition, position, name, value, source)
|
|
1687
|
+
* @return {String}
|
|
1688
|
+
*/
|
|
1689
|
+
function parseStyle (css, onAttr) {
|
|
1690
|
+
css = _.trimRight(css);
|
|
1691
|
+
if (css[css.length - 1] !== ';') css += ';';
|
|
1692
|
+
var cssLength = css.length;
|
|
1693
|
+
var isParenthesisOpen = false;
|
|
1694
|
+
var lastPos = 0;
|
|
1695
|
+
var i = 0;
|
|
1696
|
+
var retCSS = '';
|
|
1697
|
+
|
|
1698
|
+
function addNewAttr () {
|
|
1699
|
+
// 如果没有正常的闭合圆括号,则直接忽略当前属性
|
|
1700
|
+
if (!isParenthesisOpen) {
|
|
1701
|
+
var source = _.trim(css.slice(lastPos, i));
|
|
1702
|
+
var j = source.indexOf(':');
|
|
1703
|
+
if (j !== -1) {
|
|
1704
|
+
var name = _.trim(source.slice(0, j));
|
|
1705
|
+
var value = _.trim(source.slice(j + 1));
|
|
1706
|
+
// 必须有属性名称
|
|
1707
|
+
if (name) {
|
|
1708
|
+
var ret = onAttr(lastPos, retCSS.length, name, value, source);
|
|
1709
|
+
if (ret) retCSS += ret + '; ';
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
lastPos = i + 1;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
for (; i < cssLength; i++) {
|
|
1717
|
+
var c = css[i];
|
|
1718
|
+
if (c === '/' && css[i + 1] === '*') {
|
|
1719
|
+
// 备注开始
|
|
1720
|
+
var j = css.indexOf('*/', i + 2);
|
|
1721
|
+
// 如果没有正常的备注结束,则后面的部分全部跳过
|
|
1722
|
+
if (j === -1) break;
|
|
1723
|
+
// 直接将当前位置调到备注结尾,并且初始化状态
|
|
1724
|
+
i = j + 1;
|
|
1725
|
+
lastPos = i + 1;
|
|
1726
|
+
isParenthesisOpen = false;
|
|
1727
|
+
} else if (c === '(') {
|
|
1728
|
+
isParenthesisOpen = true;
|
|
1729
|
+
} else if (c === ')') {
|
|
1730
|
+
isParenthesisOpen = false;
|
|
1731
|
+
} else if (c === ';') {
|
|
1732
|
+
if (isParenthesisOpen) {
|
|
1733
|
+
// 在圆括号里面,忽略
|
|
1734
|
+
} else {
|
|
1735
|
+
addNewAttr();
|
|
1736
|
+
}
|
|
1737
|
+
} else if (c === '\n') {
|
|
1738
|
+
addNewAttr();
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
return _.trim(retCSS);
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
module.exports = parseStyle;
|
|
1746
|
+
|
|
1747
|
+
},{"./util":10}],10:[function(require,module,exports){
|
|
1748
|
+
module.exports = {
|
|
1749
|
+
indexOf: function (arr, item) {
|
|
1750
|
+
var i, j;
|
|
1751
|
+
if (Array.prototype.indexOf) {
|
|
1752
|
+
return arr.indexOf(item);
|
|
1753
|
+
}
|
|
1754
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
1755
|
+
if (arr[i] === item) {
|
|
1756
|
+
return i;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
return -1;
|
|
1760
|
+
},
|
|
1761
|
+
forEach: function (arr, fn, scope) {
|
|
1762
|
+
var i, j;
|
|
1763
|
+
if (Array.prototype.forEach) {
|
|
1764
|
+
return arr.forEach(fn, scope);
|
|
1765
|
+
}
|
|
1766
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
1767
|
+
fn.call(scope, arr[i], i, arr);
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
trim: function (str) {
|
|
1771
|
+
if (String.prototype.trim) {
|
|
1772
|
+
return str.trim();
|
|
1773
|
+
}
|
|
1774
|
+
return str.replace(/(^\s*)|(\s*$)/g, '');
|
|
1775
|
+
},
|
|
1776
|
+
trimRight: function (str) {
|
|
1777
|
+
if (String.prototype.trimRight) {
|
|
1778
|
+
return str.trimRight();
|
|
1779
|
+
}
|
|
1780
|
+
return str.replace(/(\s*$)/g, '');
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
},{}]},{},[2]);
|