1/*
2* Test server for iframe, anchor, and area referrer attributes.
3* https://bugzilla.mozilla.org/show_bug.cgi?id=1175736
4* Also server for further referrer tests such as redirecting tests
5* bug 1174913, bug 1175736, bug 1184781
6*/
7
8Components.utils.importGlobalProperties(["URLSearchParams"]);
9const SJS = "referrer_testserver.sjs?";
10const SJS_PATH = "/tests/dom/security/test/referrer-policy/";
11const BASE_ORIGIN = "example.com"
12const BASE_URL = BASE_ORIGIN + SJS_PATH + SJS;
13const SHARED_KEY = SJS;
14const SAME_ORIGIN = "mochi.test:8888" + SJS_PATH + SJS;
15const CROSS_ORIGIN_URL = "test1.example.com" + SJS_PATH + SJS;
16
17const IMG_BYTES = atob(
18  "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
19  "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
20
21function createTestUrl(aPolicy, aAction, aName, aType, aSchemeFrom, aSchemeTo,
22                       crossOrigin, referrerPolicyHeader) {
23  var schemeTo = aSchemeTo || "http";
24  var schemeFrom = aSchemeFrom || "http";
25  var rpHeader = referrerPolicyHeader || "";
26  var url = schemeTo + "://";
27  url += (crossOrigin ? CROSS_ORIGIN_URL : BASE_URL);
28  url +=
29         "ACTION=" + aAction + "&" +
30         "policy=" + aPolicy + "&" +
31         "NAME=" + aName + "&" +
32         "type=" + aType + "&" +
33         "RP_HEADER=" + rpHeader + "&" +
34         "SCHEME_FROM=" + schemeFrom;
35  return url;
36  }
37
38// test page using iframe referrer attribute
39// if aParams are set this creates a test where the iframe url is a redirect
40function createIframeTestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aParams,
41                                           aSchemeFrom, aSchemeTo, aChangingMethod) {
42  var metaString = "";
43  if (aMetaPolicy) {
44    metaString = `<meta name="referrer" content="${aMetaPolicy}">`;
45  }
46  var changeString = "";
47  if (aChangingMethod === "setAttribute") {
48    changeString = `document.getElementById("myframe").setAttribute("referrerpolicy", "${aNewAttributePolicy}")`;
49  } else if (aChangingMethod === "property") {
50    changeString = `document.getElementById("myframe").referrerPolicy = "${aNewAttributePolicy}"`;
51  }
52  var iFrameString = `<iframe src="" id="myframe" ${aAttributePolicy ? ` referrerpolicy="${aAttributePolicy}"` : ""}>iframe</iframe>`;
53  var iframeUrl = "";
54  if (aParams) {
55    aParams.delete("ACTION");
56    aParams.append("ACTION", "redirectIframe");
57    iframeUrl = "http://" + CROSS_ORIGIN_URL + aParams.toString();
58  } else {
59    iframeUrl = createTestUrl(aAttributePolicy, "test", aName, "iframe", aSchemeFrom, aSchemeTo);
60  }
61
62  return `<!DOCTYPE HTML>
63           <html>
64             <head>
65             ${metaString}
66             </head>
67             <body>
68               ${iFrameString}
69               <script>
70                 window.addEventListener("load", function() {
71                   ${changeString}
72                   document.getElementById("myframe").onload = function(){
73                    parent.postMessage("childLoadComplete", "http://mochi.test:8888");
74                   };
75                   document.getElementById("myframe").src = "${iframeUrl}";
76                 }.bind(window), false);
77               </script>
78             </body>
79           </html>`;
80}
81
82function buildAnchorString(aMetaPolicy, aReferrerPolicy, aName, aRelString, aSchemeFrom, aSchemeTo){
83  if (aReferrerPolicy) {
84    return `<a href="${createTestUrl(aReferrerPolicy, 'test', aName, 'link', aSchemeFrom, aSchemeTo)}" referrerpolicy="${aReferrerPolicy}" id="link" ${aRelString}>${aReferrerPolicy}</a>`;
85  }
86  return `<a href="${createTestUrl(aMetaPolicy, 'test', aName, 'link', aSchemeFrom, aSchemeTo)}" id="link" ${aRelString}>link</a>`;
87}
88
89function buildAreaString(aMetaPolicy, aReferrerPolicy, aName, aRelString, aSchemeFrom, aSchemeTo){
90  var result = `<img src="file_mozfiledataurl_img.jpg" alt="image" usemap="#imageMap">`;
91  result += `<map name="imageMap">`;
92  if (aReferrerPolicy) {
93    result += `<area shape="circle" coords="1,1,1" href="${createTestUrl(aReferrerPolicy, 'test', aName, 'link', aSchemeFrom, aSchemeTo)}" alt="theArea" referrerpolicy="${aReferrerPolicy}" id="link" ${aRelString}>`;
94  } else {
95    result += `<area shape="circle" coords="1,1,1" href="${createTestUrl(aMetaPolicy, 'test', aName, 'link', aSchemeFrom, aSchemeTo)}" alt="theArea" id="link" ${aRelString}>`;
96  }
97  result += `</map>`;
98
99  return result;
100}
101
102// test page using anchor or area referrer attribute
103function createAETestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aSchemeFrom, aSchemeTo, aChangingMethod) {
104  var metaString = "";
105  if (aMetaPolicy) {
106    metaString = `<head><meta name="referrer" content="${aMetaPolicy}"></head>`;
107  }
108  var changeString = "";
109  if (aChangingMethod === "setAttribute") {
110    changeString = `document.getElementById("link").setAttribute("referrerpolicy", "${aNewAttributePolicy}")`;
111  } else if (aChangingMethod === "property") {
112    changeString = `document.getElementById("link").referrerPolicy = "${aNewAttributePolicy}"`;
113  }
114  var relString = "";
115  if (aRel) {
116    relString = `rel="noreferrer"`;
117  }
118  var elementString = aStringBuilder(aMetaPolicy, aAttributePolicy, aName, relString, aSchemeFrom, aSchemeTo);
119
120  return `<!DOCTYPE HTML>
121           <html>
122             ${metaString}
123             <body>
124               ${elementString}
125               <script>
126                 window.addEventListener("load", function() {
127                   ${changeString}
128                   document.getElementById("link").click();
129                 }.bind(window), false);
130               </script>
131             </body>
132           </html>`;
133}
134
135// test page using anchor target=_blank rel=noopener
136function createTargetBlankRefferer(aMetaPolicy, aName, aSchemeFrom,
137                                   aSchemeTo, aRpHeader) {
138  var metaString = "";
139  if (aMetaPolicy) {
140    metaString = `<head><meta name="referrer" content="${aMetaPolicy}"></head>`;
141  }
142  var elementString = `<a href="${createTestUrl(aMetaPolicy, 'test', aName, 'link', aSchemeFrom, aSchemeTo, aRpHeader)}" target=_blank rel="noopener" id="link">link</a>`;
143
144  return `<!DOCTYPE HTML>
145           <html>
146             ${metaString}
147             <body>
148               ${elementString}
149               <script>
150                 window.addEventListener("load", function() {
151                   let link = document.getElementById("link");
152                   SpecialPowers.wrap(window).parent.postMessage("childLoadReady", "*");
153                   link.click();
154                 }.bind(window), false);
155               </script>
156             </body>
157           </html>`;
158}
159
160// creates test page with img that is a redirect
161function createRedirectImgTestCase(aParams, aAttributePolicy) {
162  var metaString = "";
163  if (aParams.has("META_POLICY")) {
164    metaString = `<meta name="referrer" content="${aParams.get('META_POLICY')}">`;
165  }
166  aParams.delete("ACTION");
167  aParams.append("ACTION", "redirectImg");
168  var imgUrl = "http://" + CROSS_ORIGIN_URL + aParams.toString();
169
170  return `<!DOCTYPE HTML>
171          <html>
172          <head>
173          <meta charset="utf-8">
174          ${metaString}
175          <title>Test referrer policies on redirect (img)</title>
176          </head>
177          <body>
178          <img id="testImg" src="${imgUrl}" ${aAttributePolicy ? ` referrerpolicy="${aAttributePolicy}"` : ""}>
179          <script>
180            window.addEventListener("load", function() {
181              parent.postMessage("childLoadComplete", "http://mochi.test:8888");
182            }.bind(window), false);
183          </script>
184          </body>
185          </html>`;
186}
187
188// test page using link referrer attribute
189function createLinkPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aSchemeFrom, aSchemeTo, aTestType) {
190  var metaString = "";
191  if (aMetaPolicy) {
192    metaString = `<meta name="referrer" content="${aMetaPolicy}">`;
193  }
194
195  var changeString = "";
196  var policy = aAttributePolicy ? aAttributePolicy : aMetaPolicy;
197  var elementString = aStringBuilder(policy, aName, aRel, aSchemeFrom, aSchemeTo, aTestType);
198
199  if (aTestType === "setAttribute") {
200    changeString = `var link = document.getElementById("test_link");
201                    link.setAttribute("referrerpolicy", "${aNewAttributePolicy}");
202                    link.href = "${createTestUrl(policy, "test", aName, "link_element_" + aRel, aSchemeFrom, aSchemeTo)}";`;
203  } else if (aTestType === "property") {
204    changeString = `var link = document.getElementById("test_link");
205                    link.referrerPolicy = "${aNewAttributePolicy}";
206                    link.href = "${createTestUrl(policy, "test", aName, "link_element_" + aRel, aSchemeFrom, aSchemeTo)}";`;
207  }
208
209  return `<!DOCTYPE HTML>
210           <html>
211             <head>
212               ${metaString}
213             </head>
214             <body>
215                ${elementString}
216                <script>
217                  ${changeString}
218                </script>
219             </body>
220           </html>`;
221}
222
223function createFetchUserControlRPTestCase(aName, aSchemeFrom, aSchemeTo, crossOrigin) {
224  var srcUrl = createTestUrl("", "test", aName, "fetch", aSchemeFrom, aSchemeTo, crossOrigin);
225
226  return `<!DOCTYPE HTML>
227          <html>
228          <head>
229          <meta charset="utf-8">
230          <title>Test user control referrer policies</title>
231          </head>
232          <body>
233          <script>
234            fetch("${srcUrl}", {referrerPolicy: ""}).then(function (response) {
235              window.parent.postMessage("childLoadComplete", "http://mochi.test:8888");
236            });
237          </script>
238          </body>
239          </html>`;
240}
241
242function buildLinkString(aPolicy, aName, aRel, aSchemeFrom, aSchemeTo, aTestType) {
243  var href = '';
244  var onChildComplete = `window.parent.postMessage("childLoadComplete", "http://mochi.test:8888");`
245  var policy = '';
246  var asString = '';
247  var relString = '';
248
249  if (aRel) {
250    relString = `rel="${aRel}"`;
251  }
252
253  if (aPolicy) {
254    policy = `referrerpolicy=${aPolicy}`;
255  }
256
257  if (aRel == "preload") {
258    asString = 'as="image"';
259  }
260
261  if (!aTestType) {
262    href = `href=${createTestUrl(aPolicy, "test", aName, "link_element_" + aRel, aSchemeFrom, aSchemeTo)}`;
263  }
264
265  return `<link ${relString} ${href} ${policy} ${asString} id="test_link" onload='${onChildComplete}' onerror='${onChildComplete}'>`;
266}
267
268function handleRequest(request, response) {
269  var params = new URLSearchParams(request.queryString);
270  var action = params.get("ACTION");
271  var schemeFrom = params.get("SCHEME_FROM") || "http";
272  var schemeTo = params.get("SCHEME_TO") || "http";
273  var crossOrigin = params.get("CROSS_ORIGIN") || false;
274  var referrerPolicyHeader = params.get("RP_HEADER") || "";
275
276  response.setHeader("Access-Control-Allow-Origin", "*", false);
277  if (referrerPolicyHeader) {
278    response.setHeader("Referrer-Policy", referrerPolicyHeader, false);
279  }
280
281  if (action === "resetState") {
282    setSharedState(SHARED_KEY, "{}");
283    response.write("");
284    return;
285  }
286  if (action === "get-test-results") {
287    // ?action=get-result
288    response.setHeader("Cache-Control", "no-cache", false);
289    response.setHeader("Content-Type", "text/plain", false);
290    response.write(getSharedState(SHARED_KEY));
291    return;
292  }
293  if (action === "redirect") {
294    response.write('<script>parent.postMessage("childLoadComplete", "http://mochi.test:8888");</script>');
295    return;
296  }
297  if (action === "redirectImg"){
298    params.delete("ACTION");
299    params.append("ACTION", "test");
300    params.append("type", "img");
301    // 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect
302    response.setStatusLine("1.1", 302, "found");
303    response.setHeader("Location",  "http://" + CROSS_ORIGIN_URL + params.toString(), false);
304    return;
305  }
306  if (action === "redirectIframe"){
307    params.delete("ACTION");
308    params.append("ACTION", "test");
309    params.append("type", "iframe");
310    // 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect
311    response.setStatusLine("1.1", 302, "found");
312    response.setHeader("Location",  "http://" + CROSS_ORIGIN_URL + params.toString(), false);
313    return;
314  }
315  if (action === "test") {
316    // ?action=test&policy=origin&name=name
317    var policy = params.get("policy");
318    var name = params.get("NAME");
319    var type = params.get("type");
320    var result = getSharedState(SHARED_KEY);
321
322    result = result ? JSON.parse(result) : {};
323
324    var referrerLevel = "none";
325    var test = {}
326    if (request.hasHeader("Referer")) {
327      var referrer = request.getHeader("Referer");
328      if (referrer.indexOf("referrer_testserver") > 0) {
329        referrerLevel = "full";
330      } else if (referrer.indexOf(schemeFrom + "://example.com") == 0) {
331        referrerLevel = "origin";
332      } else {
333        // this is never supposed to happen
334        referrerLevel = "other-origin";
335      }
336      test.referrer = referrer;
337    } else {
338      test.referrer = "";
339    }
340    test.policy = referrerLevel;
341    test.expected = policy;
342
343    result[name] = test;
344
345    setSharedState(SHARED_KEY, JSON.stringify(result));
346
347    if (type === "img" || type == "link_element_preload") {
348      // return image
349      response.setHeader("Content-Type", "image/png");
350      response.write(IMG_BYTES);
351      return;
352    }
353    if (type === "iframe") {
354      // return iframe page
355      response.write("<html><body>I am the iframe</body></html>");
356      return;
357    }
358    if (type === "link") {
359      // forward link click to redirect URL to finish test
360      var loc = "http://" + BASE_URL + "ACTION=redirect";
361      response.setStatusLine("1.1", 302, "Found");
362      response.setHeader("Location", loc, false);
363    }
364    return;
365  }
366
367  response.setHeader("Cache-Control", "no-cache", false);
368  response.setHeader("Content-Type", "text/html; charset=utf-8", false);
369
370  // parse test arguments and start test
371  var attributePolicy = params.get("ATTRIBUTE_POLICY") || "";
372  var newAttributePolicy = params.get("NEW_ATTRIBUTE_POLICY") || "";
373  var metaPolicy = params.get("META_POLICY") || "";
374  var rel = params.get("REL") || "";
375  var name = params.get("NAME");
376
377  // anchor & area
378  var _getPage = createAETestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel);
379  var _getAnchorPage = _getPage.bind(null, buildAnchorString, schemeFrom, schemeTo);
380  var _getAreaPage = _getPage.bind(null, buildAreaString, schemeFrom, schemeTo);
381
382  // aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod, aStringBuilder
383  if (action === "generate-anchor-policy-test") {
384    response.write(_getAnchorPage());
385    return;
386  }
387  if (action === "generate-anchor-changing-policy-test-set-attribute") {
388    response.write(_getAnchorPage("setAttribute"));
389    return;
390  }
391  if (action === "generate-anchor-changing-policy-test-property") {
392    response.write(_getAnchorPage("property"));
393    return;
394  }
395  if (action === "generate-area-policy-test") {
396    response.write(_getAreaPage());
397    return;
398  }
399  if (action === "generate-area-changing-policy-test-set-attribute") {
400    response.write(_getAreaPage("setAttribute"));
401    return;
402  }
403  if (action === "generate-area-changing-policy-test-property") {
404    response.write(_getAreaPage("property"));
405    return;
406  }
407  if (action === "generate-anchor-target-blank-policy-test") {
408    response.write(createTargetBlankRefferer(metaPolicy, name, schemeFrom, schemeTo, referrerPolicyHeader));
409    return;
410  }
411
412  // iframe
413  _getPage = createIframeTestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, "",
414                                                    schemeFrom, schemeTo);
415
416  // aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod
417  if (action === "generate-iframe-policy-test") {
418    response.write(_getPage());
419    return;
420  }
421  if (action === "generate-iframe-changing-policy-test-set-attribute") {
422    response.write(_getPage("setAttribute"));
423    return;
424  }
425  if (action === "generate-iframe-changing-policy-test-property") {
426    response.write(_getPage("property"));
427    return;
428  }
429
430  // redirect tests with img and iframe
431  if (action === "generate-img-redirect-policy-test") {
432    response.write(createRedirectImgTestCase(params, attributePolicy));
433    return;
434  }
435  if (action === "generate-iframe-redirect-policy-test") {
436    response.write(createIframeTestPageUsingRefferer(metaPolicy, attributePolicy, newAttributePolicy, name, params,
437                                                     schemeFrom, schemeTo));
438    return;
439  }
440
441  var _getPage = createLinkPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel);
442  var _getLinkPage = _getPage.bind(null, buildLinkString, schemeFrom, schemeTo);
443
444  // link
445  if (action === "generate-link-policy-test") {
446    response.write(_getLinkPage());
447    return;
448  }
449  if (action === "generate-link-policy-test-set-attribute") {
450    response.write(_getLinkPage("setAttribute"));
451    return;
452  }
453  if (action === "generate-link-policy-test-property") {
454    response.write(_getLinkPage("property"));
455    return;
456  }
457
458  if (action === "generate-fetch-user-control-policy-test") {
459    response.write(createFetchUserControlRPTestCase(name, schemeFrom, schemeTo, crossOrigin));
460    return;
461  }
462
463  response.write("I don't know action " + action);
464  return;
465}
466