UNPKG

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