Javascript  |  80行  |  2.24 KB

/**
 * Whitelist of tag names allowed in parseHtmlSubset.
 * @type {[string]}
 */
var allowedTags = ['A', 'B', 'STRONG'];

/**
 * Parse a very small subset of HTML.
 * @param {string} s The string to parse.
 * @throws {Error} In case of non supported markup.
 * @return {DocumentFragment} A document fragment containing the DOM tree.
 */
var allowedAttributes = {
  'href': function(node, value) {
    // Only allow a[href] starting with http:// and https://
    return node.tagName == 'A' && (value.indexOf('http://') == 0 ||
        value.indexOf('https://') == 0);
  },
  'target': function(node, value) {
    // Allow a[target] but reset the value to "".
    if (node.tagName != 'A')
      return false;
    node.setAttribute('target', '');
    return true;
  }
}

/**
 * Parse a very small subset of HTML.  This ensures that insecure HTML /
 * javascript cannot be injected into the new tab page.
 * @param {string} s The string to parse.
 * @throws {Error} In case of non supported markup.
 * @return {DocumentFragment} A document fragment containing the DOM tree.
 */
function parseHtmlSubset(s) {
  function walk(n, f) {
    f(n);
    for (var i = 0; i < n.childNodes.length; i++) {
      walk(n.childNodes[i], f);
    }
  }

  function assertElement(node) {
    if (allowedTags.indexOf(node.tagName) == -1)
      throw Error(node.tagName + ' is not supported');
  }

  function assertAttribute(attrNode, node) {
    var n = attrNode.nodeName;
    var v = attrNode.nodeValue;
    if (!allowedAttributes.hasOwnProperty(n) || !allowedAttributes[n](node, v))
      throw Error(node.tagName + '[' + n + '="' + v + '"] is not supported');
  }

  var r = document.createRange();
  r.selectNode(document.body);
  // This does not execute any scripts.
  var df = r.createContextualFragment(s);
  walk(df, function(node) {
    switch (node.nodeType) {
      case Node.ELEMENT_NODE:
        assertElement(node);
        var attrs = node.attributes;
        for (var i = 0; i < attrs.length; i++) {
          assertAttribute(attrs[i], node);
        }
        break;

      case Node.COMMENT_NODE:
      case Node.DOCUMENT_FRAGMENT_NODE:
      case Node.TEXT_NODE:
        break;

      default:
        throw Error('Node type ' + node.nodeType + ' is not supported');
    }
  });
  return df;
}