Home | History | Annotate | Download | only in js
      1 /**
      2  * Whitelist of tag names allowed in parseHtmlSubset.
      3  * @type {[string]}
      4  */
      5 var allowedTags = ['A', 'B', 'STRONG'];
      6 
      7 /**
      8  * Parse a very small subset of HTML.
      9  * @param {string} s The string to parse.
     10  * @throws {Error} In case of non supported markup.
     11  * @return {DocumentFragment} A document fragment containing the DOM tree.
     12  */
     13 var allowedAttributes = {
     14   'href': function(node, value) {
     15     // Only allow a[href] starting with http:// and https://
     16     return node.tagName == 'A' && (value.indexOf('http://') == 0 ||
     17         value.indexOf('https://') == 0);
     18   },
     19   'target': function(node, value) {
     20     // Allow a[target] but reset the value to "".
     21     if (node.tagName != 'A')
     22       return false;
     23     node.setAttribute('target', '');
     24     return true;
     25   }
     26 }
     27 
     28 /**
     29  * Parse a very small subset of HTML.  This ensures that insecure HTML /
     30  * javascript cannot be injected into the new tab page.
     31  * @param {string} s The string to parse.
     32  * @throws {Error} In case of non supported markup.
     33  * @return {DocumentFragment} A document fragment containing the DOM tree.
     34  */
     35 function parseHtmlSubset(s) {
     36   function walk(n, f) {
     37     f(n);
     38     for (var i = 0; i < n.childNodes.length; i++) {
     39       walk(n.childNodes[i], f);
     40     }
     41   }
     42 
     43   function assertElement(node) {
     44     if (allowedTags.indexOf(node.tagName) == -1)
     45       throw Error(node.tagName + ' is not supported');
     46   }
     47 
     48   function assertAttribute(attrNode, node) {
     49     var n = attrNode.nodeName;
     50     var v = attrNode.nodeValue;
     51     if (!allowedAttributes.hasOwnProperty(n) || !allowedAttributes[n](node, v))
     52       throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported');
     53   }
     54 
     55   var r = document.createRange();
     56   r.selectNode(document.body);
     57   // This does not execute any scripts.
     58   var df = r.createContextualFragment(s);
     59   walk(df, function(node) {
     60     switch (node.nodeType) {
     61       case Node.ELEMENT_NODE:
     62         assertElement(node);
     63         var attrs = node.attributes;
     64         for (var i = 0; i < attrs.length; i++) {
     65           assertAttribute(attrs[i], node);
     66         }
     67         break;
     68 
     69       case Node.COMMENT_NODE:
     70       case Node.DOCUMENT_FRAGMENT_NODE:
     71       case Node.TEXT_NODE:
     72         break;
     73 
     74       default:
     75         throw Error('Node type ' + node.nodeType + ' is not supported');
     76     }
     77   });
     78   return df;
     79 }
     80