Repository: angular/angular.js
File: src/jqLite.js (JavaScript)

Code before:
  'col': [2, '<table><colgroup>', '</colgroup></table>'],
  'tr': [2, '<table><tbody>', '</tbody></table>'],
  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
  '_default': [0, '', '']
};

wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;

// Support: IE <=9 only
// IE <=9 replaces <option> tags with their contents when inserted outside of
// the select element.
if (msie < 10) {
  wrapMap.optgroup = wrapMap.option = [1, '<select multiple="multiple">', '</select>'];
}


function jqLiteIsTextNode(html) {
  return !HTML_REGEXP.test(html);
}

function jqLiteAcceptsData(node) {
  // The window object can accept data but has no nodeType
  // Otherwise we are only interested in elements (1) and documents (9)
  var nodeType = node.nodeType;
  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
}

function jqLiteHasData(node) {
  for (var key in jqCache[node.ng339]) {
    return true;
  }
  return false;
}

function jqLiteBuildFragment(html, context) {
  var tmp, tag, wrap, finalHtml,
      fragment = context.createDocumentFragment(),
      nodes = [], i;

  if (jqLiteIsTextNode(html)) {
    // Convert non-html into a text node
    nodes.push(context.createTextNode(html));
  } else {
    // Convert html into DOM nodes
    tmp = fragment.appendChild(context.createElement('div'));
    tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase();
    wrap = wrapMap[tag] || wrapMap._default;
    finalHtml = JQLite.legacyXHTMLReplacement ?
      html.replace(XHTML_TAG_REGEXP, '<$1></$2>') :
      html;

Review comment (bug):
I'm not sure if you actually need the wrapping at all in modern browsers. At least in Chrome appending the elements directly works, even if, say, `td` is not under `tbody` or `table`.

In any case, while the current patch fixes the security bug we currently know about, a better way would be to avoid concatenating HTML strings whatsoever. If, for example, you need wrapping, use `document.createElement` and `appendChild` and append to the newly created one.


Code after:
// XHTML parsers do not magically insert elements in the
// same way that tag soup parsers do, so we cannot shorten
// this by omitting <tbody> or other required elements.
var wrapMap = {
  thead: ['table'],
  col: ['colgroup', 'table'],
  tr: ['tbody', 'table'],
  td: ['tr', 'tbody', 'table']
};

wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;

// Support: IE <10 only
// IE 9 requires an option wrapper & it needs to have the whole table structure
// set up in advance; assigning `"<td></td>"` to `tr.innerHTML` doesn't work, etc.
var wrapMapIE9 = {
  option: [1, '<select multiple="multiple">', '</select>'],
  _default: [0, '', '']
};

for (var key in wrapMap) {
  var wrapMapValueClosing = wrapMap[key];
  var wrapMapValue = wrapMapValueClosing.slice().reverse();
  wrapMapIE9[key] = [wrapMapValue.length, '<' + wrapMapValue.join('><') + '>', '</' + wrapMapValueClosing.join('></') + '>'];
}

wrapMapIE9.optgroup = wrapMapIE9.option;

function jqLiteIsTextNode(html) {
  return !HTML_REGEXP.test(html);
}

function jqLiteAcceptsData(node) {
  // The window object can accept data but has no nodeType
  // Otherwise we are only interested in elements (1) and documents (9)
  var nodeType = node.nodeType;
  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
}

function jqLiteHasData(node) {
  for (var key in jqCache[node.ng339]) {
    return true;
  }
  return false;
}

function jqLiteBuildFragment(html, context) {
  var tmp, tag, wrap, finalHtml,
      fragment = context.createDocumentFragment(),
      nodes = [], i;