author Kris Maglione <>
Wed, 24 Jan 2018 14:56:48 -0800
changeset 454546 64737c752ac4af4766ad6f82720818521f3aca24
child 457640 b38d59f71915f78922b46a7c7bc65a48488c45f1
permissions -rw-r--r--
Bug 1432966: Sanitize HTML fragments created for chrome-privileged documents. r=bz f=gijs a=jcristau This is a short-term solution to our inability to apply CSP to chrome-privileged documents. Ideally, we should be preventing all inline script execution in chrome-privileged documents, since the reprecussions of XSS in chrome documents are much worse than in content documents. Unfortunately, that's not possible in the near term because a) we don't support CSP in system principal documents at all, and b) we rely heavily on inline JS in our static XUL. This stop-gap solution at least prevents some of the most common vectors of XSS attack, by automatically sanitizing any HTML fragment created for a chrome-privileged document. MozReview-Commit-ID: 5w17celRFr

<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<window title="Mozilla Bug 1432966"
  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"/>

  <script type="application/javascript"><![CDATA[

var { classes: Cc, interfaces: Ci } = Components;

const NS_HTML = "";

function awaitLoad(frame) {
  return new Promise(resolve => {
    frame.addEventListener("load", resolve, {once: true});

async function testFrame(frame, html, expected = html) {
  await awaitLoad(frame);

  // Remove the xmlns attributes that will be automatically added when we're
  // in an XML document, and break the comparison.
  function unNS(text) {
    return text.replace(RegExp(` xmlns="${NS_HTML}"`, "g"), "");

  let doc = frame.contentDocument;
  let body = doc.body || doc.documentElement;

  let div = doc.createElementNS(NS_HTML, "div");

  div.innerHTML = html;
  is(unNS(div.innerHTML), expected, "innerHTML value");

  div.innerHTML = "<div></div>";
  div.firstChild.outerHTML = html;
  is(unNS(div.innerHTML), expected, "outerHTML value");

  div.textContent = "";
  div.insertAdjacentHTML("beforeend", html);
  is(unNS(div.innerHTML), expected, "insertAdjacentHTML('beforeend') value");

  div.innerHTML = "<a>foo</a>";
  div.firstChild.insertAdjacentHTML("afterend", html);
  is(unNS(div.innerHTML), "<a>foo</a>" + expected, "insertAdjacentHTML('afterend') value");


add_task(async function test_fragment_sanitization() {
  const XUL_URL = "chrome://global/content/win.xul";
  const HTML_URL = "chrome://mochitests/content/chrome/dom/base/test/file_empty.html";

  const HTML = '<a onclick="foo()" href="javascript:foo"><script>bar()<\/script>Meh.</a><a href="http://foo/"></a>';
  const SANITIZED = '<a>Meh.</a><a href="http://foo/"></a>';

  info("Test content HTML document");
    let frame = document.createElementNS(NS_HTML, "iframe");
    frame.src = "";

    await testFrame(frame, HTML);

  info("Test chrome HTML document");
    let frame = document.createElementNS(NS_HTML, "iframe");
    frame.src = HTML_URL;

    await testFrame(frame, HTML, SANITIZED);

  info("Test chrome XUL document");
    let frame = document.createElementNS(NS_HTML, "iframe");
    frame.src = XUL_URL;

    await testFrame(frame, HTML, SANITIZED);


  <description style="-moz-user-focus: normal; -moz-user-select: text;"><![CDATA[

  <body xmlns="">
    <a href=""
       target="_blank">Mozilla Bug 1432966</a>