gecko-dev/testing/web-platform/tests/content-security-policy
Alex Moshchuk 940d431b24 Bug 1454091 [wpt PR 10467] - Reland 2: Use PostTask to schedule cross-process postMessage forwarding., a=testonly
Automatic update from web-platform-testsReland 2: Use PostTask to schedule cross-process postMessage forwarding.

Changes from first reland attempt at https://crrev.com/c/1011287:
- fix an additional source of flakiness in ChromeOS login tests

Changes from original attempt at https://crrev.com/c/999182:
- fix flakiness in two additional ChromeOS login tests
- fix CSP WPT tests to not depend on ordering between iframe's onload
  and postMessage - see https://crbug.com/832319.

Previously, we sent the IPC to forward a cross-process postMessage
immediately.  This caused a behavioral difference from the
same-process case where the postMessage is always scheduled.  Namely,
in a scenario like this:

  frame.postMessage(...);
  doSomethingThatSendsIPCsToFrame(frame);

the IPCs from JS following the postMessage would've been ordered
incorrectly, causing |frame| to see their side effects after the
postMessage dispatch in the cross-process case, whereas they would be
seen before the postMessage dispatch in the same-process case.  One
example of this is frame.focus(), and another is a frame element
onload event (dispatched via FrameHostMsg_DispatchLoad) arriving after
a postMessage dispatched from an inline script while the frame was
still loading.

To resolve these ordering concerns, this CL changes cross-process
postMessage to do a PostTask on the sender side before sending the
message to the browser process.  This improves the current state of
the world, but does not yet achieve a perfect match for the IPC
ordering in the same-process case - see discussion on the bug.

Bug: 828529
Tbr: dcheng@chromium.org
Change-Id: If2cc6591db31471adff0d84ec0b689905e21cdf1
Reviewed-on: https://chromium-review.googlesource.com/999182
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Alex Moshchuk <alexmos@chromium.org>
Cr-Original-Original-Commit-Position: refs/heads/master@{#550284}
Reviewed-on: https://chromium-review.googlesource.com/1011287
Cr-Original-Commit-Position: refs/heads/master@{#550621}
Reviewed-on: https://chromium-review.googlesource.com/1012472
Cr-Commit-Position: refs/heads/master@{#550769}

--

wpt-commits: c53d084cc57749bc666e42aaceeb34daa42539c7
wpt-pr: 10467
2018-04-24 12:22:45 +00:00
..
base-uri Bug 1421370 - Update web-platform-tests to revision 2b50389ee72d89dd0be12bc6ca54a6e95c98d163, a=testonly 2017-11-28 21:32:30 +00:00
blob Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
child-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
connect-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
default-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
embedded-enforcement Bug 1454091 [wpt PR 10467] - Reland 2: Use PostTask to schedule cross-process postMessage forwarding., a=testonly 2018-04-24 12:22:45 +00:00
font-src Bug 1410245 - Update web-platform-tests to revision a372dce582c21c911056273eec042fd9073cbd60, a=testonly 2017-10-25 10:45:59 +01:00
form-action Bug 1435868 - Update web-platform-tests to revision 1e5122139897bc70789527960120354b8c640ef0, a=testonly 2018-02-05 22:20:58 +00:00
frame-ancestors Bug 1453248 [wpt PR 10411] - Return null from contentDocument/getSVGDocument() for cross-origin documents., a=testonly 2018-04-24 12:22:26 +00:00
frame-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
generic Bug 1421370 - Update web-platform-tests to revision 2b50389ee72d89dd0be12bc6ca54a6e95c98d163, a=testonly 2017-11-28 21:32:30 +00:00
img-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
inheritance Bug 1429043 - Update web-platform-tests to revision 4de5305adf3d33badc23952672bcf28168fea37e, a=testonly 2018-01-09 14:45:43 +00:00
inside-worker
media-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
meta Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
navigate-to Bug 1450577 - Add long timeout to unstable CSP tests, a=testonly on a CLOSED TREE 2018-04-01 20:56:58 +01:00
navigation
nonce-hiding Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
object-src Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
plugin-types Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
prefetch-src Bug 1433617 - Cache control headers added to png image. r=ckerschb 2018-03-25 10:44:59 +02:00
reporting Bug 1404744 - Check for FTP subresource after applying CSP. r=ckerschb 2018-04-06 00:27:02 +02:00
reporting-api Bug 1441848 [wpt PR 9714] - Reporting: Use new format for Report-To header, a=testonly 2018-03-31 22:28:40 +01:00
sandbox Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
script-src Bug 1429043 - Update web-platform-tests to revision 4de5305adf3d33badc23952672bcf28168fea37e, a=testonly 2018-01-09 14:45:43 +00:00
securitypolicyviolation Bug 1452296 [wpt PR 10285] - Check inherited interfaces exist, a=testonly 2018-04-15 09:30:53 +01:00
style-src Bug 1037335 - Fix timing issue of web-platform test: content-security-policy/style-src/style-src-imported-style-blocked.html. r=ckerschb 2017-11-29 16:55:00 +02:00
support Bug 1433617 - Cache control headers added to png image. r=ckerschb 2018-03-25 10:44:59 +02:00
svg Bug 1429043 - Update web-platform-tests to revision 4de5305adf3d33badc23952672bcf28168fea37e, a=testonly 2018-01-09 14:45:43 +00:00
unsafe-eval Bug 1419296 - Update web-platform-tests to fa27b785c62e67cc3a12201612db188c4fdd49ad, a=testonly 2017-11-21 07:39:00 +00:00
unsafe-hashed-attributes Bug 1410245 - Update web-platform-tests to revision a372dce582c21c911056273eec042fd9073cbd60, a=testonly 2017-10-25 10:45:59 +01:00
worker-src Bug 1409706 - Files added for CSP WPT for worker-src,child-src,script-src,default fallback behaviour, r=ckerschb 2018-02-02 09:34:19 +01:00
OWNERS
README.css
README.html

<!DOCTYPE html>
<html>

<head>
    <title>Introduction to Writing Content Security Policy Tests</title>
    <link rel="stylesheet" type="text/css" href="README.css">
    <link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/default.min.css">
    <script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js"></script>
    <script>
        hljs.initHighlightingOnLoad();
    </script>
</head>

<body>
    <h1>Introduction to Writing Content Security Policy Tests</h1>
    <p>The CSP test suite uses the standard W3C testharness.js framework, but there are a few additional things you'll need to do because of the unique way CSP works, even if you're already an expert at writing W3C tests. These tests require the use of the
    <a href="https://github.com/w3c/wptserve">wptserve</a> server (included in the <a href="https://github.com/w3c/web-platform-tests">web-platform-tests repository</a>) to operate correctly.</p>

    <h2>What's different about writing CSP tests?</h2>

    <h3>Headers</h3>
    <p>Content Security Policy is preferentially set through an HTTP header. This means we can't do our tests just as a simple set of HTML+CSS+JS files. Luckily the wptserver framework provides an easy method to add headers to a file.</p>
    <p>If my file is named <span class=code>example.html</span> then I can create a file
        <span class=code>example.html.headers</span> to define the headers that will be served with it. If I need to do template substitutions in the headers, I can instead create a file named <span class=code>example.html.sub.headers</span>.</p>

    <h3>Negative Test Cases and Blocked Script Execution</h3>
    <p>Another interesting feature of CSP is that it <em>prevents</em> things from happening. It even can and prevent script from running. How do we write tests that detect something didn't happen?</p>

    <h3>Checking Reports</h3>
    <p>CSP also has a feature to send a report. We ideally want to check that whenever a policy is enforced, a report is sent. This also helps us with the previous problem - if it is difficult to observe something not happening, we can still check that a report fired.</p>

    <h2>Putting it Together</h2>
    <p>Here's an example of a simple test. (ignore the highlights for now...) This file lives in the
        <span class=code>/content-security-policy/script-src/</span> directory.</p>

    <p class=codeTitle>script-src-1_1.html</p>
    <pre><code class="html">&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;script src=&#39;/resources/testharness.js&#39;&gt;&lt;/script&gt;
    &lt;script src=&#39;/resources/testharnessreport.js&#39;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Inline script should not run without &#39;unsafe-inline&#39; script-src directive.&lt;/h1&gt;
    &lt;div id=&#39;log&#39;&gt;&lt;/div&gt;

    &lt;script&gt;
    test(function() {
        asset_unreached(&#39;Unsafe inline script ran.&#39;)},
        &#39;Inline script in a script tag should not run without an unsafe-inline directive&#39;
    );
    &lt;/script&gt;

    &lt;img src=&#39;doesnotexist.jpg&#39; onerror=&#39;test(function() { assert_false(true, &quot;Unsafe inline event handler ran.&quot;) }, &quot;Inline event handlers should not run without an unsafe-inline directive&quot;);&#39;&gt;

    &lt;script async defer src=&#39;../support/checkReport.sub.js?reportField=violated-directive&amp;reportValue=<span class=highlight1>script-src%20%27self%27</span>&#39;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
        </code></pre>


    <p>This code includes three tests. The first one in the script block will generate a failure if it runs. The second one, in the onerror handler for the img which does not exist should also generate a failure if it runs. But for a successful CSP implementation, neither of these tests does run. The final test is run by the link to <span class=code>../support/checkReport.sub.js</span>. It will load some script in the page (make sure its not blocked by your policy!) which contacts the server asynchronously and sees if the expected report was sent. This should always run an generate a positive or negative result even if the inline tests are blocked as we expect.</p>

    <p>Now, to acutally exercise these tests against a policy, we'll need to set headers. In the same directory we'll place this file:</p>

    <p class=codeTitle>script-src-1_1.html.sub.headers</p>
    <pre><code class="html">
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: <span class=highlight2>script-src-1_1</span>={{$id:uuid()}}; Path=<span class=highlight2>/content-security-policy/script-src/</span>
Content-Security-Policy: <span class=highlight1>script-src 'self'</span>; report-uri  <span class=highlight2>..</span>/support/report.py?op=put&reportID={{$id}}
        </code></pre>
    <p>This sets some headers to prevent caching (just so we are more likely to see our latest changes if we're actively developing this test) sets a cookie (more on that later) and sets the relevant <span class=code>Content-Security-Policy</span> header for our test case.</p>

    <h4>What about those highlights?</h4>
    <p>In production code we don't like to repeat ourselves. For this test suite, we'll relax that rule a little bit. Why? It's easier to have many people contributing "safe" files using some template substitutions than require every file to be executable content like Python or PHP which would require much more careful code review. The highlights show where you have to be careful as you repeat yourself in more limited static files.
    </p>

    <p>The <span class=highlight1>YELLOW</span> highlighted text is information that must be the same between both files for report checking to work correctly. In the html file, we're telling
        <span class=code>checkReport.sub.js</span> to check the value of the <span class=code>
        violated-directive</span> key in the report JSON. So it needs to match (after URL encoding) the directive we set in the header.</p>

    <p>The <span class=highlight2>PINK</span> highlighted text is information that must be repeated from the path and filename of your test file into the headers file. The name of the cookie must match the name of the test file without its extension, the path for the cookie must be correct, and the relative path component to the report-uri must also be corrected if you nest your tests more than one directory deep.</p>

    <h2>Check Your Effects!</h2>
    <p>A good test case should also verify the state of the DOM in addition to checking the report - after all, a browser might send a report without actually blocking the banned content. Note that in a browser without CSP support there will be three failures on the example page as the inline script executes.</p>
    <p>How exactly you check your effects will depend on the directive, but don't hesitate to use script for testing to see if computed styles are as expected, if layouts changed or if certain elements were added to the DOM. Checking that the report also fired is just the final step of verifing correct behavior.</p>

    <p>Note that avoiding inline script is good style and good habits, but not 100% necessary for every test case. Go ahead and specify 'unsafe-inline' if it makes your life easier.</p>

    <h2>Report Existence Only and Double-Negative Tests</h2>
    <p>If you want to check that a report exists, or verify that a report <em>wasn't</em> sent for a double-negative test case,
    you can pass <strong>?reportExists=</strong><em>[true|false]</em> to <span class=code>checkReport.sub.js</span> instead of <strong>reportField</strong> and <strong>reportValue</strong>.</p>

    <h2>How does the magic happen?</h2>
    <p>Behind the scenes, a few things are going on in the framework.</p>
    <ol>
        <li>The {{$id:uuid}} templating marker in the headers file tells the wptserve HTTP server to create a new unique id and assign it to a variable, which we can re-use as {{$id}}.</li>
        <li>We'll use this UUID in two places:
            <ol>
                <li>As a GET parameter to our reporting script, to uniquely identify this instance of the test case so our report can be stored and retrieved.
                </li>
                <li>As a cookie value associated with the filename, so script in the page context can learn what UUID the report was sent under.</li>
            </ol>
        </li>
        <li>The report listener is a simple python file that stashes the report value under its UUID and allows it to be retrieved again, exactly once.</li>
        <li><span class=code>checkReport.sub.js</span> then grabs the current path information and uses that to find the cookie holding the report UUID. It deletes that cookie (otherwise the test suite would overrun the maximum size of a cookie header allowed) then makes an XMLHttpRequest to the report listener to retrieve the report, parse it and verify the contents as per the parameters it was loaded with.</li>
    </ol>

    <p>Why all these gymnastics? CSP reports are delivered by an <em>anonymous fetch</em>. This means that the browser does not process the response headers, body, or allow any state changes as a result. So we can't pull a trick like just echoing the report contents back in a Set-Cookie header or writing them to local storage.</p>

    <p>Luckily, you shouldn't have to worry about this magic much, as long as you get the incantation correct.</p>
</body>

</html>