summaryrefslogtreecommitdiffstats
path: root/dom/security/test/contentverifier/head.js
blob: d9637d18b71c0d34ca28bddba4bb598a2bee8acf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
 * Test Content-Signature for remote about:newtab
 *  - Bug 1226928 - allow about:newtab to load remote content
 *
 * This tests content-signature verification on remote about:newtab in the
 * following cases (see TESTS, all failed loads display about:blank fallback):
 * - good case (signature should verify and correct page is displayed)
 * - reload of newtab when the siganture was invalidated after the last correct
 *   load
 * - malformed content-signature header
 * - malformed keyid directive
 * - malformed p384ecdsa directive
 * - wrong signature (this is not a siganture for the delivered document)
 * - invalid signature (this is not even a signature)
 * - loading a file that doesn't fit the key or signature
 * - cache poisoning (load a malicious remote page not in newtab, subsequent
 *   newtab load has to load the fallback)
 */

const ABOUT_NEWTAB_URI = "about:newtab";

const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
const URI_GOOD = BASE + "sig=good&x5u=good&file=good&header=good";

const INVALIDATE_FILE = BASE + "invalidateFile=yep";
const VALIDATE_FILE = BASE + "validateFile=yep";

const URI_HEADER_BASE = BASE + "sig=good&x5u=good&file=good&header=";
const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInX5U";
const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";

const URI_BAD_SIG = BASE + "sig=bad&x5u=good&file=good&header=good";
const URI_BROKEN_SIG = BASE + "sig=broken&x5u=good&file=good&header=good";
const URI_BAD_X5U = BASE + "sig=good&x5u=bad&file=good&header=good";
const URI_HTTP_X5U = BASE + "sig=good&x5u=http&file=good&header=good";
const URI_BAD_FILE = BASE + "sig=good&x5u=good&file=bad&header=good";
const URI_BAD_ALL = BASE + "sig=bad&x5u=bad&file=bad&header=bad";
const URI_BAD_CSP = BASE + "sig=bad-csp&x5u=good&file=bad-csp&header=good";

const URI_BAD_FILE_CACHED = BASE + "sig=good&x5u=good&file=bad&header=good&cached=true";

const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
const ABOUT_BLANK = "<head></head><body></body>";

const URI_CLEANUP = BASE + "cleanup=true";
const CLEANUP_DONE = "Done";

const URI_SRI = BASE + "sig=sri&x5u=good&file=sri&header=good";
const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";

const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";

// Needs to sync with pref "security.signed_content.CSP.default".
const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;

var browser = null;
var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
                           .getService(Ci.nsIAboutNewTabService);

function pushPrefs(...aPrefs) {
  return new Promise((resolve) => {
    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
  });
}

/*
 * run tests with input from TESTS
 */
function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
  // set about:newtab location for this test if it's a newtab test
  if (aNewTabPref) {
    aboutNewTabService.newTabURL = aNewTabPref;
  }

  // set prefs
  yield pushPrefs(
      ["browser.newtabpage.remote.content-signing-test", true],
      ["browser.newtabpage.remote", true],
      ["security.content.signature.root_hash",
       "CC:BE:04:87:74:B2:98:24:4A:C6:7A:71:BC:6F:DB:D6:C0:48:17:29:57:51:96:47:38:CC:24:C8:E4:F9:DD:CB"]);

  if (aNewTabPref === URI_BAD_CSP) {
    // Use stricter CSP to test CSP violation.
    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
  } else {
    // Use weaker CSP to test normal content.
    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
  }

  // start the test
  yield BrowserTestUtils.withNewTab({
      gBrowser,
      url: aUrl,
    },
    function * (browser) {
      // check if everything's set correct for testing
      ok(Services.prefs.getBoolPref(
          "browser.newtabpage.remote.content-signing-test"),
          "sanity check: remote newtab signing test should be used");
      ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
          "sanity check: remote newtab should be used");
      // we only check this if we really do a newtab test
      if (aNewTabPref) {
        ok(aboutNewTabService.overridden,
            "sanity check: default URL for about:newtab should be overriden");
        is(aboutNewTabService.newTabURL, aNewTabPref,
            "sanity check: default URL for about:newtab should return the new URL");
      }

      // Every valid remote newtab page must have built-in CSP.
      let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
                          (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));

      if (shouldHaveCSP) {
        is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
           "Valid remote newtab page must have built-in CSP.");
      }

      yield ContentTask.spawn(
          browser, aExpectedStrings, function * (aExpectedStrings) {
            for (let expectedString of aExpectedStrings) {
              ok(content.document.documentElement.innerHTML.includes(expectedString),
                 "Expect the following value in the result\n" + expectedString +
                 "\nand got " + content.document.documentElement.innerHTML);
            }
          });

      // for good test cases we check if a reload fails if the remote page
      // changed from valid to invalid in the meantime
      if (reload) {
        yield BrowserTestUtils.withNewTab({
            gBrowser,
            url: INVALIDATE_FILE,
          },
          function * (browser2) {
            yield ContentTask.spawn(browser2, null, function * () {
              ok(content.document.documentElement.innerHTML.includes("Done"),
                 "Expect the following value in the result\n" + "Done" +
                 "\nand got " + content.document.documentElement.innerHTML);
            });
          }
        );

        browser.reload();
        yield BrowserTestUtils.browserLoaded(browser);

        let expectedStrings = [ABOUT_BLANK];
        if (aNewTabPref == URI_SRI) {
          expectedStrings = [
            STYLESHEET_WITHOUT_SRI_BLOCKED,
            STYLESHEET_WITH_SRI_BLOCKED,
            SCRIPT_WITHOUT_SRI_BLOCKED,
            SCRIPT_WITH_SRI_BLOCKED
          ];
        }
        yield ContentTask.spawn(browser, expectedStrings,
          function * (expectedStrings) {
            for (let expectedString of expectedStrings) {
              ok(content.document.documentElement.innerHTML.includes(expectedString),
                 "Expect the following value in the result\n" + expectedString +
                 "\nand got " + content.document.documentElement.innerHTML);
            }
          }
        );

        yield BrowserTestUtils.withNewTab({
            gBrowser,
            url: VALIDATE_FILE,
          },
          function * (browser2) {
            yield ContentTask.spawn(browser2, null, function * () {
              ok(content.document.documentElement.innerHTML.includes("Done"),
                 "Expect the following value in the result\n" + "Done" +
                 "\nand got " + content.document.documentElement.innerHTML);
              });
          }
        );
      }
    }
  );
}

function runTests() {
  // run tests from TESTS
  for (let i = 0; i < TESTS.length; i++) {
    let testCase = TESTS[i];
    let url = "", aNewTabPref = "";
    let reload = false;
    var aExpectedStrings = testCase.testStrings;
    if (testCase.aboutURI) {
      url = ABOUT_NEWTAB_URI;
      aNewTabPref = testCase.aboutURI;
      if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
        reload = true;
      }
    } else {
      url = testCase.url;
    }

    yield doTest(aExpectedStrings, reload, url, aNewTabPref);
  }
}