insane.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /*
  2. The MIT License (MIT)
  3. Copyright © 2015 Nicolas Bevacqua
  4. Permission is hereby granted, free of charge, to any person obtaining a copy of
  5. this software and associated documentation files (the "Software"), to deal in
  6. the Software without restriction, including without limitation the rights to
  7. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  8. the Software, and to permit persons to whom the Software is furnished to do so,
  9. subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  14. FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  15. COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  16. IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  17. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. */
  19. let __insane_func;
  20. (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 })()({
  21. 1: [function (require, module, exports) {
  22. 'use strict';
  23. var toMap = require('./toMap');
  24. var uris = ['background', 'base', 'cite', 'href', 'longdesc', 'src', 'usemap'];
  25. module.exports = {
  26. uris: toMap(uris) // attributes that have an href and hence need to be sanitized
  27. };
  28. }, { "./toMap": 10 }], 2: [function (require, module, exports) {
  29. 'use strict';
  30. var defaults = {
  31. allowedAttributes: {
  32. '*': ['title', 'accesskey'],
  33. a: ['href', 'name', 'target', 'aria-label'],
  34. iframe: ['allowfullscreen', 'frameborder', 'src'],
  35. img: ['src', 'alt', 'title', 'aria-label']
  36. },
  37. allowedClasses: {},
  38. allowedSchemes: ['http', 'https', 'mailto'],
  39. allowedTags: [
  40. 'a', 'abbr', 'article', 'b', 'blockquote', 'br', 'caption', 'code', 'del', 'details', 'div', 'em',
  41. 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins', 'kbd', 'li', 'main', 'mark',
  42. 'ol', 'p', 'pre', 'section', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'table',
  43. 'tbody', 'td', 'th', 'thead', 'tr', 'u', 'ul'
  44. ],
  45. filter: null
  46. };
  47. module.exports = defaults;
  48. }, {}], 3: [function (require, module, exports) {
  49. 'use strict';
  50. var toMap = require('./toMap');
  51. var voids = ['area', 'br', 'col', 'hr', 'img', 'wbr', 'input', 'base', 'basefont', 'link', 'meta'];
  52. module.exports = {
  53. voids: toMap(voids)
  54. };
  55. }, { "./toMap": 10 }], 4: [function (require, module, exports) {
  56. 'use strict';
  57. var he = require('he');
  58. var assign = require('assignment');
  59. var parser = require('./parser');
  60. var sanitizer = require('./sanitizer');
  61. var defaults = require('./defaults');
  62. function insane(html, options, strict) {
  63. var buffer = [];
  64. var configuration = strict === true ? options : assign({}, defaults, options);
  65. var handler = sanitizer(buffer, configuration);
  66. parser(html, handler);
  67. return buffer.join('');
  68. }
  69. insane.defaults = defaults;
  70. module.exports = insane;
  71. __insane_func = insane;
  72. }, { "./defaults": 2, "./parser": 7, "./sanitizer": 8, "assignment": 6, "he": 9 }], 5: [function (require, module, exports) {
  73. 'use strict';
  74. module.exports = function lowercase(string) {
  75. return typeof string === 'string' ? string.toLowerCase() : string;
  76. };
  77. }, {}], 6: [function (require, module, exports) {
  78. 'use strict';
  79. function assignment(result) {
  80. var stack = Array.prototype.slice.call(arguments, 1);
  81. var item;
  82. var key;
  83. while (stack.length) {
  84. item = stack.shift();
  85. for (key in item) {
  86. if (item.hasOwnProperty(key)) {
  87. if (Object.prototype.toString.call(result[key]) === '[object Object]') {
  88. result[key] = assignment(result[key], item[key]);
  89. } else {
  90. result[key] = item[key];
  91. }
  92. }
  93. }
  94. }
  95. return result;
  96. }
  97. module.exports = assignment;
  98. }, {}], 7: [function (require, module, exports) {
  99. 'use strict';
  100. var he = require('he');
  101. var lowercase = require('./lowercase');
  102. var attributes = require('./attributes');
  103. var elements = require('./elements');
  104. var rstart = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/;
  105. var rend = /^<\s*\/\s*([\w:-]+)[^>]*>/;
  106. var rattrs = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g;
  107. var rtag = /^</;
  108. var rtagend = /^<\s*\//;
  109. function createStack() {
  110. var stack = [];
  111. stack.lastItem = function lastItem() {
  112. return stack[stack.length - 1];
  113. };
  114. return stack;
  115. }
  116. function parser(html, handler) {
  117. var stack = createStack();
  118. var last = html;
  119. var chars;
  120. while (html) {
  121. parsePart();
  122. }
  123. parseEndTag(); // clean up any remaining tags
  124. function parsePart() {
  125. chars = true;
  126. parseTag();
  127. var same = html === last;
  128. last = html;
  129. if (same) { // discard, because it's invalid
  130. html = '';
  131. }
  132. }
  133. function parseTag() {
  134. if (html.substr(0, 4) === '<!--') { // comments
  135. parseComment();
  136. } else if (rtagend.test(html)) {
  137. parseEdge(rend, parseEndTag);
  138. } else if (rtag.test(html)) {
  139. parseEdge(rstart, parseStartTag);
  140. }
  141. parseTagDecode();
  142. }
  143. function parseEdge(regex, parser) {
  144. var match = html.match(regex);
  145. if (match) {
  146. html = html.substring(match[0].length);
  147. match[0].replace(regex, parser);
  148. chars = false;
  149. }
  150. }
  151. function parseComment() {
  152. var index = html.indexOf('-->');
  153. if (index >= 0) {
  154. if (handler.comment) {
  155. handler.comment(html.substring(4, index));
  156. }
  157. html = html.substring(index + 3);
  158. chars = false;
  159. }
  160. }
  161. function parseTagDecode() {
  162. if (!chars) {
  163. return;
  164. }
  165. var text;
  166. var index = html.indexOf('<');
  167. if (index >= 0) {
  168. text = html.substring(0, index);
  169. html = html.substring(index);
  170. } else {
  171. text = html;
  172. html = '';
  173. }
  174. if (handler.chars) {
  175. handler.chars(text);
  176. }
  177. }
  178. function parseStartTag(tag, tagName, rest, unary) {
  179. var attrs = {};
  180. var low = lowercase(tagName);
  181. var u = elements.voids[low] || !!unary;
  182. rest.replace(rattrs, attrReplacer);
  183. if (!u) {
  184. stack.push(low);
  185. }
  186. if (handler.start) {
  187. handler.start(low, attrs, u);
  188. }
  189. function attrReplacer(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
  190. if (doubleQuotedValue === void 0 && singleQuotedValue === void 0 && unquotedValue === void 0) {
  191. attrs[name] = void 0; // attribute is like <button disabled></button>
  192. } else {
  193. attrs[name] = he.decode(doubleQuotedValue || singleQuotedValue || unquotedValue || '');
  194. }
  195. }
  196. }
  197. function parseEndTag(tag, tagName) {
  198. var i;
  199. var pos = 0;
  200. var low = lowercase(tagName);
  201. if (low) {
  202. for (pos = stack.length - 1; pos >= 0; pos--) {
  203. if (stack[pos] === low) {
  204. break; // find the closest opened tag of the same type
  205. }
  206. }
  207. }
  208. if (pos >= 0) {
  209. for (i = stack.length - 1; i >= pos; i--) {
  210. if (handler.end) { // close all the open elements, up the stack
  211. handler.end(stack[i]);
  212. }
  213. }
  214. stack.length = pos;
  215. }
  216. }
  217. }
  218. module.exports = parser;
  219. }, { "./attributes": 1, "./elements": 3, "./lowercase": 5, "he": 9 }], 8: [function (require, module, exports) {
  220. 'use strict';
  221. var he = require('he');
  222. var lowercase = require('./lowercase');
  223. var attributes = require('./attributes');
  224. var elements = require('./elements');
  225. function sanitizer(buffer, options) {
  226. var last;
  227. var context;
  228. var o = options || {};
  229. reset();
  230. return {
  231. start: start,
  232. end: end,
  233. chars: chars
  234. };
  235. function out(value) {
  236. buffer.push(value);
  237. }
  238. function start(tag, attrs, unary) {
  239. var low = lowercase(tag);
  240. if (context.ignoring) {
  241. ignore(low); return;
  242. }
  243. if ((o.allowedTags || []).indexOf(low) === -1) {
  244. ignore(low); return;
  245. }
  246. if (o.filter && !o.filter({ tag: low, attrs: attrs })) {
  247. ignore(low); return;
  248. }
  249. out('<');
  250. out(low);
  251. Object.keys(attrs).forEach(parse);
  252. out(unary ? '/>' : '>');
  253. function parse(key) {
  254. var value = attrs[key];
  255. var classesOk = (o.allowedClasses || {})[low] || [];
  256. var attrsOk = (o.allowedAttributes || {})[low] || [];
  257. attrsOk = attrsOk.concat((o.allowedAttributes || {})['*'] || []);
  258. var valid;
  259. var lkey = lowercase(key);
  260. if (lkey === 'class' && attrsOk.indexOf(lkey) === -1) {
  261. value = value.split(' ').filter(isValidClass).join(' ').trim();
  262. valid = value.length;
  263. } else {
  264. valid = attrsOk.indexOf(lkey) !== -1 && (attributes.uris[lkey] !== true || testUrl(value));
  265. }
  266. if (valid) {
  267. out(' ');
  268. out(key);
  269. if (typeof value === 'string') {
  270. out('="');
  271. out(he.encode(value));
  272. out('"');
  273. }
  274. }
  275. function isValidClass(className) {
  276. return classesOk && classesOk.indexOf(className) !== -1;
  277. }
  278. }
  279. }
  280. function end(tag) {
  281. var low = lowercase(tag);
  282. var allowed = (o.allowedTags || []).indexOf(low) !== -1;
  283. if (allowed) {
  284. if (context.ignoring === false) {
  285. out('</');
  286. out(low);
  287. out('>');
  288. } else {
  289. unignore(low);
  290. }
  291. } else {
  292. unignore(low);
  293. }
  294. }
  295. function testUrl(text) {
  296. var start = text[0];
  297. if (start === '#' || start === '/') {
  298. return true;
  299. }
  300. var colon = text.indexOf(':');
  301. if (colon === -1) {
  302. return true;
  303. }
  304. var questionmark = text.indexOf('?');
  305. if (questionmark !== -1 && colon > questionmark) {
  306. return true;
  307. }
  308. var hash = text.indexOf('#');
  309. if (hash !== -1 && colon > hash) {
  310. return true;
  311. }
  312. return o.allowedSchemes.some(matches);
  313. function matches(scheme) {
  314. return text.indexOf(scheme + ':') === 0;
  315. }
  316. }
  317. function chars(text) {
  318. if (context.ignoring === false) {
  319. out(o.transformText ? o.transformText(text) : text);
  320. }
  321. }
  322. function ignore(tag) {
  323. if (elements.voids[tag]) {
  324. return;
  325. }
  326. if (context.ignoring === false) {
  327. context = { ignoring: tag, depth: 1 };
  328. } else if (context.ignoring === tag) {
  329. context.depth++;
  330. }
  331. }
  332. function unignore(tag) {
  333. if (context.ignoring === tag) {
  334. if (--context.depth <= 0) {
  335. reset();
  336. }
  337. }
  338. }
  339. function reset() {
  340. context = { ignoring: false, depth: 0 };
  341. }
  342. }
  343. module.exports = sanitizer;
  344. }, { "./attributes": 1, "./elements": 3, "./lowercase": 5, "he": 9 }], 9: [function (require, module, exports) {
  345. 'use strict';
  346. var escapes = {
  347. '&': '&amp;',
  348. '<': '&lt;',
  349. '>': '&gt;',
  350. '"': '&quot;',
  351. "'": '&#39;'
  352. };
  353. var unescapes = {
  354. '&amp;': '&',
  355. '&lt;': '<',
  356. '&gt;': '>',
  357. '&quot;': '"',
  358. '&#39;': "'"
  359. };
  360. var rescaped = /(&amp;|&lt;|&gt;|&quot;|&#39;)/g;
  361. var runescaped = /[&<>"']/g;
  362. function escapeHtmlChar(match) {
  363. return escapes[match];
  364. }
  365. function unescapeHtmlChar(match) {
  366. return unescapes[match];
  367. }
  368. function escapeHtml(text) {
  369. return text == null ? '' : String(text).replace(runescaped, escapeHtmlChar);
  370. }
  371. function unescapeHtml(html) {
  372. return html == null ? '' : String(html).replace(rescaped, unescapeHtmlChar);
  373. }
  374. escapeHtml.options = unescapeHtml.options = {};
  375. module.exports = {
  376. encode: escapeHtml,
  377. escape: escapeHtml,
  378. decode: unescapeHtml,
  379. unescape: unescapeHtml,
  380. version: '1.0.0-browser'
  381. };
  382. }, {}], 10: [function (require, module, exports) {
  383. 'use strict';
  384. function toMap(list) {
  385. return list.reduce(asKey, {});
  386. }
  387. function asKey(accumulator, item) {
  388. accumulator[item] = true;
  389. return accumulator;
  390. }
  391. module.exports = toMap;
  392. }, {}]
  393. }, {}, [4]);
  394. // ESM-comment-begin
  395. // define(function() { return { insane: __insane_func }; });
  396. // ESM-comment-end
  397. // ESM-uncomment-begin
  398. export var insane = __insane_func;
  399. // ESM-uncomment-end