diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /toolkit/components/url-classifier/tests/mochitest | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/url-classifier/tests/mochitest')
84 files changed, 6158 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html new file mode 100644 index 0000000000..1096274260 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html @@ -0,0 +1,143 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> + +// Modified by evil.js +var scriptItem; + +var scriptItem1 = "untouched"; +var imageItem1 = "untouched"; +var frameItem1 = "untouched"; +var scriptItem2 = "untouched"; +var imageItem2 = "untouched"; +var frameItem2 = "untouched"; +var xhrItem = "untouched"; +var fetchItem = "untouched"; +var mediaItem1 = "untouched"; + +async function checkLoads() { + window.parent.is(scriptItem1, "spoiled", "Should not block tracking js 1"); + window.parent.is(scriptItem2, "spoiled", "Should not block tracking js 2"); + window.parent.is(imageItem1, "spoiled", "Should not block tracking img 1"); + window.parent.is(imageItem2, "spoiled", "Should not block tracking img 2"); + window.parent.is(frameItem1, "spoiled", "Should not block tracking iframe 1"); + window.parent.is(frameItem2, "spoiled", "Should not block tracking iframe 2"); + window.parent.is(mediaItem1, "loaded", "Should not block tracking video"); + window.parent.is(xhrItem, "loaded", "Should not block tracking XHR"); + window.parent.is(fetchItem, "loaded", "Should not block fetches from tracking domains"); + window.parent.is(window.document.blockedNodeByClassifierCount, 0, + "No elements should be blocked"); + + // End (parent) test. + await window.parent.clearPermissions(); + window.parent.SimpleTest.finish(); +} + +var onloadCalled = false; +var xhrFinished = false; +var fetchFinished = false; +var videoLoaded = false; +function loaded(type) { + if (type === "onload") { + onloadCalled = true; + } else if (type === "xhr") { + xhrFinished = true; + } else if (type === "fetch") { + fetchFinished = true; + } else if (type === "video") { + videoLoaded = true; + } + + if (onloadCalled && xhrFinished && fetchFinished && videoLoaded) { + checkLoads(); + } +} +</script> + +</head> + +<body onload="loaded('onload')"> + +<!-- Try loading from a tracking script URI (1) --> +<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = 'spoiled';"></script> + +<!-- Try loading from a tracking image URI (1) --> +<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg" onload="imageItem1 = 'spoiled';"/> + +<!-- Try loading from a tracking frame URI (1) --> +<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe> + +<!-- Try loading from a tracking video URI --> +<video id="badmedia1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/vp9.webm"></video> + +<script> +var v = document.getElementById("badmedia1"); +v.addEventListener("loadedmetadata", function() { + mediaItem1 = "loaded"; + loaded("video"); +}, true); +v.addEventListener("error", function() { + mediaItem1 = "error"; + loaded("video"); +}, true); + +// Try loading from a tracking script URI (2) - The loader may follow a +// different path depending on whether the resource is loaded from JS or HTML. +var newScript = document.createElement("script"); +newScript.id = "badscript2"; +newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"; +newScript.addEventListener("load", function onload() { scriptItem2 = "spoiled"; }); +document.body.appendChild(newScript); + +// / Try loading from a tracking image URI (2) +var newImage = document.createElement("img"); +newImage.id = "badimage2"; +newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg"; +newImage.addEventListener("load", function onload() { imageItem2 = "spoiled"; }); +document.body.appendChild(newImage); + +// Try loading from a tracking iframe URI (2) +var newFrame = document.createElement("iframe"); +newFrame.id = "badframe2"; +newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html"; +newFrame.addEventListener("load", function onload() { frameItem2 = "spoiled"; }); +document.body.appendChild(newFrame); + +// Try doing an XHR against a tracking domain (bug 1216793) +function reqListener() { + xhrItem = "loaded"; + loaded("xhr"); +} +function transferFailed() { + xhrItem = "failed"; + loaded("xhr"); +} +function transferCanceled() { + xhrItem = "canceled"; + loaded("xhr"); +} +var oReq = new XMLHttpRequest(); +oReq.addEventListener("load", reqListener); +oReq.addEventListener("error", transferFailed); +oReq.addEventListener("abort", transferCanceled); +oReq.open("GET", "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"); +oReq.send(); + +// Fetch from a tracking domain +fetch("http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js").then(function(response) { + if (response.ok) { + fetchItem = "loaded"; + loaded("fetch"); + } else { + fetchItem = "badresponse"; + loaded("fetch"); + } + }).catch(function(error) { + fetchItem = "error"; + loaded("fetch"); +}); +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/bad.css b/toolkit/components/url-classifier/tests/mochitest/bad.css new file mode 100644 index 0000000000..f57b36a778 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/bad.css @@ -0,0 +1 @@ +#styleBad { visibility: hidden; } diff --git a/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^ b/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^ new file mode 100644 index 0000000000..4030ea1d3d --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/toolkit/components/url-classifier/tests/mochitest/basic.vtt b/toolkit/components/url-classifier/tests/mochitest/basic.vtt new file mode 100644 index 0000000000..7781790d04 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt @@ -0,0 +1,27 @@ +WEBVTT +Region: id=testOne lines=2 width=30% +Region: id=testTwo lines=4 width=20% + +1 +00:00.500 --> 00:00.700 region:testOne +This + +2 +00:01.200 --> 00:02.400 region:testTwo +Is + +2.5 +00:02.000 --> 00:03.500 region:testOne +(Over here?!) + +3 +00:02.710 --> 00:02.910 +A + +4 +00:03.217 --> 00:03.989 +Test + +5 +00:03.217 --> 00:03.989 +And more! diff --git a/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^ new file mode 100644 index 0000000000..23de552c1a --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^ @@ -0,0 +1 @@ +Access-Control-Allow-Origin: *
\ No newline at end of file diff --git a/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html new file mode 100644 index 0000000000..80124da3cc --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html @@ -0,0 +1,11 @@ +<html> +<head> +<title></title> +</head> +<body> + +<!-- Try loading from a malware javascript URI --> +<script id="badscript" data-touched="not sure" src="http://bug1281083.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script> + +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html b/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html new file mode 100644 index 0000000000..ae305bbb47 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> +<title></title> +</head> +<body> + +<script id="goodscript" data-touched="not sure" src="http://mochitest.apps.fbsbx.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script> + +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/cache.sjs b/toolkit/components/url-classifier/tests/mochitest/cache.sjs new file mode 100644 index 0000000000..84fb0e9089 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/cache.sjs @@ -0,0 +1,90 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function handleRequest(request, response) { + var query = {}; + request.queryString.split("&").forEach(function (val) { + var idx = val.indexOf("="); + query[val.slice(0, idx)] = unescape(val.slice(idx + 1)); + }); + + var responseBody; + + // Store fullhash in the server side. + if ("list" in query && "fullhash" in query) { + // In the server side we will store: + // 1. All the full hashes for a given list + // 2. All the lists we have right now + // data is separate by '\n' + let list = query.list; + let hashes = getState(list); + + let hash = atob(query.fullhash); + hashes += hash + "\n"; + setState(list, hashes); + + let lists = getState("lists"); + if (!lists.includes(list)) { + lists += list + "\n"; + setState("lists", lists); + } + + return; + // gethash count return how many gethash request received. + // This is used by client to know if a gethash request is triggered by gecko + } else if ("gethashcount" == request.queryString) { + let counter = getState("counter"); + responseBody = counter == "" ? "0" : counter; + } else { + let body = new BinaryInputStream(request.bodyInputStream); + let avail; + let bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + let counter = getState("counter"); + counter = counter == "" ? "1" : (parseInt(counter) + 1).toString(); + setState("counter", counter); + + responseBody = parseV2Request(bytes); + } + + response.setHeader("Content-Type", "text/plain", false); + response.write(responseBody); +} + +function parseV2Request(bytes) { + var request = String.fromCharCode.apply(this, bytes); + var [HEADER, PREFIXES] = request.split("\n"); + var [PREFIXSIZE, LENGTH] = HEADER.split(":").map(val => { + return parseInt(val); + }); + + var ret = ""; + for (var start = 0; start < LENGTH; start += PREFIXSIZE) { + getState("lists") + .split("\n") + .forEach(function (list) { + var completions = getState(list).split("\n"); + + for (var completion of completions) { + if (completion.indexOf(PREFIXES.substr(start, PREFIXSIZE)) == 0) { + ret += list + ":1:32\n"; + ret += completion; + } + } + }); + } + + return ret; +} diff --git a/toolkit/components/url-classifier/tests/mochitest/chrome.toml b/toolkit/components/url-classifier/tests/mochitest/chrome.toml new file mode 100644 index 0000000000..cbd0e475e3 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/chrome.toml @@ -0,0 +1,83 @@ +[DEFAULT] +skip-if = ["os == 'android'"] +support-files = [ + "allowlistAnnotatedFrame.html", + "classifiedAnnotatedFrame.html", + "classifiedAnnotatedPBFrame.html", + "trackingRequest.html", + "bug_1281083.html", + "bug_1580416.html", + "report.sjs", + "gethash.sjs", + "classifierCommon.js", + "classifierHelper.js", + "head.js", + "threathit.sjs", + "redirect_tracker.sjs", + "!/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html", + "!/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js", + "!/toolkit/components/url-classifier/tests/mochitest/good.js", + "!/toolkit/components/url-classifier/tests/mochitest/evil.css", + "!/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^", + "!/toolkit/components/url-classifier/tests/mochitest/evil.js", + "!/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^", + "!/toolkit/components/url-classifier/tests/mochitest/evilWorker.js", + "!/toolkit/components/url-classifier/tests/mochitest/import.css", + "!/toolkit/components/url-classifier/tests/mochitest/raptor.jpg", + "!/toolkit/components/url-classifier/tests/mochitest/track.html", + "!/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js", + "!/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^", + "!/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js", + "!/toolkit/components/url-classifier/tests/mochitest/vp9.webm", + "!/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html", + "!/toolkit/components/url-classifier/tests/mochitest/workerFrame.html", + "!/toolkit/components/url-classifier/tests/mochitest/ping.sjs", + "!/toolkit/components/url-classifier/tests/mochitest/basic.vtt", + "!/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^", + "!/toolkit/components/url-classifier/tests/mochitest/dnt.html", + "!/toolkit/components/url-classifier/tests/mochitest/dnt.sjs", + "!/toolkit/components/url-classifier/tests/mochitest/update.sjs", + "!/toolkit/components/url-classifier/tests/mochitest/bad.css", + "!/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^", + "!/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html", + "!/toolkit/components/url-classifier/tests/mochitest/seek.webm", + "!/toolkit/components/url-classifier/tests/mochitest/cache.sjs", +] + +["test_advisory_link.html"] + +["test_allowlisted_annotations.html"] +tags = "trackingprotection" + +["test_classified_annotations.html"] +tags = "trackingprotection" +skip-if = ["os == 'linux' && asan"] # Bug 1202548 + +["test_classifier_changetablepref.html"] +skip-if = ["verify"] + +["test_classifier_changetablepref_bug1395411.html"] + +["test_donottrack.html"] + +["test_privatebrowsing_trackingprotection.html"] +tags = "trackingprotection" + +["test_reporturl.html"] +skip-if = ["verify"] + +["test_safebrowsing_bug1272239.html"] + +["test_threathit_report.html"] +skip-if = ["verify"] + +["test_trackingprotection_bug1157081.html"] +tags = "trackingprotection" + +["test_trackingprotection_bug1312515.html"] + +["test_trackingprotection_bug1580416.html"] +tags = "trackingprotection" + +["test_trackingprotection_whitelist.html"] +tags = "trackingprotection" diff --git a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html new file mode 100644 index 0000000000..9b12f529cb --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html @@ -0,0 +1,154 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> +"use strict"; + +var scriptItem = "untouched"; +var scriptItem1 = "untouched"; +var scriptItem2 = "untouched"; +var imageItem1 = "untouched"; +var imageItem2 = "untouched"; +var frameItem1 = "untouched"; +var frameItem2 = "untouched"; +var xhrItem = "untouched"; +var fetchItem = "untouched"; +var mediaItem1 = "untouched"; + +var badids = [ + "badscript1", + "badscript2", + "badimage1", + "badimage2", + "badframe1", + "badframe2", + "badmedia1", + "badcss", +]; + +var onloadCalled = false; +var xhrFinished = false; +var fetchFinished = false; +var videoLoaded = false; +function loaded(type) { + if (type === "onload") { + onloadCalled = true; + } else if (type === "xhr") { + xhrFinished = true; + } else if (type === "fetch") { + fetchFinished = true; + } else if (type === "video") { + videoLoaded = true; + } + if (onloadCalled && xhrFinished && fetchFinished && videoLoaded) { + var msg = new window.CustomEvent("OnLoadComplete", { + detail: JSON.stringify({ + scriptItem, + scriptItem1, + scriptItem2, + imageItem1, + imageItem2, + frameItem1, + frameItem2, + xhrItem, + fetchItem, + mediaItem1, + }), + }); + window.dispatchEvent(msg); + } +} +</script> + +<!-- Try loading from a tracking CSS URI --> +<link id="badcss" rel="stylesheet" type="text/css" href="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link> + +</head> + +<body onload="loaded('onload')"> + +<!-- Try loading from a tracking script URI (1): evil.js onload will have updated the scriptItem variable --> +<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = scriptItem;"></script> + +<!-- Try loading from a tracking image URI (1) --> +<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true" onload="imageItem1 = 'spoiled';"/> + +<!-- Try loading from a tracking frame URI (1) --> +<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe> + +<!-- Try loading from a tracking video URI --> +<video id="badmedia1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/vp9.webm?reload=true"></video> + +<script> +var v = document.getElementById("badmedia1"); +v.addEventListener("loadedmetadata", function() { + mediaItem1 = "loaded"; + loaded("video"); +}, true); +v.addEventListener("error", function() { + mediaItem1 = "error"; + loaded("video"); +}, true); + +// Try loading from a tracking script URI (2) - The loader may follow a different path depending on whether the resource is loaded from JS or HTML. +var newScript = document.createElement("script"); +newScript.id = "badscript2"; +newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"; +newScript.addEventListener("load", function() { scriptItem2 = scriptItem; }); +document.body.appendChild(newScript); + +// Try loading from a tracking image URI (2) +var newImage = document.createElement("img"); +newImage.id = "badimage2"; +newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true"; +newImage.addEventListener("load", function() { imageItem2 = "spoiled"; }); +document.body.appendChild(newImage); + +// Try loading from a tracking iframe URI (2) +var newFrame = document.createElement("iframe"); +newFrame.id = "badframe2"; +newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html"; +newFrame.addEventListener("load", function() { frameItem2 = "spoiled"; }); +document.body.appendChild(newFrame); + +// Try doing an XHR against a tracking domain (bug 1216793) +function reqListener() { + xhrItem = "loaded"; + loaded("xhr"); +} +function transferFailed() { + xhrItem = "failed"; + loaded("xhr"); +} +function transferCanceled() { + xhrItem = "canceled"; + loaded("xhr"); +} +var oReq = new XMLHttpRequest(); +oReq.addEventListener("load", reqListener); +oReq.addEventListener("error", transferFailed); +oReq.addEventListener("abort", transferCanceled); +oReq.open("GET", "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"); +oReq.send(); + +// Fetch from a tracking domain +fetch("http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js").then(function(response) { + if (response.ok) { + fetchItem = "loaded"; + loaded("fetch"); + } else { + fetchItem = "badresponse"; + loaded("fetch"); + } + }).catch(function(error) { + fetchItem = "error"; + loaded("fetch"); +}); +</script> + +The following should not be hidden: +<div id="styleCheck">STYLE TEST</div> + +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html new file mode 100644 index 0000000000..ecd89e91d6 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> +<title></title> + +<link id="badcss" rel="stylesheet" type="text/css" href="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link> + +</head> +<body> + +<script id="badscript" data-touched="not sure" src="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script> + +<script id="goodscript" data-touched="not sure" src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script> + +<!-- The image cache can cache JS handlers, so make sure we use a different URL for raptor.jpg each time --> +<img id="badimage" data-touched="not sure" src="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/> + +<img id="goodimage" data-touched="not sure" src="https://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test2" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/> + +The following should not be hidden: +<div id="styleCheck">STYLE TEST</div> + +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js new file mode 100644 index 0000000000..ca288986e3 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js @@ -0,0 +1,108 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/chrome-script */ + +var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService( + Ci.nsIUrlClassifierDBService +); +var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService( + Ci.nsIUrlListManager +); + +var timer; +function setTimeout(callback, delay) { + timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback( + { notify: callback }, + delay, + Ci.nsITimer.TYPE_ONE_SHOT + ); +} + +function doUpdate(update) { + let listener = { + QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]), + updateUrlRequested(url) {}, + streamFinished(status) {}, + updateError(errorCode) { + sendAsyncMessage("updateError", errorCode); + }, + updateSuccess(requestedTimeout) { + sendAsyncMessage("updateSuccess"); + }, + }; + + try { + dbService.beginUpdate( + listener, + "test-malware-simple,test-unwanted-simple", + "" + ); + dbService.beginStream("", ""); + dbService.updateStream(update); + dbService.finishStream(); + dbService.finishUpdate(); + } catch (e) { + // beginUpdate may fail if there's an existing update in progress + // retry until success or testcase timeout. + setTimeout(() => { + doUpdate(update); + }, 1000); + } +} + +function doReload() { + try { + dbService.reloadDatabase(); + sendAsyncMessage("reloadSuccess"); + } catch (e) { + setTimeout(() => { + doReload(); + }, 1000); + } +} + +// SafeBrowsing.jsm is initialized after mozEntries are added. Add observer +// to receive "finished" event. For the case when this function is called +// after the event had already been notified, we lookup entries to see if +// they are already added to database. +function waitForInit() { + if (listmanager.isRegistered()) { + sendAsyncMessage("safeBrowsingInited"); + } else { + setTimeout(() => { + waitForInit(); + }, 1000); + } +} + +function doGetTables() { + const callback = tables => { + sendAsyncMessage("GetTableSuccess", tables); + }; + + try { + dbService.getTables(callback); + } catch (e) { + setTimeout(() => { + doGetTables(); + }, 1000); + } +} + +addMessageListener("doUpdate", ({ testUpdate }) => { + doUpdate(testUpdate); +}); + +addMessageListener("doReload", () => { + doReload(); +}); + +addMessageListener("waitForInit", () => { + waitForInit(); +}); + +addMessageListener("doGetTables", () => { + doGetTables(); +}); diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html new file mode 100644 index 0000000000..1e3617b9d8 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html @@ -0,0 +1,57 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> + +var scriptItem = "untouched"; + +function checkLoads() { + // Make sure the javascript did not load. + window.parent.is(scriptItem, "untouched", "Should not load bad javascript"); + + // Make sure the css did not load. + var elt = document.getElementById("styleCheck"); + var style = document.defaultView.getComputedStyle(elt); + window.parent.isnot(style.visibility, "hidden", "Should not load bad css"); + + elt = document.getElementById("styleBad"); + style = document.defaultView.getComputedStyle(elt); + window.parent.isnot(style.visibility, "hidden", "Should not load bad css"); + + elt = document.getElementById("styleImport"); + style = document.defaultView.getComputedStyle(elt); + window.parent.isnot(style.visibility, "visible", "Should import clean css"); + + // Call parent.loadTestFrame again to test classification metadata in HTTP + // cache entries. + if (window.parent.firstLoad) { + window.parent.info("Reloading from cache..."); + window.parent.firstLoad = false; + window.parent.loadTestFrame(); + return; + } + + // End (parent) test. + window.parent.SimpleTest.finish(); +} + +</script> + +<!-- Try loading from a malware javascript URI --> +<script type="text/javascript" src="http://malware.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script> + +<!-- Try loading from an uwanted software css URI --> +<link rel="stylesheet" type="text/css" href="http://unwanted.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link> + +<!-- Try loading a marked-as-malware css through an @import from a clean URI --> +<link rel="stylesheet" type="text/css" href="import2.css"></link> +</head> + +<body onload="checkLoads()"> +The following should not be hidden: +<div id="styleCheck">STYLE TEST</div> +<div id="styleBad">STYLE BAD</div> +<div id="styleImport">STYLE IMPORT</div> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js new file mode 100644 index 0000000000..157e5d54f7 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js @@ -0,0 +1,204 @@ +if (typeof classifierHelper == "undefined") { + var classifierHelper = {}; +} + +const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js"); +var gScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL); +var gOriginalGetHashURL; + +const PREFS = { + PROVIDER_LISTS: "browser.safebrowsing.provider.mozilla.lists", + DISALLOW_COMPLETIONS: "urlclassifier.disallow_completions", + PROVIDER_GETHASHURL: "browser.safebrowsing.provider.mozilla.gethashURL", +}; + +classifierHelper._curAddChunkNum = 1; + +// addUrlToDB is asynchronous, queue the task to ensure +// the callback follow correct order. +classifierHelper._updates = []; + +// Keep urls added to database, those urls should be automatically +// removed after test complete. +classifierHelper._updatesToCleanup = []; + +classifierHelper._initsCB = []; + +// This function return a Promise, promise is resolved when SafeBrowsing.jsm +// is initialized. +classifierHelper.waitForInit = function () { + return new Promise(function (resolve, reject) { + classifierHelper._initsCB.push(resolve); + gScript.sendAsyncMessage("waitForInit"); + }); +}; + +// This function is used to allow completion for specific "list", +// some lists like "test-malware-simple" is default disabled to ask for complete. +// "list" is the db we would like to allow it +// "url" is the completion server +classifierHelper.allowCompletion = async function (lists, url) { + for (var list of lists) { + // Add test db to provider + var pref = await SpecialPowers.getParentCharPref(PREFS.PROVIDER_LISTS); + pref += "," + list; + await SpecialPowers.setCharPref(PREFS.PROVIDER_LISTS, pref); + + // Rename test db so we will not disallow it from completions + pref = await SpecialPowers.getParentCharPref(PREFS.DISALLOW_COMPLETIONS); + pref = pref.replace(list, list + "-backup"); + await SpecialPowers.setCharPref(PREFS.DISALLOW_COMPLETIONS, pref); + } + + // Store the original get hash URL in order to reset it back during clean up. + gOriginalGetHashURL = SpecialPowers.getCharPref(PREFS.PROVIDER_GETHASHURL); + + // Set get hash url + await SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, url); +}; + +// Pass { url: ..., db: ... } to add url to database, +// onsuccess/onerror will be called when update complete. +classifierHelper.addUrlToDB = function (updateData) { + return new Promise(function (resolve, reject) { + var testUpdate = ""; + for (var update of updateData) { + var LISTNAME = update.db; + var CHUNKDATA = update.url; + var CHUNKLEN = CHUNKDATA.length; + var HASHLEN = update.len ? update.len : 32; + + update.addChunk = classifierHelper._curAddChunkNum; + classifierHelper._curAddChunkNum += 1; + + classifierHelper._updatesToCleanup.push(update); + testUpdate += + "n:1000\n" + + "i:" + + LISTNAME + + "\n" + + "ad:1\n" + + "a:" + + update.addChunk + + ":" + + HASHLEN + + ":" + + CHUNKLEN + + "\n" + + CHUNKDATA; + } + + classifierHelper._update(testUpdate, resolve, reject); + }); +}; + +// This API is used to expire all add/sub chunks we have updated +// by using addUrlToDB. +classifierHelper.resetDatabase = function () { + function removeDatabase() { + return new Promise(function (resolve, reject) { + var testUpdate = ""; + for (var update of classifierHelper._updatesToCleanup) { + testUpdate += + "n:1000\ni:" + update.db + "\nad:" + update.addChunk + "\n"; + } + + classifierHelper._update(testUpdate, resolve, reject); + }); + } + + // Remove and then reload will ensure both database and cache will + // be cleared. + return Promise.resolve() + .then(removeDatabase) + .then(classifierHelper.reloadDatabase); +}; + +classifierHelper.reloadDatabase = function () { + return new Promise(function (resolve, reject) { + gScript.addMessageListener("reloadSuccess", function handler() { + gScript.removeMessageListener("reloadSuccess", handler); + resolve(); + }); + + gScript.sendAsyncMessage("doReload"); + }); +}; + +classifierHelper.getTables = function () { + return new Promise(resolve => { + gScript.addMessageListener("GetTableSuccess", function handler(tables) { + gScript.removeMessageListener("GetTableSuccess", handler); + resolve(tables); + }); + + gScript.sendAsyncMessage("doGetTables"); + }); +}; + +classifierHelper._update = function (testUpdate, onsuccess, onerror) { + // Queue the task if there is still an on-going update + classifierHelper._updates.push({ + data: testUpdate, + onsuccess, + onerror, + }); + if (classifierHelper._updates.length != 1) { + return; + } + + gScript.sendAsyncMessage("doUpdate", { testUpdate }); +}; + +classifierHelper._updateSuccess = function () { + var update = classifierHelper._updates.shift(); + update.onsuccess(); + + if (classifierHelper._updates.length) { + var testUpdate = classifierHelper._updates[0].data; + gScript.sendAsyncMessage("doUpdate", { testUpdate }); + } +}; + +classifierHelper._updateError = function (errorCode) { + var update = classifierHelper._updates.shift(); + update.onerror(errorCode); + + if (classifierHelper._updates.length) { + var testUpdate = classifierHelper._updates[0].data; + gScript.sendAsyncMessage("doUpdate", { testUpdate }); + } +}; + +classifierHelper._inited = function () { + classifierHelper._initsCB.forEach(function (cb) { + cb(); + }); + classifierHelper._initsCB = []; +}; + +classifierHelper._setup = function () { + gScript.addMessageListener("updateSuccess", classifierHelper._updateSuccess); + gScript.addMessageListener("updateError", classifierHelper._updateError); + gScript.addMessageListener("safeBrowsingInited", classifierHelper._inited); + + // cleanup will be called at end of each testcase to remove all the urls added to database. + SimpleTest.registerCleanupFunction(classifierHelper._cleanup); +}; + +classifierHelper._cleanup = function () { + // clean all the preferences may touch by helper + Object.values(PREFS).map(pref => SpecialPowers.clearUserPref(pref)); + + // Set the getHashURL back, the original value isn't the same as the default + // pref value. + SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, gOriginalGetHashURL); + + if (!classifierHelper._updatesToCleanup) { + return Promise.resolve(); + } + + return classifierHelper.resetDatabase(); +}; + +classifierHelper._setup(); diff --git a/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js new file mode 100644 index 0000000000..e7361119b6 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js @@ -0,0 +1,12 @@ +/* eslint-env worker */ + +onmessage = function () { + try { + importScripts("evilWorker.js"); + } catch (ex) { + postMessage("success"); + return; + } + + postMessage("failure"); +}; diff --git a/toolkit/components/url-classifier/tests/mochitest/dnt.html b/toolkit/components/url-classifier/tests/mochitest/dnt.html new file mode 100644 index 0000000000..2246263688 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/dnt.html @@ -0,0 +1,31 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> + +function makeXHR(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.onload = function() { + callback(xhr.response); + }; + xhr.send(); +} + +function loaded(type) { + window.parent.postMessage("navigator.doNotTrack=" + navigator.doNotTrack, "*"); + + makeXHR("dnt.sjs", (res) => { + window.parent.postMessage("DNT=" + res, "*"); + window.parent.postMessage("finish", "*"); + }); +} + +</script> +</head> + +<body onload="loaded('onload')"> +</body> + +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/dnt.sjs b/toolkit/components/url-classifier/tests/mochitest/dnt.sjs new file mode 100644 index 0000000000..bbb836482a --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/dnt.sjs @@ -0,0 +1,9 @@ +function handleRequest(request, response) { + var dnt = "unspecified"; + if (request.hasHeader("DNT")) { + dnt = "1"; + } + + response.setHeader("Content-Type", "text/plain", false); + response.write(dnt); +} diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.css b/toolkit/components/url-classifier/tests/mochitest/evil.css new file mode 100644 index 0000000000..62d506c899 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evil.css @@ -0,0 +1 @@ +#styleCheck { visibility: hidden; } diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^ b/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^ new file mode 100644 index 0000000000..4030ea1d3d --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.js b/toolkit/components/url-classifier/tests/mochitest/evil.js new file mode 100644 index 0000000000..3e8b165587 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evil.js @@ -0,0 +1,3 @@ +/* global scriptItem:true */ + +scriptItem = "loaded malware javascript!"; diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^ b/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^ new file mode 100644 index 0000000000..3eced96143 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^ @@ -0,0 +1,2 @@ +Access-Control-Allow-Origin: * +Cache-Control: no-store diff --git a/toolkit/components/url-classifier/tests/mochitest/evilWorker.js b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js new file mode 100644 index 0000000000..b4e8a47602 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js @@ -0,0 +1,5 @@ +/* eslint-env worker */ + +onmessage = function () { + postMessage("loaded bad file"); +}; diff --git a/toolkit/components/url-classifier/tests/mochitest/features.js b/toolkit/components/url-classifier/tests/mochitest/features.js new file mode 100644 index 0000000000..0004693d81 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/features.js @@ -0,0 +1,291 @@ +var tests = [ + // Config is an array with 4 elements: + // - annotation blocklist + // - annotation entitylist + // - tracking blocklist + // - tracking entitylist + + // All disabled. + { + config: [false, false, false, false], + loadExpected: true, + annotationExpected: false, + }, + + // Just entitylisted. + { + config: [false, false, false, true], + loadExpected: true, + annotationExpected: false, + }, + + // Just blocklisted. + { + config: [false, false, true, false], + loadExpected: false, + annotationExpected: false, + }, + + // entitylist + blocklist => entitylist wins + { + config: [false, false, true, true], + loadExpected: true, + annotationExpected: false, + }, + + // just annotated in entitylist. + { + config: [false, true, false, false], + loadExpected: true, + annotationExpected: false, + }, + + // TP and annotation entitylisted. + { + config: [false, true, false, true], + loadExpected: true, + annotationExpected: false, + }, + + // Annotation entitylisted, but TP blocklisted. + { + config: [false, true, true, false], + loadExpected: false, + annotationExpected: false, + }, + + // Annotation entitylisted. TP blocklisted and entitylisted: entitylist wins. + { + config: [false, true, true, true], + loadExpected: true, + annotationExpected: false, + }, + + // Just blocklist annotated. + { + config: [true, false, false, false], + loadExpected: true, + annotationExpected: true, + }, + + // annotated but TP entitylisted. + { + config: [true, false, false, true], + loadExpected: true, + annotationExpected: true, + }, + + // annotated and blocklisted. + { + config: [true, false, true, false], + loadExpected: false, + annotationExpected: false, + }, + + // annotated, TP blocklisted and entitylisted: entitylist wins. + { + config: [true, false, true, true], + loadExpected: true, + annotationExpected: true, + }, + + // annotated in white and blocklist. + { + config: [true, true, false, false], + loadExpected: true, + annotationExpected: false, + }, + + // annotated in white and blocklist. TP Whiteslited + { + config: [true, true, false, true], + loadExpected: true, + annotationExpected: false, + }, + + // everywhere. TP entitylist wins. + { + config: [true, true, true, true], + loadExpected: true, + annotationExpected: false, + }, +]; + +function prefBlacklistValue(value) { + return value ? "example.com" : ""; +} + +function prefWhitelistValue(value) { + return value ? "mochi.test,mochi.xorigin-test" : ""; +} + +async function runTest(test, expectedFlag, expectedTrackingResource, prefs) { + let config = [ + [ + "urlclassifier.trackingAnnotationTable.testEntries", + prefBlacklistValue(test.config[0]), + ], + [ + "urlclassifier.features.fingerprinting.annotate.blacklistHosts", + prefBlacklistValue(test.config[0]), + ], + [ + "urlclassifier.features.cryptomining.annotate.blacklistHosts", + prefBlacklistValue(test.config[0]), + ], + [ + "urlclassifier.features.socialtracking.annotate.blacklistHosts", + prefBlacklistValue(test.config[0]), + ], + + [ + "urlclassifier.trackingAnnotationWhitelistTable.testEntries", + prefWhitelistValue(test.config[1]), + ], + [ + "urlclassifier.features.fingerprinting.annotate.whitelistHosts", + prefWhitelistValue(test.config[1]), + ], + [ + "urlclassifier.features.cryptomining.annotate.whitelistHosts", + prefWhitelistValue(test.config[1]), + ], + [ + "urlclassifier.features.socialtracking.annotate.whitelistHosts", + prefWhitelistValue(test.config[1]), + ], + + [ + "urlclassifier.trackingTable.testEntries", + prefBlacklistValue(test.config[2]), + ], + [ + "urlclassifier.features.fingerprinting.blacklistHosts", + prefBlacklistValue(test.config[2]), + ], + [ + "urlclassifier.features.cryptomining.blacklistHosts", + prefBlacklistValue(test.config[2]), + ], + [ + "urlclassifier.features.socialtracking.blacklistHosts", + prefBlacklistValue(test.config[2]), + ], + + [ + "urlclassifier.trackingWhitelistTable.testEntries", + prefWhitelistValue(test.config[3]), + ], + [ + "urlclassifier.features.fingerprinting.whitelistHosts", + prefWhitelistValue(test.config[3]), + ], + [ + "urlclassifier.features.cryptomining.whitelistHosts", + prefWhitelistValue(test.config[3]), + ], + [ + "urlclassifier.features.socialtracking.whitelistHosts", + prefWhitelistValue(test.config[3]), + ], + ]; + + info("Testing: " + JSON.stringify(config) + "\n"); + + await SpecialPowers.pushPrefEnv({ set: config.concat(prefs) }); + + // This promise will be resolved when the chromeScript knows if the channel + // is annotated or not. + let annotationPromise; + if (test.loadExpected) { + info("We want to have annotation information"); + annotationPromise = new Promise(resolve => { + chromeScript.addMessageListener( + "last-channel-flags", + data => resolve(data), + { once: true } + ); + }); + } + + // Let's load a script with a random query string to avoid network cache. + // Using a script as the fingerprinting feature does not block display content + let result = await new Promise(resolve => { + let script = document.createElement("script"); + script.setAttribute( + "src", + "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" + + Math.random() + ); + script.onload = _ => resolve(true); + script.onerror = _ => resolve(false); + document.body.appendChild(script); + }); + + is(result, test.loadExpected, "The loading happened correctly"); + + if (annotationPromise) { + let data = await annotationPromise; + is( + !!data.classificationFlags, + test.annotationExpected, + "The annotation happened correctly" + ); + if (test.annotationExpected) { + is(data.classificationFlags, expectedFlag, "Correct flag"); + is( + data.isThirdPartyTrackingResource, + expectedTrackingResource, + "Tracking resource flag matches" + ); + } + } +} + +var chromeScript; + +function runTests(flag, prefs, trackingResource) { + chromeScript = SpecialPowers.loadChromeScript(_ => { + /* eslint-env mozilla/chrome-script */ + function onExamResp(subject, topic, data) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel); + if ( + !channel || + !classifiedChannel || + !channel.URI.spec.startsWith( + "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" + ) + ) { + return; + } + + sendAsyncMessage("last-channel-flags", { + classificationFlags: classifiedChannel.classificationFlags, + isThirdPartyTrackingResource: + classifiedChannel.isThirdPartyTrackingResource(), + }); + } + + addMessageListener("done", __ => { + Services.obs.removeObserver(onExamResp, "http-on-examine-response"); + }); + + Services.obs.addObserver(onExamResp, "http-on-examine-response"); + + sendAsyncMessage("start-test"); + }); + + chromeScript.addMessageListener( + "start-test", + async _ => { + for (let test in tests) { + await runTest(tests[test], flag, trackingResource, prefs); + } + + chromeScript.sendAsyncMessage("done"); + SimpleTest.finish(); + }, + { once: true } + ); +} diff --git a/toolkit/components/url-classifier/tests/mochitest/gethash.sjs b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs new file mode 100644 index 0000000000..12e8c11835 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs @@ -0,0 +1,86 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function handleRequest(request, response) { + var query = {}; + request.queryString.split("&").forEach(function (val) { + var idx = val.indexOf("="); + query[val.slice(0, idx)] = unescape(val.slice(idx + 1)); + }); + + var responseBody; + + // Store fullhash in the server side. + if ("list" in query && "fullhash" in query) { + // In the server side we will store: + // 1. All the full hashes for a given list + // 2. All the lists we have right now + // data is separate by '\n' + let list = query.list; + let hashes = getState(list); + + let hash = atob(query.fullhash); + hashes += hash + "\n"; + setState(list, hashes); + + let lists = getState("lists"); + if (!lists.includes(list)) { + lists += list + "\n"; + setState("lists", lists); + } + + return; + // gethash count return how many gethash request received. + // This is used by client to know if a gethash request is triggered by gecko + } else if ("gethashcount" == request.queryString) { + let counter = getState("counter"); + responseBody = counter == "" ? "0" : counter; + } else { + let body = new BinaryInputStream(request.bodyInputStream); + let avail; + let bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + let counter = getState("counter"); + counter = counter == "" ? "1" : (parseInt(counter) + 1).toString(); + setState("counter", counter); + + responseBody = parseV2Request(bytes); + } + + response.setHeader("Content-Type", "text/plain", false); + response.write(responseBody); +} + +function parseV2Request(bytes) { + var request = String.fromCharCode.apply(this, bytes); + var [HEADER, PREFIXES] = request.split("\n"); + var [PREFIXSIZE, LENGTH] = HEADER.split(":").map(val => { + return parseInt(val); + }); + + var ret = ""; + for (var start = 0; start < LENGTH; start += PREFIXSIZE) { + getState("lists") + .split("\n") + .forEach(function (list) { + var completions = getState(list).split("\n"); + + for (var completion of completions) { + if (completion.indexOf(PREFIXES.substr(start, PREFIXSIZE)) == 0) { + ret += list + ":1:32\n"; + ret += completion; + } + } + }); + } + + return ret; +} diff --git a/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html new file mode 100644 index 0000000000..4f518dabe0 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html @@ -0,0 +1,61 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> + +var scriptItem = "untouched"; + +function checkLoads() { + var title = document.getElementById("title"); + title.textContent = window.parent.shouldLoad ? + "The following should be hidden:" : + "The following should not be hidden:"; + + if (window.parent.shouldLoad) { + window.parent.is(scriptItem, "loaded malware javascript!", "Should load bad javascript"); + } else { + window.parent.is(scriptItem, "untouched", "Should not load bad javascript"); + } + + var elt = document.getElementById("styleImport"); + var style = document.defaultView.getComputedStyle(elt); + window.parent.isnot(style.visibility, "visible", "Should load clean css"); + + // Make sure the css did not load. + elt = document.getElementById("styleCheck"); + style = document.defaultView.getComputedStyle(elt); + if (window.parent.shouldLoad) { + window.parent.isnot(style.visibility, "visible", "Should load bad css"); + } else { + window.parent.isnot(style.visibility, "hidden", "Should not load bad css"); + } + + elt = document.getElementById("styleBad"); + style = document.defaultView.getComputedStyle(elt); + if (window.parent.shouldLoad) { + window.parent.isnot(style.visibility, "visible", "Should import bad css"); + } else { + window.parent.isnot(style.visibility, "hidden", "Should not import bad css"); + } +} + +</script> + +<!-- Try loading from a malware javascript URI --> +<script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script> + +<!-- Try loading from an uwanted software css URI --> +<link rel="stylesheet" type="text/css" href="http://unwanted.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link> + +<!-- Try loading a marked-as-malware css through an @import from a clean URI --> +<link rel="stylesheet" type="text/css" href="import.css"></link> +</head> + +<body onload="checkLoads()"> +<div id="title"></div> +<div id="styleCheck">STYLE EVIL</div> +<div id="styleBad">STYLE BAD</div> +<div id="styleImport">STYLE IMPORT</div> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/good.js b/toolkit/components/url-classifier/tests/mochitest/good.js new file mode 100644 index 0000000000..a3c8fcb0c8 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/good.js @@ -0,0 +1,3 @@ +/* global scriptItem:true */ + +scriptItem = "loaded whitelisted javascript!"; diff --git a/toolkit/components/url-classifier/tests/mochitest/head.js b/toolkit/components/url-classifier/tests/mochitest/head.js new file mode 100644 index 0000000000..dca6abe279 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/head.js @@ -0,0 +1,40 @@ +// calculate the fullhash and send it to gethash server +function addCompletionToServer(list, url, mochitestUrl) { + return new Promise(function (resolve, reject) { + var listParam = "list=" + list; + var fullhashParam = "fullhash=" + hash(url); + + var xhr = new XMLHttpRequest(); + xhr.open("PUT", mochitestUrl + "?" + listParam + "&" + fullhashParam, true); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function () { + if (this.readyState == this.DONE) { + resolve(); + } + }; + xhr.send(); + }); +} + +function hash(str) { + var hasher = SpecialPowers.Cc["@mozilla.org/security/hash;1"].createInstance( + SpecialPowers.Ci.nsICryptoHash + ); + + var data = new TextEncoder().encode(str); + hasher.init(hasher.SHA256); + hasher.update(data, data.length); + + return hasher.finish(true); +} + +var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({ set: p }); + +function whenDelayedStartupFinished(aWindow, aCallback) { + Services.obs.addObserver(function observer(aSubject, aTopic) { + if (aWindow == aSubject) { + Services.obs.removeObserver(observer, aTopic); + setTimeout(aCallback, 0); + } + }, "browser-delayed-startup-finished"); +} diff --git a/toolkit/components/url-classifier/tests/mochitest/import.css b/toolkit/components/url-classifier/tests/mochitest/import.css new file mode 100644 index 0000000000..9b86c82169 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/import.css @@ -0,0 +1,3 @@ +/* malware.example.com is in the malware database. */ +@import url("http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/bad.css"); +#styleImport { visibility: hidden; } diff --git a/toolkit/components/url-classifier/tests/mochitest/import2.css b/toolkit/components/url-classifier/tests/mochitest/import2.css new file mode 100644 index 0000000000..55de698e0c --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/import2.css @@ -0,0 +1,3 @@ +/* malware.mochi.test is in the malware database. */ +@import url("http://malware.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/bad.css"); +#styleImport { visibility: hidden; } diff --git a/toolkit/components/url-classifier/tests/mochitest/mochitest.toml b/toolkit/components/url-classifier/tests/mochitest/mochitest.toml new file mode 100644 index 0000000000..20ebb3bf34 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.toml @@ -0,0 +1,91 @@ +[DEFAULT] +tags = "condprof" +support-files = [ + "classifierCommon.js", + "classifierFrame.html", + "classifierHelper.js", + "cleanWorker.js", + "good.js", + "head.js", + "evil.css", + "evil.css^headers^", + "evil.js", + "evil.js^headers^", + "evilWorker.js", + "import.css", + "import2.css", + "raptor.jpg", + "track.html", + "trackerFrame.html", + "trackerFrame.sjs", + "trackingRequest.js", + "trackingRequest.js^headers^", + "unwantedWorker.js", + "vp9.webm", + "whitelistFrame.html", + "workerFrame.html", + "ping.sjs", + "basic.vtt", + "basic.vtt^headers^", + "dnt.html", + "dnt.sjs", + "update.sjs", + "bad.css", + "bad.css^headers^", + "gethash.sjs", + "gethashFrame.html", + "seek.webm", + "cache.sjs", + "features.js", + "sw_register.html", + "sw_unregister.html", + "sw_worker.js", + "sandboxed.html", + "sandboxed.html^headers^", +] +skip-if = [ + "http3", + "http2", +] + +["test_annotation_vs_TP.html"] + +["test_bug1254766.html"] + +["test_cachemiss.html"] +skip-if = ["verify"] + +["test_classifier.html"] + +["test_classifier_match.html"] + +["test_classifier_worker.html"] + +["test_classify_by_default.html"] +skip-if = [ + "http3", + "http2", +] + +["test_classify_ping.html"] +skip-if = ["verify && debug && (os == 'win' || os == 'mac')"] + +["test_classify_top_sandboxed.html"] + +["test_classify_track.html"] + +["test_cryptomining.html"] + +["test_cryptomining_annotate.html"] + +["test_emailtracking.html"] + +["test_fingerprinting.html"] + +["test_fingerprinting_annotate.html"] + +["test_gethash.html"] + +["test_socialtracking.html"] + +["test_socialtracking_annotate.html"] diff --git a/toolkit/components/url-classifier/tests/mochitest/ping.sjs b/toolkit/components/url-classifier/tests/mochitest/ping.sjs new file mode 100644 index 0000000000..09a5f17356 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/ping.sjs @@ -0,0 +1,15 @@ +function handleRequest(request, response) { + var query = {}; + request.queryString.split("&").forEach(function (val) { + var [name, value] = val.split("="); + query[name] = unescape(value); + }); + + if (request.method == "POST") { + setState(query.id, "ping"); + } else { + var value = getState(query.id); + response.setHeader("Content-Type", "text/plain", false); + response.write(value); + } +} diff --git a/toolkit/components/url-classifier/tests/mochitest/raptor.jpg b/toolkit/components/url-classifier/tests/mochitest/raptor.jpg Binary files differnew file mode 100644 index 0000000000..243ba9e2d4 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/raptor.jpg diff --git a/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs b/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs new file mode 100644 index 0000000000..563abae5cb --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs @@ -0,0 +1,7 @@ +const gURL = + "http://trackertest.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"; + +function handleRequest(request, response) { + response.setStatusLine("1.1", 302, "found"); + response.setHeader("Location", gURL, false); +} diff --git a/toolkit/components/url-classifier/tests/mochitest/report.sjs b/toolkit/components/url-classifier/tests/mochitest/report.sjs new file mode 100644 index 0000000000..b5844eb7a4 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/report.sjs @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const SJS = "report.sjs?"; +const REDIRECT = + "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/" + + SJS; + +function createBlockedIframePage() { + return `<!DOCTYPE HTML> + <html> + <head> + <title></title> + </head> + <body> + <iframe id="phishingFrame" ></iframe> + </body> + </html>`; +} + +function createPage() { + return `<!DOCTYPE HTML> + <html> + <head> + <title>Hello World</title> + </head> + <body> + <script></script> + </body> + </html>`; +} + +function handleRequest(request, response) { + var params = new URLSearchParams(request.queryString); + var action = params.get("action"); + + if (action === "create-blocked-iframe") { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.write(createBlockedIframePage()); + return; + } + + if (action === "redirect") { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.write(createPage()); + return; + } + + if (action === "reporturl") { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.write(createPage()); + return; + } + + if (action === "create-blocked-redirect") { + params.delete("action"); + params.append("action", "redirect"); + response.setStatusLine("1.1", 302, "found"); + response.setHeader( + "Location", + "https://" + REDIRECT + params.toString(), + false + ); + return; + } + + response.write("I don't know action " + action); +} diff --git a/toolkit/components/url-classifier/tests/mochitest/sandboxed.html b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html new file mode 100644 index 0000000000..3eb8870edd --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<body> +<script src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^ new file mode 100644 index 0000000000..4705ce9ded --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-scripts; diff --git a/toolkit/components/url-classifier/tests/mochitest/seek.webm b/toolkit/components/url-classifier/tests/mochitest/seek.webm Binary files differnew file mode 100644 index 0000000000..72b0297233 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/seek.webm diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_register.html b/toolkit/components/url-classifier/tests/mochitest/sw_register.html new file mode 100644 index 0000000000..2a536128fa --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/sw_register.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<script> +function waitForState(worker, state, context) { + return new Promise(resolve => { + function onStateChange() { + if (worker.state === state) { + worker.removeEventListener("statechange", onStateChange); + resolve(context); + } + } + + // First add an event listener, so we won't miss any change that happens + // before we check the current state. + worker.addEventListener("statechange", onStateChange); + + // Now check if the worker is already in the desired state. + onStateChange(); + }); +} + +(async function () { + dump("[Dimi]register sw...\n"); + let reg = await navigator.serviceWorker.register("sw_worker.js", {scope: "."}); + await waitForState(reg.installing, "activated", reg); + window.parent.postMessage({status: "registrationdone"}, "*"); +})(); + +</script> +</head> +<html> diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html b/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html new file mode 100644 index 0000000000..b94fa0baac --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.opener.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_worker.js b/toolkit/components/url-classifier/tests/mochitest/sw_worker.js new file mode 100644 index 0000000000..bad16608b4 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/sw_worker.js @@ -0,0 +1,10 @@ +self.addEventListener("fetch", function (event) { + let sep = "synth.html?"; + let idx = event.request.url.indexOf(sep); + if (idx > 0) { + let url = event.request.url.substring(idx + sep.length); + event.respondWith(fetch(url, { credentials: "include" })); + } else { + event.respondWith(fetch(event.request)); + } +}); diff --git a/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html new file mode 100644 index 0000000000..5fa49f628e --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html @@ -0,0 +1,138 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test advisory link (Bug #1366384)</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); +const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +var mainWindow = window.browsingContext.topChromeWindow; + +var testDatas = [ + { url: "itisaphishingsite.org/phishing.html", + list: "mochi1-phish-simple", + provider: "google", + }, + + { url: "fakeitisaphishingsite.org/phishing.html", + list: "fake1-phish-simple", + provider: "mozilla", + }, + + { url: "phishing.example.com/test.html", + list: "mochi2-phish-simple", + provider: "google4", + }, +]; + +let pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p}); + +function addUrlToDB(list, url) { + let testData = [{ db: list, url}]; + + return classifierHelper.addUrlToDB(testData) + .catch(function(err) { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +function setupTestData(data) { + let promises = []; + let providerList = "browser.safebrowsing.provider." + data.provider + ".lists"; + promises.push(pushPrefs([providerList, data.list])); + + let activeTablePref = "urlclassifier.phishTable"; + let activeTable = SpecialPowers.getCharPref(activeTablePref); + activeTable += "," + data.list; + promises.push(pushPrefs([activeTablePref, activeTable])); + + promises.push(addUrlToDB(data.list, data.url)); + return Promise.all(promises); +} + +function testOnWindow(aTestData) { + return new Promise(resolve => { + let win = mainWindow.OpenBrowserWindow(); + + (async function() { + await TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win); + + let browser = win.gBrowser.selectedBrowser; + BrowserTestUtils.startLoadingURIString(browser, aTestData.url); + await BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded"); + + let doc = win.gBrowser.contentDocument; + + // This test works on a document which uses Fluent. + // Fluent localization may finish later than DOMContentLoaded, + // so let's wait for `document.l10n.ready` promise to resolve. + await doc.l10n.ready; + let advisoryEl = doc.getElementById("advisory_provider"); + if (aTestData.provider != "google" && aTestData.provider != "google4") { + ok(!advisoryEl, "Advisory should not be shown"); + } else { + ok(advisoryEl, "Advisory element should exist"); + let expectedUrl = + SpecialPowers.getCharPref("browser.safebrowsing.provider." + + aTestData.provider + + ".advisoryURL"); + is(advisoryEl.href, expectedUrl, "Correct advisory url"); + let expectedText = + SpecialPowers.getCharPref("browser.safebrowsing.provider." + + aTestData.provider + + ".advisoryName"); + is(advisoryEl.text, expectedText, "correct advisory text"); + } + + win.close(); + resolve(); + })(); + }); +} + +SpecialPowers.pushPrefEnv( + {"set": [["browser.safebrowsing.phishing.enabled", true]]}, + test); + +function test() { + (async function() { + await classifierHelper.waitForInit(); + + for (let testData of testDatas) { + await setupTestData(testData); + await testOnWindow(testData); + await classifierHelper._cleanup(); + } + + SimpleTest.finish(); + })(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html new file mode 100644 index 0000000000..bb512e7cdc --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var Cc = SpecialPowers.Cc; +var Ci = SpecialPowers.Ci; + +const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); + +// Add https://allowlisted.example.com to the permissions manager +SpecialPowers.addPermission("trackingprotection", + Ci.nsIPermissionManager.ALLOW_ACTION, + { url: "https://allowlisted.example.com" }); + +async function clearPermissions() { + await SpecialPowers.removePermission("trackingprotection", + { url: "https://allowlisted.example.com" }); + ok(!await SpecialPowers.testPermission("trackingprotection", + Ci.nsIPermissionManager.ALLOW_ACTION, + { url: "https://allowlisted.example.com" })); +} + +SpecialPowers.pushPrefEnv( + {"set": [["urlclassifier.trackingTable", "moztest-track-simple"], + ["privacy.trackingprotection.enabled", true], + ["privacy.trackingprotection.testing.report_blocked_node", true], + ["channelclassifier.allowlist_example", true], + ["dom.security.skip_remote_script_assertion_in_system_priv_context", true]]}, + test); + +function test() { + SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers); + UrlClassifierTestUtils.addTestTrackers().then(() => { + document.getElementById("testFrame").src = "allowlistAnnotatedFrame.html"; + }); +} + +// Expected finish() call is in "allowlistedAnnotatedFrame.html". +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html b/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html new file mode 100644 index 0000000000..ad8e98005b --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the relationship between annotation vs TP</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="features.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING, + [ + ["privacy.trackingprotection.enabled", true], + ["privacy.trackingprotection.annotate_channels", true], + ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""], + ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""], + ["privacy.trackingprotection.fingerprinting.enabled", false], + ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""], + ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""], + ["privacy.trackingprotection.cryptomining.enabled", false], + ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""], + ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""], + ], + true /* a tracking resource */); +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html new file mode 100644 index 0000000000..2e528a7242 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html @@ -0,0 +1,265 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Test gethash.</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script src="head.js"></script> +<script class="testbody" type="text/javascript"> +const MALWARE_LIST = "mochi-malware-simple"; +const MALWARE_HOST1 = "malware.example.com/"; +const MALWARE_HOST2 = "test1.example.com/"; + +const UNWANTED_LIST = "mochi-unwanted-simple"; +const UNWANTED_HOST1 = "unwanted.example.com/"; +const UNWANTED_HOST2 = "test2.example.com/"; + + +const UNUSED_MALWARE_HOST = "unused.malware.com/"; +const UNUSED_UNWANTED_HOST = "unused.unwanted.com/"; + +const GETHASH_URL = + "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/gethash.sjs"; + +var gPreGethashCounter = 0; +var gCurGethashCounter = 0; + +var expectLoad = false; + +function loadTestFrame() { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.setAttribute("src", "gethashFrame.html"); + document.body.appendChild(iframe); + + iframe.onload = function() { + document.body.removeChild(iframe); + resolve(); + }; + }).then(getGethashCounter); +} + +function getGethashCounter() { + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest; + xhr.open("PUT", GETHASH_URL + "?gethashcount"); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + gPreGethashCounter = gCurGethashCounter; + gCurGethashCounter = parseInt(xhr.response); + resolve(); + } + }; + xhr.send(); + }); +} + +// setup function allows classifier send gethash request for test database +// also it calculate to fullhash for url and store those hashes in gethash sjs. +async function setup() { + await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL); + + return Promise.all([ + addCompletionToServer(MALWARE_LIST, MALWARE_HOST1, GETHASH_URL), + addCompletionToServer(MALWARE_LIST, MALWARE_HOST2, GETHASH_URL), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST1, GETHASH_URL), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST2, GETHASH_URL), + ]); +} + +// Reset function in helper try to simulate the behavior we restart firefox +function reset() { + return classifierHelper.resetDatabase() + .catch(err => { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +function updateUnusedUrl() { + var testData = [ + { url: UNUSED_MALWARE_HOST, db: MALWARE_LIST }, + { url: UNUSED_UNWANTED_HOST, db: UNWANTED_LIST }, + ]; + + return classifierHelper.addUrlToDB(testData) + .catch(err => { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +function addPrefixToDB() { + return update(true); +} + +function addCompletionToDB() { + return update(false); +} + +function update(prefix = false) { + var length = prefix ? 4 : 32; + var testData = [ + { url: MALWARE_HOST1, db: MALWARE_LIST, len: length }, + { url: MALWARE_HOST2, db: MALWARE_LIST, len: length }, + { url: UNWANTED_HOST1, db: UNWANTED_LIST, len: length }, + { url: UNWANTED_HOST2, db: UNWANTED_LIST, len: length }, + ]; + + return classifierHelper.addUrlToDB(testData) + .catch(err => { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +// This testcase is to make sure gethash works: +// 1. Add prefixes to DB. +// 2. Load test frame contains malware & unwanted url, those urls should be blocked. +// 3. The second step should also trigger a gethash request since completions is not in +// either cache or DB. +// 4. Load test frame again, since completions is stored in cache now, no gethash +// request should be triggered. +function testGethash() { + return Promise.resolve() + .then(addPrefixToDB) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); +}) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered."); +}) + .then(reset); +} + +// This testcae is to make sure completions in update works: +// 1. Add completions to DB. +// 2. Load test frame, since completions is stored in DB, gethash request should +// not be triggered. +function testUpdate() { + return Promise.resolve() + .then(addCompletionToDB) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered."); +}) + .then(reset); +} + +// This testcase is to make sure an update request will not clear completions in DB: +// 1. Add completions to DB. +// 2. Load test frame to make sure completions is stored in database, in this case, gethash +// should not be triggered. +// 3. Trigger an update, cache is cleared, but completions in DB should still remain. +// 4. Load test frame again, since completions is in DB, gethash request should not be triggered. +function testUpdateNotClearCompletions() { + return Promise.resolve() + .then(addCompletionToDB) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered."); +}) + .then(updateUnusedUrl) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered."); +}) + .then(reset); +} + +// This testcase is to make sure completion store in DB will properly load after restarting. +// 1. Add completions to DB. +// 2. Simulate firefox restart by calling reloadDatabase. +// 3. Load test frame, since completions should be loaded from DB, no gethash request should +// be triggered. +function testUpdateCompletionsAfterReload() { + return Promise.resolve() + .then(addCompletionToDB) + .then(classifierHelper.reloadDatabase) + // Call getTables to ensure the DB is fully reloaded before we load the test + // frame. + .then(classifierHelper.getTables) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered."); +}) + .then(reset); +} + +// This testcase is to make sure cache will be cleared after restarting +// 1. Add prefixes to DB. +// 2. Load test frame, this should trigger a gethash request and completions will be stored in +// cache. +// 3. Load test frame again, no gethash should be triggered because of cache. +// 4. Simulate firefox restart by calling reloadDatabase. +// 5. Load test frame again, since cache is cleared, gethash request should be triggered. +function testGethashCompletionsAfterReload() { + return Promise.resolve() + .then(addPrefixToDB) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); +}) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered."); +}) + .then(classifierHelper.reloadDatabase) + // Call getTables to ensure the DB is fully reloaded before we load the test + // frame. + .then(classifierHelper.getTables) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); +}) + .then(reset); +} + +function runTest() { + Promise.resolve() + .then(classifierHelper.waitForInit) + .then(setup) + .then(testGethash) + .then(testUpdate) + .then(testUpdateNotClearCompletions) + .then(testUpdateCompletionsAfterReload) + .then(testGethashCompletionsAfterReload) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); +} + +SimpleTest.waitForExplicitFinish(); + +// 'network.predictor.enabled' is disabled because if other testcase load +// evil.js, evil.css ...etc resources, it may cause we load them from cache +// directly and bypass classifier check +SpecialPowers.pushPrefEnv({"set": [ + ["browser.safebrowsing.malware.enabled", true], + ["urlclassifier.malwareTable", "mochi-malware-simple,mochi-unwanted-simple"], + ["network.predictor.enabled", false], + ["urlclassifier.gethash.timeout_ms", 30000], +]}, runTest); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html new file mode 100644 index 0000000000..2af0285884 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html @@ -0,0 +1,167 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Test gethash.</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script src="head.js"></script> +<script class="testbody" type="text/javascript"> +const MALWARE_LIST = "test-malware-simple"; +const MALWARE_HOST = "malware.example.com/"; + +const UNWANTED_LIST = "test-unwanted-simple"; +const UNWANTED_HOST = "unwanted.example.com/"; + +const GETHASH_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/cache.sjs"; + +var shouldLoad = false; + +var gPreGethashCounter = 0; +var gCurGethashCounter = 0; + +function loadTestFrame() { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.setAttribute("src", "gethashFrame.html"); + document.body.appendChild(iframe); + + iframe.onload = function() { + document.body.removeChild(iframe); + resolve(); + }; + }).then(getGethashCounter); +} + +function getGethashCounter() { + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest; + xhr.open("PUT", GETHASH_URL + "?gethashcount"); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + gPreGethashCounter = gCurGethashCounter; + gCurGethashCounter = parseInt(xhr.response); + resolve(); + } + }; + xhr.send(); + }); +} + +// add 4-bytes prefixes to local database, so when we access the url, +// it will trigger gethash request. +function addPrefixToDB(list, url) { + var testData = [{ db: list, url, len: 4 }]; + + return classifierHelper.addUrlToDB(testData) + .catch(function(err) { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +// manually reset DB to make sure next test won't be affected by cache. +function reset() { + return classifierHelper.resetDatabase(); +} + +// This test has to come before testPositiveCache to ensure gethash server doesn't +// contain completions. +function testNegativeCache() { + SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache(); + shouldLoad = true; + + async function setup() { + await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL); + + // Only add prefix to database. not server, so gethash will not return + // result. + return Promise.all([ + addPrefixToDB(MALWARE_LIST, MALWARE_HOST), + addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST), + ]); + } + + return Promise.resolve() + .then(setup) + .then(() => loadTestFrame()) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); +}) + // Second load should not trigger gethash request because cache. + .then(() => loadTestFrame()) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered."); +}) + .then(reset); +} + +function testPositiveCache() { + SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache(); + shouldLoad = false; + + async function setup() { + await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL); + + return Promise.all([ + addPrefixToDB(MALWARE_LIST, MALWARE_HOST), + addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST), + addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL), + ]); + } + + return Promise.resolve() + .then(setup) + .then(() => loadTestFrame()) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); +}) + // Second load should not trigger gethash request because cache. + .then(() => loadTestFrame()) + .then(() => { + ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered."); +}) + .then(reset); +} + +function runTest() { + Promise.resolve() + // This test resources get blocked when gethash returns successfully + .then(classifierHelper.waitForInit) + .then(testNegativeCache) + .then(testPositiveCache) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); +} + +SimpleTest.waitForExplicitFinish(); + +// 'network.predictor.enabled' is disabled because if other testcase load +// evil.js, evil.css ...etc resources, it may cause we load them from cache +// directly and bypass classifier check +SpecialPowers.pushPrefEnv({"set": [ + ["browser.safebrowsing.malware.enabled", true], + ["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"], + ["network.predictor.enabled", false], + ["urlclassifier.gethash.timeout_ms", 30000], +]}, runTest); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html new file mode 100644 index 0000000000..44a0c92549 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html @@ -0,0 +1,171 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; +var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html"; + +var Cc = SpecialPowers.Cc; +var Ci = SpecialPowers.Ci; + +const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function testOnWindow() { + return new Promise((resolve, reject) => { + let win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("OnLoadComplete", function innerLoad2(e) { + win.content.removeEventListener("OnLoadComplete", innerLoad2); + SimpleTest.executeSoon(function() { + checkLoads(win, JSON.parse(e.detail)); + resolve(win); + }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + }); + }); + }, {capture: true, once: true}); + }); +} + +var badids = [ + "badscript1", + "badscript2", + "badimage1", + "badimage2", + "badframe1", + "badframe2", + "badmedia1", + "badcss", +]; + +function checkLoads(aWindow, aData) { + var win = aWindow.content; + + is(aData.scriptItem1, "untouched", "Should not load tracking javascript"); + is(aData.scriptItem2, "untouched", "Should not load tracking javascript (2)"); + + is(aData.imageItem1, "untouched", "Should not load tracking images"); + is(aData.imageItem2, "untouched", "Should not load tracking images (2)"); + + is(aData.frameItem1, "untouched", "Should not load tracking iframes"); + is(aData.frameItem2, "untouched", "Should not load tracking iframes (2)"); + is(aData.mediaItem1, "error", "Should not load tracking videos"); + is(aData.xhrItem, "failed", "Should not load tracking XHRs"); + is(aData.fetchItem, "error", "Should not fetch from tracking URLs"); + + var elt = win.document.getElementById("styleCheck"); + var style = win.document.defaultView.getComputedStyle(elt); + isnot(style.visibility, "hidden", "Should not load tracking css"); + + is(win.document.blockedNodeByClassifierCount, badids.length, + "Should identify all tracking elements"); + + var blockedNodes = win.document.blockedNodesByClassifier; + + // Make sure that every node in blockedNodes exists in the tree + // (that may not always be the case but do not expect any nodes to disappear + // from the tree here) + var allNodeMatch = true; + for (let i = 0; i < blockedNodes.length; i++) { + let nodeMatch = false; + for (let j = 0; j < badids.length && !nodeMatch; j++) { + nodeMatch = nodeMatch || + (blockedNodes[i] == win.document.getElementById(badids[j])); + } + + allNodeMatch = allNodeMatch && nodeMatch; + } + ok(allNodeMatch, "All annotated nodes are expected in the tree"); + + // Make sure that every node with a badid (see badids) is found in the + // blockedNodes. This tells us if we are neglecting to annotate + // some nodes + allNodeMatch = true; + for (let j = 0; j < badids.length; j++) { + let nodeMatch = false; + for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) { + nodeMatch = nodeMatch || + (blockedNodes[i] == win.document.getElementById(badids[j])); + } + + if (!nodeMatch) { + console.log(badids[j] + " was not found in blockedNodes"); + } + allNodeMatch = allNodeMatch && nodeMatch; + } + ok(allNodeMatch, "All tracking nodes are expected to be annotated as such"); + + // End (parent) test. +} + +function cleanup() { + SpecialPowers.clearUserPref("privacy.trackingprotection.enabled"); + SpecialPowers.clearUserPref("channelclassifier.allowlist_example"); + SpecialPowers.clearUserPref("privacy.trackingprotection.testing.report_blocked_node"); + + UrlClassifierTestUtils.cleanupTestTrackers(); +} + +SpecialPowers.pushPrefEnv( + {"set": [["urlclassifier.trackingTable", "moztest-track-simple"]]}, + test); + +async function test() { + SimpleTest.registerCleanupFunction(cleanup); + await UrlClassifierTestUtils.addTestTrackers(); + + await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true); + // Make sure chrome:// URIs are processed. This does not white-list + // any URIs unless 'https://allowlisted.example.com' is added in the + // permission manager (see test_allowlisted_annotations.html) + await SpecialPowers.setBoolPref("channelclassifier.allowlist_example", true); + await SpecialPowers.setBoolPref("privacy.trackingprotection.testing.report_blocked_node", true); + + await testOnWindow().then(function(aWindow) { + aWindow.close(); + }); + + SimpleTest.finish(); +} + +// Expected finish() call is in "classifiedAnnotatedFrame.html". +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html new file mode 100644 index 0000000000..787b70798a --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html @@ -0,0 +1,141 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> +var firstLoad = true; + +// Add some URLs to the malware database. +var testData = [ + { url: "malware.mochi.test/", + db: "test-malware-simple", + }, + { url: "unwanted.mochi.test/", + db: "test-unwanted-simple", + }, + { url: "blocked.mochi.test/", + db: "test-block-simple", + }, + { url: "harmful.mochi.test/", + db: "test-harmful-simple", + }, + { url: "phish.mochi.test/firefox/its-a-trap.html", + db: "test-phish-simple", + }, +]; + +const Cc = SpecialPowers.Cc; +const Ci = SpecialPowers.Ci; +const Cr = SpecialPowers.Cr; + +var testURLs = [ + { url: "http://mochi.test", + table: "", + result: Cr.NS_OK, + }, + { url: "http://malware.mochi.test", + table: "test-malware-simple", + result: Cr.NS_ERROR_MALWARE_URI, + }, + { url: "http://unwanted.mochi.test", + table: "test-unwanted-simple", + result: Cr.NS_ERROR_UNWANTED_URI, + }, + { url: "http://phish.mochi.test/firefox/its-a-trap.html", + table: "test-phish-simple", + result: Cr.NS_ERROR_PHISHING_URI, + }, + { url: "http://harmful.mochi.test", + table: "test-harmful-simple", + result: Cr.NS_ERROR_HARMFUL_URI, + }, + { url: "http://blocked.mochi.test", + table: "test-block-simple", + result: Cr.NS_ERROR_BLOCKED_URI, + }, +]; + +function loadTestFrame() { + document.getElementById("testFrame").src = "classifierFrame.html"; +} + +// Expected finish() call is in "classifierFrame.html". +SimpleTest.waitForExplicitFinish(); + +function updateSuccess() { + return SpecialPowers.pushPrefEnv( + {"set": [["browser.safebrowsing.malware.enabled", true]]}); +} + +function updateError(errorCode) { + ok(false, "Couldn't update classifier. Error code: " + errorCode); + // Abort test. + SimpleTest.finish(); +} + +function testService() { + return new Promise(resolve => { + function runNextTest() { + if (!testURLs.length) { + resolve(); + return; + } + let test = testURLs.shift(); + let tables = "test-malware-simple,test-unwanted-simple,test-phish-simple,test-block-simple,test-harmful-simple"; + let uri = SpecialPowers.Services.io.newURI(test.url); + let prin = SpecialPowers.Services.scriptSecurityManager.createContentPrincipal(uri, {}); + SpecialPowers.doUrlClassify(prin, function(errorCode) { + is(errorCode, test.result, + `Successful asynchronous classification of ${test.url}`); + SpecialPowers.doUrlClassifyLocal(uri, tables, function(results) { + if (!results.length) { + is(test.table, "", + `Successful asynchronous local classification of ${test.url}`); + } else { + let result = results[0].QueryInterface(Ci.nsIUrlClassifierFeatureResult); + is(result.list, test.table, + `Successful asynchronous local classification of ${test.url}`); + } + runNextTest(); + }); + }); + } + runNextTest(resolve); + }); +} + +SpecialPowers.pushPrefEnv( + {"set": [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple,test-harmful-simple"], + ["urlclassifier.blockedTable", "test-block-simple"], + ["urlclassifier.phishTable", "test-phish-simple"], + ["browser.safebrowsing.phishing.enabled", true], + ["browser.safebrowsing.malware.enabled", true], + ["browser.safebrowsing.blockedURIs.enabled", true], + ["browser.safebrowsing.debug", true]]}, + function() { + classifierHelper.waitForInit() + .then(() => classifierHelper.addUrlToDB(testData)) + .then(updateSuccess) + .catch(err => { + updateError(err); + }) + .then(testService) + .then(loadTestFrame); + }); + +</script> + +</pre> +<iframe id="testFrame" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html new file mode 100644 index 0000000000..ea16c14e74 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html @@ -0,0 +1,157 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1281083 - Changing the urlclassifier.*Table prefs doesn't take effect before the next browser restart.</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script src="head.js"></script> +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; +var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html"; + +const testTable = "moz-track-digest256"; +const UPDATE_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/update.sjs"; + +var Cc = SpecialPowers.Cc; +var Ci = SpecialPowers.Ci; + +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +var timer = Cc["@mozilla.org/timer;1"] + .createInstance(Ci.nsITimer); + +// If default preference contain the table we want to test, +// We should change test table to a different one. +var trackingTables = SpecialPowers.getCharPref("urlclassifier.trackingTable").split(","); +ok(!trackingTables.includes(testTable), "test table should not be in the preference"); + +var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]. + getService(Ci.nsIUrlListManager); + +is(listmanager.getGethashUrl(testTable), "", + "gethash url for test table should be empty before setting to preference"); + +function checkLoads(aWindow, aBlocked) { + var win = aWindow.content; + is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript"); +} + +function testOnWindow() { + return new Promise((resolve, reject) => { + let win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("load", function innerLoad2(e) { + win.content.removeEventListener("load", innerLoad2); + SimpleTest.executeSoon(function() { + resolve(win); + }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + }); + }); + }, {capture: true, once: true}); + }); +} + +function setup() { + return new Promise(function(resolve, reject) { + // gethash url of test table "moz-track-digest256" should be updated + // after setting preference. + var url = listmanager.getGethashUrl(testTable); + var expected = SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.gethashURL"); + + is(url, expected, testTable + " matches its gethash url"); + + // Trigger update + listmanager.disableUpdate(testTable); + listmanager.enableUpdate(testTable); + listmanager.maybeToggleUpdateChecking(); + + // We wait until "nextupdattime" was set as a signal that update is complete. + waitForUpdateSuccess(function() { + resolve(); + }); + }); +} + +function waitForUpdateSuccess(callback) { + let nextupdatetime = + SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.nextupdatetime"); + + if (nextupdatetime !== "1") { + callback(); + return; + } + + timer.initWithCallback(function() { + waitForUpdateSuccess(callback); + }, 10, Ci.nsITimer.TYPE_ONE_SHOT); +} + +async function runTest() { + // To make sure url is not blocked by an already blocked url. + // Here we use non-tracking.example.com as a tracked url. + // Since this table is only used in this bug, so it won't affect other testcases. + await addCompletionToServer(testTable, "bug1281083.example.com/", UPDATE_URL); + + /** + * In this test we try to modify only urlclassifier.*Table preference to see if + * url specified in the table will be blocked after update. + */ + await SpecialPowers.pushPrefEnv( + {"set": [["urlclassifier.trackingTable", testTable]]}); + + await setup(); + + await testOnWindow().then(function(aWindow) { + checkLoads(aWindow, true); + aWindow.close(); + }); + + SimpleTest.finish(); +} + +// Set nextupdatetime to 1 to trigger an update +SpecialPowers.pushPrefEnv( + {"set": [["privacy.trackingprotection.enabled", true], + ["channelclassifier.allowlist_example", true], + ["browser.safebrowsing.provider.mozilla.nextupdatetime", "1"], + ["browser.safebrowsing.provider.mozilla.lists", testTable], + ["browser.safebrowsing.provider.mozilla.updateURL", UPDATE_URL]]}, + runTest); + +// Expected finish() call is in "bug_1281083.html". +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html new file mode 100644 index 0000000000..df3ac79d8a --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html @@ -0,0 +1,72 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Bug 1395411 - Changing the urlclassifier.*Table prefs doesn't remove them from the update checker.</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> +var Cc = SpecialPowers.Cc; +var Ci = SpecialPowers.Ci; + +const testTableV2 = "mochi1-phish-simple"; +const testTableV4 = "mochi1-phish-proto"; +const UPDATE_URL_V2 = "http://mochi.test:8888/tests/toolkit/components/url-classifier/dummyV2"; +const UPDATE_URL_V4 = "http://mochi.test:8888/tests/toolkit/components/url-classifier/dummyV4"; + +let listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]. + getService(Ci.nsIUrlListManager); + +let pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p}); + +SpecialPowers.pushPrefEnv( + {"set": [["browser.safebrowsing.phishing.enabled", true], + ["browser.safebrowsing.provider.mozilla.lists", testTableV2], + ["browser.safebrowsing.provider.mozilla4.lists", testTableV4], + ["browser.safebrowsing.provider.mozilla4.updateURL", UPDATE_URL_V4], + ["browser.safebrowsing.provider.mozilla.updateURL", UPDATE_URL_V2]]}, + runTest); + +function runTest() { + (async function() { + await classifierHelper.waitForInit(); + + await pushPrefs(["urlclassifier.phishTable", testTableV2 + "," + testTableV4]); + is(listmanager.getUpdateUrl(testTableV4), UPDATE_URL_V4, "Correct update url v4"); + is(listmanager.getUpdateUrl(testTableV2), UPDATE_URL_V2, "Correct update url v2"); + + await pushPrefs(["urlclassifier.phishTable", testTableV2]); + is(listmanager.getUpdateUrl(testTableV4), "", "Correct empty update url v4"); + is(listmanager.getUpdateUrl(testTableV2), UPDATE_URL_V2, "Correct update url v2"); + + await pushPrefs(["urlclassifier.phishTable", testTableV4]); + is(listmanager.getUpdateUrl(testTableV4), UPDATE_URL_V4, "Correct update url v4"); + is(listmanager.getUpdateUrl(testTableV2), "", "Correct empty update url v2"); + + await pushPrefs(["urlclassifier.phishTable", ""]); + is(listmanager.getUpdateUrl(testTableV4), "", "Correct empty update url v4"); + is(listmanager.getUpdateUrl(testTableV2), "", "Correct empty update url v2"); + + await classifierHelper._cleanup(); + + SimpleTest.finish(); + })(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html new file mode 100644 index 0000000000..f6ea460be1 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html @@ -0,0 +1,179 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier Matched Info (bug 1288633) </title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="application/javascript"> +var Cc = SpecialPowers.Cc; +var Ci = SpecialPowers.Ci; +var Cr = SpecialPowers.Cr; + +var inputDatas = [ + { url: "malware.mochi.test/", + db: "mochi-block-simple", + }, + { url: "malware1.mochi.test/", + db: "mochi1-block-simple", + }, + { url: "malware1.mochi.test/", + db: "mochi1-malware-simple", + provider: "mozilla", + }, + { url: "malware2.mochi.test/", + db: "mochi2-unwanted-simple", + provider: "mozilla", + }, + { url: "malware2.mochi.test/", + db: "mochi2-malware-simple", + provider: "mozilla", + }, + { url: "malware3.mochi.test/", + db: "mochig3-malware-simple", + provider: "google", + }, + { url: "malware3.mochi.test/", + db: "mochim3-malware-simple", + provider: "mozilla", + }, +]; + +function hash(str) { + let hasher = Cc["@mozilla.org/security/hash;1"] + .createInstance(Ci.nsICryptoHash); + + let data = new TextEncoder().encode(str); + hasher.init(hasher.SHA256); + hasher.update(data, data.length); + + return hasher.finish(false); +} + +var testDatas = [ + // Match empty provider + { url: "http://malware.mochi.test", + expect: { error: Cr.NS_ERROR_BLOCKED_URI, + table: "mochi-block-simple", + provider: "", + fullhash: (function() { + return hash("malware.mochi.test/"); + })(), + }, + }, + // Match multiple tables, only one has valid provider + { url: "http://malware1.mochi.test", + expect: { error: Cr.NS_ERROR_MALWARE_URI, + table: "mochi1-malware-simple", + provider: "mozilla", + fullhash: (function() { + return hash("malware1.mochi.test/"); + })(), + }, + }, + // Match multiple tables, handle order + { url: "http://malware2.mochi.test", + expect: { error: Cr.NS_ERROR_MALWARE_URI, + table: "mochi2-malware-simple", + provider: "mozilla", + fullhash: (function() { + return hash("malware2.mochi.test/"); + })(), + }, + }, + // Match multiple tables, handle order + { url: "http://malware3.mochi.test", + expect: { error: Cr.NS_ERROR_MALWARE_URI, + table: "mochig3-malware-simple", + provider: "google", + fullhash: (function() { + return hash("malware3.mochi.test/"); + })(), + }, + }, + +]; + +SimpleTest.waitForExplicitFinish(); + +function setupTestData(datas) { + let prefValues = {}; + for (let data of datas) { + if (!data.provider) { + continue; + } + let providerPref = "browser.safebrowsing.provider." + data.provider + ".lists"; + let prefValue; + if (!prefValues[providerPref]) { + prefValue = data.db; + } else { + prefValue = prefValues[providerPref] + "," + data.db; + } + prefValues[providerPref] = prefValue; + } + + // Convert map to array + let prefArray = []; + for (var pref in prefValues) { + prefArray.push([pref, prefValues[pref]]); + } + + let activeTablePref = "urlclassifier.malwareTable"; + let activeTable = SpecialPowers.getCharPref(activeTablePref); + for (let data of datas) { + activeTable += "," + data.db; + } + prefArray.push([activeTablePref, activeTable]); + + return SpecialPowers.pushPrefEnv({set: prefArray}); +} + +function runTest() { + return new Promise(resolve => { + function runNextTest() { + if (!testDatas.length) { + resolve(); + return; + } + let test = testDatas.shift(); + let uri = SpecialPowers.Services.io.newURI(test.url); + let prin = SpecialPowers.Services.scriptSecurityManager.createContentPrincipal(uri, {}); + SpecialPowers.doUrlClassify(prin, function(errorCode, table, provider, fullhash) { + is(errorCode, test.expect.error, `Test url ${test.url} correct error`); + is(table, test.expect.table, `Test url ${test.url} correct table`); + is(provider, test.expect.provider, `Test url ${test.url} correct provider`); + is(fullhash, btoa(test.expect.fullhash), `Test url ${test.url} correct full hash`); + runNextTest(); + }); + } + runNextTest(); + }); +} + +SpecialPowers.pushPrefEnv( + {"set": [["browser.safebrowsing.malware.enabled", true]]}, + function() { + classifierHelper.waitForInit() + .then(() => setupTestData(inputDatas)) + .then(() => classifierHelper.addUrlToDB(inputDatas)) + .then(runTest) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some tests failed with error " + e); + SimpleTest.finish(); + }); + }); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html new file mode 100644 index 0000000000..0e984c2a64 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html @@ -0,0 +1,73 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> +// Add some URLs to the malware database. +var testData = [ + { url: "example.com/tests/toolkit/components/url-classifier/tests/mochitest/evilWorker.js", + db: "test-malware-simple", + }, + { url: "example.com/tests/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js", + db: "test-unwanted-simple", + }, +]; + +function loadTestFrame() { + document.getElementById("testFrame").src = + "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/workerFrame.html"; +} + +function onmessage(event) { + var pieces = event.data.split(":"); + if (pieces[0] == "finish") { + SimpleTest.finish(); + return; + } + + is(pieces[0], "success", pieces[1]); +} + +function updateSuccess() { + SpecialPowers.pushPrefEnv( + {"set": [["browser.safebrowsing.malware.enabled", true]]}, + loadTestFrame); +} + +function updateError(errorCode) { + ok(false, "Couldn't update classifier. Error code: " + errorCode); + // Abort test. + SimpleTest.finish(); +} + +SpecialPowers.pushPrefEnv( + {"set": [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"]]}, + function() { + classifierHelper.waitForInit() + .then(() => classifierHelper.addUrlToDB(testData)) + .then(updateSuccess) + .catch(err => { + updateError(err); + }); + }); + +window.addEventListener("message", onmessage); + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html new file mode 100644 index 0000000000..c10a0c62f9 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html @@ -0,0 +1,156 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1442496</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> + +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1442496">Mozilla Bug 1442496</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script type="text/javascript" src="classifierHelper.js"></script> +<script class="testbody" type="text/javascript"> + +// To add a request to test, add the request in trackerFrame.html +// and the id of query string "?id=xxx" here. +const trackersAll = [ + "img-src", + "object-data", + "script-src", + "iframe-src", + "link-rel-stylesheet", + "video-src", + "track-src", + "ping", + "fetch", + "xmlhttprequest", + "send-beacon", + "fetch-in-sw", +]; + +const TRACKER_DOMAIN = "itisatracker.org"; +const TEST_TOP_DOMAIN = "example.com"; + +const TEST_TOP_PAGE = "trackerFrame.html"; +const TRACKER_SERVER = "trackerFrame.sjs"; + +const TEST_PATH = "/tests/toolkit/components/url-classifier/tests/mochitest/"; + +const TEST_TOP_SITE = "https:///" + TEST_TOP_DOMAIN + TEST_PATH; +const TRACKER_SITE = "https://" + TRACKER_DOMAIN + TEST_PATH; +const TRACKER_SJS = "https://" + TRACKER_DOMAIN + TEST_PATH + TRACKER_SERVER; + +// This function ask the server to set the cookie +async function setupAndRun(hasCookie, topLevelSite = TEST_TOP_SITE) { + // In order to apply the correct cookieBehavior, we need to first open a new + // window to setup cookies. So, the new window will use the correct + // cookieBehavior. Otherwise, it will use the default cookieBehavior. + let setup_win = window.open(); + await setup_win.fetch(TRACKER_SJS + "?init=" + trackersAll.length, { + credentials: "include", + }); + setup_win.close(); + + return new Promise(resolve => { + let win; + let query = hasCookie ? "with-cookie" : "without-cookie"; + fetch(TRACKER_SJS + "?callback=" + query).then(r => { + r.text().then((body) => { + let trackers_found = body.split(","); + for (let tracker of trackersAll) { + let description = "Tracker request " + tracker + "received " + + (hasCookie ? "with" : "without") + " cookie"; + ok(trackers_found.includes(tracker), description); + } + win.close(); + resolve(); + }); + }); + + win = window.open(topLevelSite + TEST_TOP_PAGE); + }); +} + +async function cleanup(topLevelSite = TEST_TOP_SITE) { + function clearServiceWorker() { + return new Promise(resolve => { + let w; + window.onmessage = function(e) { + if (e.data.status == "unregistrationdone") { + w.close(); + resolve(); + } + } + w = window.open(TEST_TOP_SITE + "sw_unregister.html"); + }); + } + + // Ensure we clear the stylesheet cache so that we re-classify. + SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache(); + + await clearServiceWorker(); +} + +async function runTests() { + await SpecialPowers.pushPrefEnv({set: [ + ["urlclassifier.trackingAnnotationTable.testEntries", TRACKER_DOMAIN], + ["urlclassifier.trackingAnnotationWhitelistTable", "test-trackwhite-simple"], + ["network.cookie.cookieBehavior", 4], + ["privacy.trackingprotection.enabled", false ], + ["privacy.trackingprotection.annotate_channels", false], + ["browser.send_pings", true], + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}); + + await classifierHelper.waitForInit(); + + let hasCookie; + + info("Test all requests should be sent with cookies when ETP is off"); + hasCookie = true; + await setupAndRun(hasCookie); + await cleanup(); + + info("Test all requests should be sent without cookies when ETP is on"); + await SpecialPowers.pushPrefEnv({set: [ + [ "privacy.trackingprotection.annotate_channels", true], + ]}); + + hasCookie = false; + await setupAndRun(hasCookie); + await cleanup(); + + info("Test all requests should be sent with cookies when top-level is in the whitelist"); + await classifierHelper.addUrlToDB([{ + url: TEST_TOP_DOMAIN + "/?resource=" + TRACKER_DOMAIN, + db: "test-trackwhite-simple", + }]); + + hasCookie = true; + await setupAndRun(hasCookie); + await cleanup(); + + info("Test all requests should be sent with cookies when the tracker is a first-party"); + hasCookie = true; + await setupAndRun(hasCookie, TRACKER_SITE); + await cleanup(TRACKER_SITE); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> + +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html new file mode 100644 index 0000000000..6a8a189ed2 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html @@ -0,0 +1,131 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1233914 - ping doesn't honor the TP list here.</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + SimpleTest.requestFlakyTimeout("Delay to make sure ping is made prior than XHR"); + + const timeout = 200; + const host_nottrack = "http://not-tracking.example.com/"; + const host_track = "http://trackertest.org/"; + const path_ping = "tests/toolkit/components/url-classifier/tests/mochitest/ping.sjs"; + const TP_ENABLE_PREF = "privacy.trackingprotection.enabled"; + const RETRY_TIMEOUT_MS = 200; + + async function testPingNonBlocklist() { + await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true); + + var msg = "ping should reach page not in blacklist"; + var expectPing = true; + var id = "1111"; + ping(id, host_nottrack); + + return new Promise(function(resolve, reject) { + // Retry at most 30 seconds. + isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS); + }); + } + + async function testPingBlocklistSafebrowsingOff() { + await SpecialPowers.setBoolPref(TP_ENABLE_PREF, false); + + var msg = "ping should reach page in blacklist when tracking protection is off"; + var expectPing = true; + var id = "2222"; + ping(id, host_track); + + return new Promise(function(resolve, reject) { + // Retry at most 30 seconds. + isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS); + }); + } + + async function testPingBlocklistSafebrowsingOn() { + await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true); + + var msg = "ping should not reach page in blacklist when tracking protection is on"; + var expectPing = false; + var id = "3333"; + ping(id, host_track); + + return new Promise(function(resolve, reject) { + setTimeout(function() { + isPinged(id, expectPing, msg, resolve); + }, timeout); + }); + } + + function ping(id, host) { + var elm = document.createElement("a"); + elm.setAttribute("ping", host + path_ping + "?id=" + id); + elm.setAttribute("href", "#"); + document.body.appendChild(elm); + + // Trigger ping. + elm.click(); + + document.body.removeChild(elm); + } + + function isPingedWithRetry(id, expected, msg, callback, retryCnt) { + var url = "http://mochi.test:8888/" + path_ping; + var xhr = new XMLHttpRequest(); + xhr.open("GET", url + "?id=" + id); + xhr.onload = function() { + let pinged = xhr.response === "ping"; + let success = pinged === expected; + if (success || 0 === retryCnt) { + is(expected, pinged, msg); + callback(); + return; + } + // Retry on failure. + setTimeout(() => { + isPingedWithRetry(id, expected, msg, callback, retryCnt - 1); + }, RETRY_TIMEOUT_MS); + }; + xhr.send(); + } + + function isPinged(id, expected, msg, callback) { + isPingedWithRetry(id, expected, msg, callback, 0); + } + + function cleanup() { + SpecialPowers.clearUserPref(TP_ENABLE_PREF); + } + + function runTest() { + classifierHelper.waitForInit() + .then(testPingNonBlocklist) + .then(testPingBlocklistSafebrowsingOff) + .then(testPingBlocklistSafebrowsingOn) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SimpleTest.registerCleanupFunction(cleanup); + SpecialPowers.pushPrefEnv({"set": [ + ["browser.send_pings", true], + ]}, runTest); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html new file mode 100644 index 0000000000..7e3ae97751 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1647681</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + +async function runTests() { + await SpecialPowers.pushPrefEnv({set: [ + ["privacy.trackingprotection.annotate_channels", true], + ]}); + + var chromeScript; + chromeScript = SpecialPowers.loadChromeScript(_ => { + /* eslint-env mozilla/chrome-script */ + function onExamResp(subject, topic, data) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel); + if ( + !channel || + !classifiedChannel || + !channel.URI.spec.startsWith( + "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" + ) + ) { + return; + } + + sendAsyncMessage("last-channel-flags", { + classified: Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING == classifiedChannel.classificationFlags, + }); + } + + addMessageListener("done", __ => { + Services.obs.removeObserver(onExamResp, "http-on-examine-response"); + }); + + Services.obs.addObserver(onExamResp, "http-on-examine-response"); + + sendAsyncMessage("start-test"); + }); + + chromeScript.addMessageListener( + "start-test", + async _ => { + window.open("https://example.org/tests/toolkit/components/url-classifier/tests/mochitest/sandboxed.html") + }, + { once: true } + ); + + chromeScript.addMessageListener( + "last-channel-flags", + data => { + ok(data.classified, "tracker script should be classified when the top-level is sandboxed"); + chromeScript.sendAsyncMessage("done"); + SimpleTest.finish(); + }, + { once: true } + ); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + + </script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html new file mode 100644 index 0000000000..cbb6e67c78 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html @@ -0,0 +1,163 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1262406 - Track element doesn't use the URL classifier.</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + const PREF = "browser.safebrowsing.malware.enabled"; + const track_path = "tests/toolkit/components/url-classifier/tests/mochitest/basic.vtt"; + const malware_url = "http://malware.example.com/" + track_path; + const validtrack_url = "http://mochi.test:8888/" + track_path; + + var video = document.createElement("video"); + video.src = "seek.webm"; + video.crossOrigin = "anonymous"; + + document.body.appendChild(video); + + function testValidTrack() { + SpecialPowers.setBoolPref(PREF, true); + + return new Promise(function(resolve, reject) { + var track = document.createElement("track"); + track.track.mode = "hidden"; + track.src = validtrack_url; + video.appendChild(track); + + function onload() { + ok(true, "Track should be loaded when url is not in blocklist"); + finish(); + } + + function onerror() { + ok(false, "Error while loading track"); + finish(); + } + + function finish() { + track.removeEventListener("load", onload); + track.removeEventListener("error", onerror); + resolve(); + } + + track.addEventListener("load", onload); + track.addEventListener("error", onerror); + }); + } + + function testBlocklistTrackSafebrowsingOff() { + SpecialPowers.setBoolPref(PREF, false); + + return new Promise(function(resolve, reject) { + var track = document.createElement("track"); + track.track.mode = "hidden"; + track.src = malware_url; + video.appendChild(track); + + function onload() { + ok(true, "Track should be loaded when url is in blocklist and safebrowsing is off"); + finish(); + } + + function onerror() { + ok(false, "Error while loading track"); + finish(); + } + + function finish() { + track.removeEventListener("load", onload); + track.removeEventListener("error", onerror); + resolve(); + } + + track.addEventListener("load", onload); + track.addEventListener("error", onerror); + }); + } + + function testBlocklistTrackSafebrowsingOn() { + SpecialPowers.setBoolPref(PREF, true); + + return new Promise(function(resolve, reject) { + var track = document.createElement("track"); + track.track.mode = "hidden"; + // Add a query string parameter here to avoid url classifier bypass classify + // because of cache. + track.src = malware_url + "?testsbon"; + video.appendChild(track); + + function onload() { + ok(false, "Unexpected result while loading track in blocklist"); + finish(); + } + + function onerror() { + ok(true, "Track should not be loaded when url is in blocklist and safebrowsing is on"); + finish(); + } + + function finish() { + track.removeEventListener("load", onload); + track.removeEventListener("error", onerror); + resolve(); + } + + track.addEventListener("load", onload); + track.addEventListener("error", onerror); + }); + } + + function cleanup() { + SpecialPowers.clearUserPref(PREF); + } + + function setup() { + var testData = [ + { url: "malware.example.com/", + db: "test-malware-simple", + }, + ]; + + return classifierHelper.addUrlToDB(testData) + .catch(function(err) { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); + } + + function runTest() { + Promise.resolve() + .then(classifierHelper.waitForInit) + .then(setup) + .then(testValidTrack) + .then(testBlocklistTrackSafebrowsingOff) + .then(testBlocklistTrackSafebrowsingOn) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SimpleTest.registerCleanupFunction(cleanup); + SpecialPowers.pushPrefEnv({"set": [ + ["urlclassifier.malwareTable", "test-malware-simple"], + ]}, runTest); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html new file mode 100644 index 0000000000..0098d6c489 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the cryptomining classifier</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +var tests = [ + // All disabled. + { config: [ false, false ], loadExpected: true }, + + // Just entitylisted. + { config: [ false, true ], loadExpected: true }, + + // Just blocklisted. + { config: [ true, false ], loadExpected: false }, + + // entitylist + blocklist: entitylist wins + { config: [ true, true ], loadExpected: true }, +]; + +function prefValue(value, what) { + return value ? what : ""; +} + +async function runTest(test) { + await SpecialPowers.pushPrefEnv({set: [ + [ "urlclassifier.features.cryptomining.blacklistHosts", prefValue(test.config[0], "example.com") ], + [ "urlclassifier.features.cryptomining.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ], + [ "urlclassifier.features.cryptomining.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ], + [ "urlclassifier.features.cryptomining.whitelistTables", "" ], + [ "privacy.trackingprotection.enabled", false ], + [ "privacy.trackingprotection.annotate_channels", false ], + [ "privacy.trackingprotection.cryptomining.enabled", true ], + [ "privacy.trackingprotection.emailtracking.enabled", false ], + [ "privacy.trackingprotection.fingerprinting.enabled", false ], + [ "privacy.trackingprotection.socialtracking.enabled", false ], + ]}); + + info("Testing: " + JSON.stringify(test.config) + "\n"); + + // Let's load an image with a random query string, just to avoid network cache. + let result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.loadExpected, "The loading happened correctly"); + + // Let's load an image with a random query string, just to avoid network cache. + result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.loadExpected, "The loading happened correctly (by table)"); +} + +async function runTests() { + let chromeScript = SpecialPowers.loadChromeScript(_ => { + /* eslint-env mozilla/chrome-script */ + const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" + ); + + addMessageListener("loadTrackers", __ => { + return UrlClassifierTestUtils.addTestTrackers(); + }); + + addMessageListener("unloadTrackers", __ => { + UrlClassifierTestUtils.cleanupTestTrackers(); + }); + }); + + await chromeScript.sendQuery("loadTrackers"); + + for (let test in tests) { + await runTest(tests[test]); + } + + await chromeScript.sendQuery("unloadTrackers"); + + chromeScript.destroy(); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html new file mode 100644 index 0000000000..ecf5cd02a4 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the relationship between annotation vs blocking - cryptomining</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="features.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_CRYPTOMINING, + [ + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.annotate_channels", false], + ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""], + ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""], + ["privacy.trackingprotection.fingerprinting.enabled", false], + ["privacy.trackingprotection.cryptomining.enabled", true], + ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""], + ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""], + ["privacy.trackingprotection.socialtracking.enabled", false], + ["privacy.trackingprotection.emailtracking.enabled", false], + ], + false /* a tracking resource */); +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html new file mode 100644 index 0000000000..96278ae49d --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html @@ -0,0 +1,141 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1258033 - Fix the DNT loophole for tracking protection</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; + +const tests = [ + // DNT turned on and TP turned off, DNT signal sent in both private browsing + // and normal mode. + { + setting: {dntPref: true, tpPref: false, tppbPref: false, pbMode: true}, + expected: {dnt: "1"}, + }, + { + setting: {dntPref: true, tpPref: false, tppbPref: false, pbMode: false}, + expected: {dnt: "1"}, + }, + // DNT turned off and TP turned on globally, DNT signal sent in both private + // browsing and normal mode. + { + setting: {dntPref: false, tpPref: true, tppbPref: false, pbMode: true}, + expected: {dnt: "1"}, + }, + { + setting: {dntPref: false, tpPref: true, tppbPref: false, pbMode: false}, + expected: {dnt: "1"}, + }, + // DNT turned off and TP in Private Browsing only, DNT signal sent in private + // browsing mode only. + { + setting: {dntPref: false, tpPref: false, tppbPref: true, pbMode: true}, + expected: {dnt: "1"}, + }, + { + setting: {dntPref: false, tpPref: false, tppbPref: true, pbMode: false}, + expected: {dnt: "unspecified"}, + }, + // DNT turned off and TP turned off, DNT signal is never sent. + { + setting: {dntPref: false, tpPref: false, tppbPref: false, pbMode: true}, + expected: {dnt: "unspecified"}, + }, + { + setting: {dntPref: false, tpPref: false, tppbPref: false, pbMode: false}, + expected: {dnt: "unspecified"}, + }, +]; + +const DNT_PREF = "privacy.donottrackheader.enabled"; +const TP_PREF = "privacy.trackingprotection.enabled"; +const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled"; + +const contentPage = + "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/dnt.html"; + +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function executeTest(test) { + SpecialPowers.pushPrefEnv({"set": [ + [DNT_PREF, test.setting.dntPref], + [TP_PREF, test.setting.tpPref], + [TP_PB_PREF, test.setting.tppbPref], + ]}); + + var win = mainWindow.OpenBrowserWindow({private: test.setting.pbMode}); + + return new Promise(function(resolve, reject) { + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + return; + } + + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("message", function(event) { + let [key, value] = event.data.split("="); + if (key == "finish") { + win.close(); + resolve(); + } else if (key == "navigator.doNotTrack") { + is(value, test.expected.dnt, "navigator.doNotTrack should be " + test.expected.dnt); + } else if (key == "DNT") { + let msg = test.expected.dnt == "1" ? "" : "not "; + is(value, test.expected.dnt, "DNT header should " + msg + "be sent"); + } else { + ok(false, "unexpected message"); + } + }); + }, true); + SimpleTest.executeSoon(function() { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + }); + }); + }, {capture: true, once: true}); + }); +} + +let loop = function loop(index) { + if (index >= tests.length) { + SimpleTest.finish(); + return; + } + + let test = tests[index]; + let next = function next() { + loop(index + 1); + }; + let result = executeTest(test); + result.then(next, next); +}; + +SimpleTest.waitForExplicitFinish(); +loop(0); + +</script> + +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html b/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html new file mode 100644 index 0000000000..fd2996997c --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html @@ -0,0 +1,130 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the email tracking classifier</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +var tests = [ + // All disabled. + { config: [ false, false ], loadExpected: true }, + + // Just entitylisted. + { config: [ false, true ], loadExpected: true }, + + // Just blocklisted. + { config: [ true, false ], loadExpected: false }, + + // entitylist + blocklist: entitylist wins + { config: [ true, true ], loadExpected: true }, +]; + +function prefValue(value, what) { + return value ? what : ""; +} + +async function runTest(test) { + await SpecialPowers.pushPrefEnv({set: [ + [ "urlclassifier.features.emailtracking.blocklistHosts", prefValue(test.config[0], "example.com") ], + [ "urlclassifier.features.emailtracking.allowlistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ], + [ "urlclassifier.features.emailtracking.blocklistTables", prefValue(test.config[0], "mochitest5-track-simple") ], + [ "urlclassifier.features.emailtracking.allowlistTables", "" ], + [ "privacy.trackingprotection.enabled", false ], + [ "privacy.trackingprotection.annotate_channels", false ], + [ "privacy.trackingprotection.cryptomining.enabled", false ], + [ "privacy.trackingprotection.emailtracking.enabled", true ], + [ "privacy.trackingprotection.fingerprinting.enabled", false ], + [ "privacy.trackingprotection.socialtracking.enabled", false ], + ]}); + + info("Testing: " + JSON.stringify(test.config) + "\n"); + + // Let's load an image with a random query string to avoid network cache. + let result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.loadExpected, "Image loading happened correctly"); + + // Let's load an image with a random query string to avoid network cache. + result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://email-tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.loadExpected, "Image loading happened correctly (by table)"); + + // Let's load a script with a random query string to avoid network cache. + result = await new Promise(resolve => { + let script = document.createElement("script"); + script.setAttribute( + "src", + "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" + + Math.random() + ); + script.onload = _ => resolve(true); + script.onerror = _ => resolve(false); + document.body.appendChild(script); + }); + + is(result, test.loadExpected, "Script loading happened correctly"); + + // Let's load a script with a random query string to avoid network cache. + result = await new Promise(resolve => { + let script = document.createElement("script"); + script.setAttribute( + "src", + "http://email-tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" + + Math.random() + ); + script.onload = _ => resolve(true); + script.onerror = _ => resolve(false); + document.body.appendChild(script); + }); + + is(result, test.loadExpected, "Script loading happened correctly (by table)"); +} + +async function runTests() { + let chromeScript = SpecialPowers.loadChromeScript(_ => { + /* eslint-env mozilla/chrome-script */ + const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" + ); + + addMessageListener("loadTrackers", __ => { + return UrlClassifierTestUtils.addTestTrackers(); + }); + + addMessageListener("unloadTrackers", __ => { + UrlClassifierTestUtils.cleanupTestTrackers(); + }); + }); + + await chromeScript.sendQuery("loadTrackers"); + + for (let test in tests) { + await runTest(tests[test]); + } + + await chromeScript.sendQuery("unloadTrackers"); + + chromeScript.destroy(); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html new file mode 100644 index 0000000000..81e6f9a466 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html @@ -0,0 +1,130 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the fingerprinting classifier</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +var tests = [ + // All disabled. + { config: [ false, false ], imgLoadExpected: true, scriptLoadExpected: true }, + + // Just entitylisted. + { config: [ false, true ], imgLoadExpected: true, scriptLoadExpected: true }, + + // Just blocklisted. + { config: [ true, false ], imgLoadExpected: true, scriptLoadExpected: false }, + + // entitylist + blocklist: entitylist wins + { config: [ true, true ], imgLoadExpected: true, scriptLoadExpected: true }, +]; + +function prefValue(value, what) { + return value ? what : ""; +} + +async function runTest(test) { + await SpecialPowers.pushPrefEnv({set: [ + [ "urlclassifier.features.fingerprinting.blacklistHosts", prefValue(test.config[0], "example.com") ], + [ "urlclassifier.features.fingerprinting.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ], + [ "urlclassifier.features.fingerprinting.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ], + [ "urlclassifier.features.fingerprinting.whitelistTables", "" ], + [ "privacy.trackingprotection.enabled", false ], + [ "privacy.trackingprotection.annotate_channels", false ], + [ "privacy.trackingprotection.cryptomining.enabled", false ], + [ "privacy.trackingprotection.emailtracking.enabled", false ], + [ "privacy.trackingprotection.fingerprinting.enabled", true ], + [ "privacy.trackingprotection.socialtracking.enabled", false ], + ]}); + + info("Testing: " + JSON.stringify(test.config) + "\n"); + + // Let's load an image with a random query string to avoid network cache. + let result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.imgLoadExpected, "Image loading happened correctly"); + + // Let's load an image with a random query string to avoid network cache. + result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.imgLoadExpected, "Image loading happened correctly (by table)"); + + // Let's load a script with a random query string to avoid network cache. + result = await new Promise(resolve => { + let script = document.createElement("script"); + script.setAttribute( + "src", + "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" + + Math.random() + ); + script.onload = _ => resolve(true); + script.onerror = _ => resolve(false); + document.body.appendChild(script); + }); + + is(result, test.scriptLoadExpected, "Script loading happened correctly"); + + // Let's load a script with a random query string to avoid network cache. + result = await new Promise(resolve => { + let script = document.createElement("script"); + script.setAttribute( + "src", + "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" + + Math.random() + ); + script.onload = _ => resolve(true); + script.onerror = _ => resolve(false); + document.body.appendChild(script); + }); + + is(result, test.scriptLoadExpected, "Script loading happened correctly"); +} + +async function runTests() { + let chromeScript = SpecialPowers.loadChromeScript(_ => { + /* eslint-env mozilla/chrome-script */ + const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" + ); + + addMessageListener("loadTrackers", __ => { + return UrlClassifierTestUtils.addTestTrackers(); + }); + + addMessageListener("unloadTrackers", __ => { + UrlClassifierTestUtils.cleanupTestTrackers(); + }); + }); + + await chromeScript.sendQuery("loadTrackers"); + + for (let test in tests) { + await runTest(tests[test]); + } + + await chromeScript.sendQuery("unloadTrackers"); + + chromeScript.destroy(); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html new file mode 100644 index 0000000000..0bc01645b1 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the relationship between annotation vs blocking - fingerprinting</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="features.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_FINGERPRINTING, + [ + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.annotate_channels", false], + ["privacy.trackingprotection.fingerprinting.enabled", true], + ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""], + ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""], + ["privacy.trackingprotection.cryptomining.enabled", false], + ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""], + ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""], + ["privacy.trackingprotection.socialtracking.enabled", false], + ["privacy.trackingprotection.emailtracking.enabled", false], + ], + true /* a tracking resource */); +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_gethash.html b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html new file mode 100644 index 0000000000..b9d3a2fd44 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html @@ -0,0 +1,118 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Test gethash.</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +<iframe id="testFrame1" onload=""></iframe> +<iframe id="testFrame2" onload=""></iframe> + +<script src="head.js"></script> +<script class="testbody" type="text/javascript"> +const MALWARE_LIST = "test-malware-simple"; +const MALWARE_HOST = "malware.example.com/"; + +const UNWANTED_LIST = "test-unwanted-simple"; +const UNWANTED_HOST = "unwanted.example.com/"; + +const GETHASH_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/gethash.sjs"; +const NOTEXIST_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/nonexistserver.sjs"; + +var shouldLoad = false; + +// In this testcase we store prefixes to localdb and send the fullhash to gethash server. +// When access the test page gecko should trigger gethash request to server and +// get the completion response. +function loadTestFrame(id) { + return new Promise(function(resolve, reject) { + var iframe = document.getElementById(id); + iframe.setAttribute("src", "gethashFrame.html"); + + iframe.onload = function() { + resolve(); + }; + }); +} + +// add 4-bytes prefixes to local database, so when we access the url, +// it will trigger gethash request. +function addPrefixToDB(list, url) { + var testData = [{ db: list, url, len: 4 }]; + + return classifierHelper.addUrlToDB(testData) + .catch(function(err) { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +function setup404() { + shouldLoad = true; + + return Promise.all([ + classifierHelper.allowCompletion( + [MALWARE_LIST, UNWANTED_LIST], NOTEXIST_URL), + addPrefixToDB(MALWARE_LIST, MALWARE_HOST), + addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST), + ]); +} + +function setup() { + return Promise.all([ + classifierHelper.allowCompletion( + [MALWARE_LIST, UNWANTED_LIST], GETHASH_URL), + addPrefixToDB(MALWARE_LIST, MALWARE_HOST), + addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST), + addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL), + ]); +} + +// manually reset DB to make sure next test won't be affected by cache. +function reset() { + return classifierHelper.resetDatabase(); +} + +function runTest() { + Promise.resolve() + // This test resources get blocked when gethash returns successfully + .then(classifierHelper.waitForInit) + .then(setup) + .then(() => loadTestFrame("testFrame1")) + .then(reset) + // This test resources are not blocked when gethash returns an error + .then(setup404) + .then(() => loadTestFrame("testFrame2")) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); +} + +SimpleTest.waitForExplicitFinish(); + +// 'network.predictor.enabled' is disabled because if other testcase load +// evil.js, evil.css ...etc resources, it may cause we load them from cache +// directly and bypass classifier check +SpecialPowers.pushPrefEnv({"set": [ + ["browser.safebrowsing.malware.enabled", true], + ["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"], + ["network.predictor.enabled", false], + ["urlclassifier.gethash.timeout_ms", 30000], +]}, runTest); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html new file mode 100644 index 0000000000..0959ecf42e --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html @@ -0,0 +1,153 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test Tracking Protection in Private Browsing mode</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; +var contentPage = "https://www.itisatrap.org/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html"; + +const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function testOnWindow(aPrivate) { + return new Promise((resolve, reject) => { + let win = mainWindow.OpenBrowserWindow({private: aPrivate}); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("load", function innerLoad2() { + win.content.removeEventListener("load", innerLoad2); + SimpleTest.executeSoon(function() { + resolve(win); + }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + }); + }); + }, {capture: true, once: true}); + }); +} + +var badids = [ + "badscript", + "badimage", + "badcss", +]; + +function checkLoads(aWindow, aBlocked) { + var win = aWindow.content; + is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript"); + is(win.document.getElementById("badimage").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking images"); + is(win.document.getElementById("goodscript").dataset.touched, "yes", "Should load entitylisted tracking javascript"); + is(win.document.getElementById("goodimage").dataset.touched, "yes", "Should load non-blocklisted image"); + + var elt = win.document.getElementById("styleCheck"); + var style = win.document.defaultView.getComputedStyle(elt); + isnot(style.visibility, aBlocked ? "hidden" : "", "Should not load tracking css"); + + is(win.document.blockedNodeByClassifierCount, aBlocked ? badids.length : 0, "Should identify all tracking elements"); + + var blockedNodes = win.document.blockedNodesByClassifier; + + // Make sure that every node in blockedNodes exists in the tree + // (that may not always be the case but do not expect any nodes to disappear + // from the tree here) + var allNodeMatch = true; + for (let i = 0; i < blockedNodes.length; i++) { + let nodeMatch = false; + for (let j = 0; j < badids.length && !nodeMatch; j++) { + nodeMatch = nodeMatch || + (blockedNodes[i] == win.document.getElementById(badids[j])); + } + + allNodeMatch = allNodeMatch && nodeMatch; + } + is(allNodeMatch, true, "All annotated nodes are expected in the tree"); + + // Make sure that every node with a badid (see badids) is found in the + // blockedNodes. This tells us if we are neglecting to annotate + // some nodes + allNodeMatch = true; + for (let j = 0; j < badids.length; j++) { + let nodeMatch = false; + for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) { + nodeMatch = nodeMatch || + (blockedNodes[i] == win.document.getElementById(badids[j])); + } + + allNodeMatch = allNodeMatch && nodeMatch; + } + is(allNodeMatch, aBlocked, "All tracking nodes are expected to be annotated as such"); +} + +SpecialPowers.pushPrefEnv( + {"set": [ + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.pbmode.enabled", true], + ["privacy.trackingprotection.testing.report_blocked_node", true], + ]}, test); + +async function test() { + SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers); + await UrlClassifierTestUtils.addTestTrackers(); + + // Normal mode, with the pref (trackers should be loaded) + await testOnWindow(false).then(function(aWindow) { + checkLoads(aWindow, false); + aWindow.close(); + }); + + // Private Browsing, with the pref (trackers should be blocked) + await testOnWindow(true).then(function(aWindow) { + checkLoads(aWindow, true); + aWindow.close(); + }); + + // Private Browsing, without the pref (trackers should be loaded) + await SpecialPowers.setBoolPref("privacy.trackingprotection.pbmode.enabled", false); + await testOnWindow(true).then(function(aWindow) { + checkLoads(aWindow, false); + aWindow.close(); + }); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html new file mode 100644 index 0000000000..36d128cdf7 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html @@ -0,0 +1,217 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test report matched URL info (Bug #1288633)</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> +const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); +; +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +var mainWindow = window.browsingContext.topChromeWindow; +const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs"; +const BASE_URL = "http://" + SJS + "?"; + +var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p}); + +function addUrlToDB(list, url) { + let testData = [{ db: list, url}]; + + return classifierHelper.addUrlToDB(testData) + .catch(function(err) { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +function setupTestData(data) { + let promises = []; + let providerList = "browser.safebrowsing.provider." + data.provider + ".lists"; + if (!Services.prefs.prefHasUserValue(providerList)) { + promises.push(pushPrefs([providerList, data.list])); + } else { + let pref = SpecialPowers.getCharPref(providerList); + pref += "," + data.list; + promises.push(pushPrefs([providerList, pref])); + } + + let activeTablePref = "urlclassifier.phishTable"; + let activeTable = SpecialPowers.getCharPref(activeTablePref); + activeTable += "," + data.list; + promises.push(pushPrefs([activeTablePref, activeTable])); + + promises.push(addUrlToDB(data.list, data.testUrl)); + return Promise.all(promises); +} + +function testOnWindow(aTestData, aCallback, aTestCreater) { + return new Promise(resolve => { + let win = mainWindow.OpenBrowserWindow(); + + (async function() { + await TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win); + + let browser = win.gBrowser.selectedBrowser; + aTestCreater(win, browser, aTestData.topUrl, aTestData.testUrl); + + let notification = await BrowserTestUtils.waitForNotificationBar(win.gBrowser, browser, "blocked-badware-page"); + ok(notification, "Notification box should be displayed"); + + let buttons = notification.buttonContainer.getElementsByTagName("button"); + let button = buttons[1]; + if (aTestData.provider != "google" && aTestData.provider != "google4") { + is(button, undefined, "Report button should not be showed"); + win.close(); + resolve(); + return; + } + + button.click(); + + let newTabBrowser = win.gBrowser.selectedTab.linkedBrowser; + await BrowserTestUtils.browserLoaded(newTabBrowser); + + aCallback(newTabBrowser); + win.close(); + resolve(); + })(); + }); +} + +var createBlockedIframe = function(aWindow, aBrowser, aTopUrl, aUrl) { + (async function() { + BrowserTestUtils.startLoadingURIString(aBrowser, aTopUrl); + await BrowserTestUtils.browserLoaded(aBrowser); + + await SpecialPowers.spawn(aBrowser, [aUrl], async function(url) { + return new Promise(resolve => { + let listener = e => { + docShell.chromeEventHandler.removeEventListener("AboutBlockedLoaded", listener, false, true); + resolve(); + }; + docShell.chromeEventHandler.addEventListener("AboutBlockedLoaded", listener, false, true); + let frame = content.document.getElementById("phishingFrame"); + frame.setAttribute("src", "http://" + url); + }); + }); + + let doc = aWindow.gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument; + let ignoreWarningLink = doc.getElementById("ignore_warning_link"); + ok(ignoreWarningLink, "Ignore warning link should exist"); + ignoreWarningLink.click(); + })(); +}; + +var createBlockedPage = function(aWindow, aBrowser, aTopUrl, aUrl) { + (async function() { + BrowserTestUtils.startLoadingURIString(aBrowser, aTopUrl); + await BrowserTestUtils.waitForContentEvent(aBrowser, "DOMContentLoaded"); + + let doc = aWindow.gBrowser.contentDocument; + let ignoreWarningLink = doc.getElementById("ignore_warning_link"); + ok(ignoreWarningLink, "Ignore warning link should exist"); + ignoreWarningLink.click(); + })(); +}; + +function checkReportURL(aReportBrowser, aUrl) { + let expectedReportUrl = BASE_URL + "action=reporturl&reporturl=" + encodeURIComponent(aUrl); + is(aReportBrowser.contentDocument.location.href, expectedReportUrl, "Correct report URL"); +} + +var testDatas = [ + { topUrl: "http://itisaphishingsite.org/phishing.html", + testUrl: "itisaphishingsite.org/phishing.html", + list: "mochi1-phish-simple", + provider: "google", + blockCreater: createBlockedPage, + expectedReportUri: "http://itisaphishingsite.org/phishing.html", + }, + + // Non-google provider, no report button is showed. + // Test provider needs a valid update URL (mozilla for example) otherwise + // the updates inserting the test data will fail. + { topUrl: "http://fakeitisaphishingsite.org/phishing.html", + testUrl: "fakeitisaphishingsite.org/phishing.html", + list: "fake-phish-simple", + provider: "mozilla", + blockCreater: createBlockedPage, + }, + + // Iframe case: + // A top level page at + // http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe + // contains an iframe to http://phishing.example.com/test.html (blocked). + + { topUrl: "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe", + testUrl: "phishing.example.com/test.html", + list: "mochi2-phish-simple", + provider: "google4", + blockCreater: createBlockedIframe, + expectedReportUri: "http://phishing.example.com/test.html", + }, + + // Redirect case: + // A top level page at + // http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect (blocked) + // will get redirected to + // https://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect. + { topUrl: "http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect", + testUrl: "prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect", + list: "mochi3-phish-simple", + provider: "google4", + blockCreater: createBlockedPage, + expectedReportUri: "http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs", + }, + +]; + +SpecialPowers.pushPrefEnv( + {"set": [["browser.safebrowsing.provider.google.reportPhishMistakeURL", BASE_URL + "action=reporturl&reporturl="], + ["browser.safebrowsing.provider.google4.reportPhishMistakeURL", BASE_URL + "action=reporturl&reporturl="], + ["browser.safebrowsing.phishing.enabled", true]]}, + test); + +function test() { + (async function() { + await classifierHelper.waitForInit(); + + for (let testData of testDatas) { + await setupTestData(testData); + await testOnWindow(testData, function(browser) { + checkReportURL(browser, testData.expectedReportUri); + }, testData.blockCreater); + + await classifierHelper._cleanup(); + } + + SimpleTest.finish(); + })(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html new file mode 100644 index 0000000000..475145591d --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Only tables with provider could register gethash url in listmanager.</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var Cc = SpecialPowers.Cc; +var Ci = SpecialPowers.Ci; + +// List all the tables +const prefs = [ + "urlclassifier.phishTable", + "urlclassifier.malwareTable", + "urlclassifier.downloadBlockTable", + "urlclassifier.downloadAllowTable", + "urlclassifier.trackingTable", + "urlclassifier.trackingWhitelistTable", + "urlclassifier.blockedTable", +]; + +// Get providers +var providers = {}; + +var branch = SpecialPowers.Services.prefs.getBranch("browser.safebrowsing.provider."); +var children = branch.getChildList(""); + +for (var child of children) { + var prefComponents = child.split("."); + var providerName = prefComponents[0]; + providers[providerName] = {}; +} + +// Get lists from |browser.safebrowsing.provider.PROVIDER_NAME.lists| preference. +var listsWithProvider = []; +var listsToProvider = []; +for (let provider in providers) { + let pref = "browser.safebrowsing.provider." + provider + ".lists"; + let list = SpecialPowers.getCharPref(pref).split(","); + + listsToProvider = listsToProvider.concat(list.map( () => { return provider; })); + listsWithProvider = listsWithProvider.concat(list); +} + +// Get all the lists +var lists = []; +for (let pref of prefs) { + lists = lists.concat(SpecialPowers.getCharPref(pref).split(",")); +} + +var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]. + getService(Ci.nsIUrlListManager); + +let googleKey = SpecialPowers.Services.urlFormatter.formatURL("%GOOGLE_SAFEBROWSING_API_KEY%").trim(); + +for (let list of lists) { + if (!list) + continue; + + // For lists having a provider, it should have a correct gethash url + // For lists without a provider, for example, moztest-malware-simple, it should not + // have a gethash url. + var url = listmanager.getGethashUrl(list); + var index = listsWithProvider.indexOf(list); + if (index >= 0) { + let provider = listsToProvider[index]; + let pref = "browser.safebrowsing.provider." + provider + ".gethashURL"; + if ((provider == "google" || provider == "google4") && + (!googleKey || googleKey == "no-google-safebrowsing-api-key")) { + is(url, "", "getHash url of " + list + " should be empty"); + } else { + is(url, SpecialPowers.getCharPref(pref), list + " matches its gethash url"); + } + } else { + is(url, "", list + " should not have a gethash url"); + } +} + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html new file mode 100644 index 0000000000..ef1114686f --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html @@ -0,0 +1,109 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the socialtracking classifier</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +var tests = [ + // All disabled. + { config: [ false, false ], loadExpected: true }, + + // Just entitylisted. + { config: [ false, true ], loadExpected: true }, + + // Just blocklisted. + { config: [ true, false ], loadExpected: false }, + + // entitylist + blocklist: entitylist wins + { config: [ true, true ], loadExpected: true }, +]; + +function prefValue(value, what) { + return value ? what : ""; +} + +async function runTest(test) { + await SpecialPowers.pushPrefEnv({set: [ + [ "urlclassifier.features.socialtracking.blacklistHosts", prefValue(test.config[0], "example.com") ], + [ "urlclassifier.features.socialtracking.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ], + [ "urlclassifier.features.socialtracking.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ], + [ "urlclassifier.features.socialtracking.whitelistTables", "" ], + [ "privacy.trackingprotection.enabled", false ], + [ "privacy.trackingprotection.annotate_channels", false ], + [ "privacy.trackingprotection.cryptomining.enabled", false ], + [ "privacy.trackingprotection.emailtracking.enabled", false ], + [ "privacy.trackingprotection.fingerprinting.enabled", false ], + [ "privacy.trackingprotection.socialtracking.enabled", true ], + ]}); + + info("Testing: " + JSON.stringify(test.config) + "\n"); + + // Let's load an image with a random query string, just to avoid network cache. + let result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.loadExpected, "The loading happened correctly"); + + // Let's load an image with a random query string, just to avoid network cache. + result = await new Promise(resolve => { + let image = new Image(); + image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random(); + image.onload = _ => resolve(true); + image.onerror = _ => resolve(false); + }); + + is(result, test.loadExpected, "The loading happened correctly (by table)"); +} + +async function runTests() { + let chromeScript = SpecialPowers.loadChromeScript(_ => { + /* eslint-env mozilla/chrome-script */ + const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" + ); + + addMessageListener("loadTrackers", __ => { + UrlClassifierTestUtils.addTestTrackers().then(___ => { + sendAsyncMessage("trackersLoaded"); + }); + }); + + addMessageListener("unloadTrackers", __ => { + UrlClassifierTestUtils.cleanupTestTrackers(); + sendAsyncMessage("trackersUnloaded"); + }); + }); + + await new Promise(resolve => { + chromeScript.addMessageListener("trackersLoaded", resolve); + chromeScript.sendAsyncMessage("loadTrackers"); + }); + + for (let test in tests) { + await runTest(tests[test]); + } + + await new Promise(resolve => { + chromeScript.addMessageListener("trackersUnloaded", resolve); + chromeScript.sendAsyncMessage("unloadTrackers"); + }); + + chromeScript.destroy(); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html new file mode 100644 index 0000000000..f12dcf11ec --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the relationship between annotation vs blocking - socialtracking</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="features.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_SOCIALTRACKING, + [ + ["privacy.socialtracking.block_cookies.enabled", false], + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.annotate_channels", false], + ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""], + ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""], + ["privacy.trackingprotection.fingerprinting.enabled", false], + ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""], + ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""], + ["privacy.trackingprotection.cryptomining.enabled", false], + ["privacy.trackingprotection.socialtracking.enabled", true], + ["privacy.trackingprotection.emailtracking.enabled", false], + ], + false /* a tracking resource */); +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html new file mode 100644 index 0000000000..341d26fa3f --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html @@ -0,0 +1,235 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test threathit repoty </title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script src="head.js"></script> +<script class="testbody" type="text/javascript"> +const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +var mainWindow = window.browsingContext.topChromeWindow; + +var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]. + getService(Ci.nsIUrlListManager); +const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/threathit.sjs"; + +function hash(str) { + let hasher = Cc["@mozilla.org/security/hash;1"] + .createInstance(Ci.nsICryptoHash); + + let data = new TextEncoder().encode(str); + hasher.init(hasher.SHA256); + hasher.update(data, data.length); + + return hasher.finish(false); +} + +var testDatas = [ + { url: "itisaphishingsite1.org/phishing.html", + list: "test-phish-proto", + provider: "test", + // The base64 of binary protobuf representation of response: + // + // [ + // { + // 'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC + // 'response_type': 2, // FULL_UPDATE + // 'new_client_state': 'sta\x0te', // NEW_CLIENT_STATE + // 'additions': { 'compression_type': RAW, + // 'prefix_size': 1, + // 'raw_hashes': "xxxx"} // hash prefix of url itisaphishingsite.org/phishing.html + // 'minimumWaitDuration': "8.1s", + // } + // ] + // + updateProtobuf: "ChoIAiACKgwIARIICAQSBM9UdYs6BnN0YQB0ZRIECAwQCg==", + // The base64 of binary protobuf representation of response: + // { + // "matches": [ + // { + // "threat_type": 2, // SOCIAL_ENGINEERING_PUBLIC + // "threat": { + // "hash": string, + // }, + // "cacheDuration": "8.1", + // } + // ], + // "minimumWaitDuration": 12.0..1, + // "negativeCacheDuration": 12.0..1, + // } + fullhashProtobuf: "CiwIAhoiCiDPVHWLptJSc/UYiabk1/wo5OkJqbggiylVKISK28bfeSoECAwQChIECAwQChoECAwQCg==", + }, +]; + +function addDataV4ToServer(list, type, data) { + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest; + let params = new URLSearchParams(); + params.append("action", "store"); + params.append("list", list); + params.append("type", type); + params.append("data", data); + + xhr.open("PUT", "http://" + SJS + "?" + params.toString(), true); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + resolve(); + } + }; + xhr.send(); + }); +} +/** + * Grabs the results via XHR + */ +function checkResults(aTestdata, aExpected) { + let xhr = new XMLHttpRequest(); + xhr.responseType = "text"; + xhr.onload = function() { + is(aExpected, xhr.response, "Correct report request"); + SimpleTest.finish(); + }; + xhr.onerror = function() { + ok(false, "Can't get results from server."); + SimpleTest.finish(); + }; + let params = new URLSearchParams(); + params.append("action", "getreport"); + params.append("list", aTestdata.list); + let url = "http://" + SJS + "?" + params.toString(); + + xhr.open("GET", url, true); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.send(); +} + +function waitForUpdate(data) { + listmanager.checkForUpdates(data.updateUrl); + return new Promise(resolve => { + Services.obs.addObserver(function observer(aSubject, aTopic) { + Services.obs.removeObserver(observer, aTopic); + resolve(); + }, "safebrowsing-update-finished"); + }); +} + +function addUpdateDataV4ToServer(list, data) { + return addDataV4ToServer(list, "update", data); +} + +function addFullhashV4DataToServer(list, data) { + return addDataV4ToServer(list, "fullhash", data); +} + +function setupTestData(data) { + let updateParams = new URLSearchParams(); + updateParams.append("action", "get"); + updateParams.append("list", data.list); + updateParams.append("type", "update"); + data.updateUrl = "http://" + SJS + "?" + updateParams.toString(); + + let gethashParams = new URLSearchParams(); + gethashParams.append("action", "get"); + gethashParams.append("list", data.list); + gethashParams.append("type", "fullhash"); + data.gethashUrl = "http://" + SJS + "?" + gethashParams.toString(); + + listmanager.registerTable(data.list, + data.provider, + data.updateUrl, + data.gethashUrl); + + let promises = []; + let activeTablePref = "urlclassifier.phishTable"; + let activeTable = SpecialPowers.getCharPref(activeTablePref); + activeTable += "," + data.list; + + let reportPref = "browser.safebrowsing.provider." + data.provider + ".dataSharingURL"; + let reportParams = new URLSearchParams(); + reportParams.append("action", "report"); + reportParams.append("list", data.list); + data.reportUrl = "http://" + SJS + "?" + reportParams.toString(); + + let reportEnabledPref = "browser.safebrowsing.provider." + data.provider + ".dataSharing.enabled"; + + promises.push(pushPrefs([reportPref, data.reportUrl])); + promises.push(pushPrefs([reportEnabledPref, true])); + promises.push(pushPrefs([activeTablePref, activeTable])); + promises.push(addUpdateDataV4ToServer(data.list, data.updateProtobuf)); + promises.push(addFullhashV4DataToServer(data.list, data.fullhashProtobuf)); + return Promise.all(promises); +} + +function testOnWindow(aTestData) { + return new Promise(resolve => { + let win = mainWindow.OpenBrowserWindow(); + + (async function() { + await new Promise(rs => whenDelayedStartupFinished(win, rs)); + + let expected; + let browser = win.gBrowser.selectedBrowser; + let progressListener = { + onContentBlockingEvent(aWebProgress, aRequest, aEvent) { + expected = aTestData.reportUrl; + }, + QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]), + }; + win.gBrowser.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING); + + BrowserTestUtils.startLoadingURIString(browser, aTestData.url); + await BrowserTestUtils.browserLoaded( + browser, + false, + `http://${aTestData.url}`, + true + ); + checkResults(aTestData, expected); + win.close(); + resolve(); + })(); + }); +} +SpecialPowers.pushPrefEnv( + {"set": [ + ["browser.safebrowsing.phishing.enabled", true], + ["dom.testing.sync-content-blocking-notifications", true], + ]}, + test); + +function test() { + (async function() { + await classifierHelper.waitForInit(); + + for (let testData of testDatas) { + await setupTestData(testData); + await waitForUpdate(testData); + await testOnWindow(testData); + await classifierHelper._cleanup(); + } + })(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html new file mode 100644 index 0000000000..098d6098d9 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test Tracking Protection with and without Safe Browsing (Bug #1157081)</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; +var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html"; + +const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function testOnWindow(aCallback) { + var win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("load", function innerLoad2() { + win.content.removeEventListener("load", innerLoad2); + SimpleTest.executeSoon(function() { aCallback(win); }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { + win.gBrowser.loadURI(Services.io.newURI(contentPage), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}), + }); + }); + }); + }, {capture: true, once: true}); +} + +var badids = [ + "badscript", +]; + +function checkLoads(aWindow, aBlocked) { + var win = aWindow.content; + is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript"); +} + +SpecialPowers.pushPrefEnv( + {"set": [["urlclassifier.trackingTable", "moztest-track-simple"], + ["privacy.trackingprotection.enabled", true], + ["browser.safebrowsing.malware.enabled", false], + ["browser.safebrowsing.phishing.enabled", false], + ["channelclassifier.allowlist_example", true]]}, + test); + +function test() { + SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers); + UrlClassifierTestUtils.addTestTrackers().then(() => { + // Safe Browsing turned OFF, tracking protection should work nevertheless + testOnWindow(function(aWindow) { + checkLoads(aWindow, true); + aWindow.close(); + + // Safe Browsing turned ON, tracking protection should still work + SpecialPowers.setBoolPref("browser.safebrowsing.phishing.enabled", true); + testOnWindow(function(aWindow1) { + checkLoads(aWindow1, true); + aWindow1.close(); + SimpleTest.finish(); + }); + }); + }); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html new file mode 100644 index 0000000000..31c69ac293 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html @@ -0,0 +1,164 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test Bug 1312515</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> + +<p><b>To see more of what is happening: <code>export MOZ_LOG=nsChannelClassifier:3</code></b></p> + +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; +var contentPage = "http://www.itisatrap.org/chrome/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html"; + +const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); +const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function testOnWindow(aPrivate, aCallback) { + var win = mainWindow.OpenBrowserWindow({private: aPrivate}); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("load", function innerLoad2() { + win.content.removeEventListener("load", innerLoad2); + SimpleTest.executeSoon(function() { aCallback(win); }); + }, false, true); + }, true); + SimpleTest.executeSoon(function () { + BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage); + }); + }); + }, {capture: true, once: true}); +} + +const topic = "http-on-before-connect"; +var testUrl; +var testWindow; +var resolve; + +function checkLowestPriority(aSubject) { + checkPriority(aSubject, checkLowestPriority, Ci.nsISupportsPriority.PRIORITY_LOWEST, "Priority should be lowest."); +} + +function checkNormalPriority(aSubject) { + checkPriority(aSubject, checkNormalPriority, Ci.nsISupportsPriority.PRIORITY_NORMAL, "Priority should be normal."); +} + +function checkPriority(aSubject, aCallback, aPriority, aMessage) { + var channel = aSubject.QueryInterface(Ci.nsIChannel); + info("Channel classified: " + channel.name); + if (channel.name !== testUrl) { + return; + } + + SpecialPowers.removeObserver(aCallback, topic); + + var p = aSubject.QueryInterface(Ci.nsISupportsPriority); + is(p.priority, aPriority, aMessage); + + info("Resolving promise for " + channel.name); + resolve(); +} + +function testXHR1() { + return new Promise(function(aResolve, aReject) { + testUrl = "http://tracking.example.com/"; + info("Not blocklisted: " + testUrl); + resolve = aResolve; + SpecialPowers.addObserver(checkNormalPriority, topic); + testWindow.content.postMessage({type: "doXHR", url: testUrl}, "*"); + }); +} + +function testXHR2() { + return new Promise(function(aResolve, aReject) { + testUrl = "http://trackertest.org/"; + info("Blocklisted and not entitylisted: " + testUrl); + resolve = aResolve; + SpecialPowers.addObserver(checkLowestPriority, topic); + testWindow.content.postMessage({type: "doXHR", url: testUrl}, "*"); + }); +} + +function testFetch1() { + return new Promise(function(aResolve, aReject) { + testUrl = "http://itisatracker.org/"; // only entitylisted in TP, not for annotations + info("Blocklisted and not entitylisted: " + testUrl); + resolve = aResolve; + SpecialPowers.addObserver(checkLowestPriority, topic); + testWindow.content.postMessage({type: "doFetch", url: testUrl}, "*"); + }); +} + +function testFetch2() { + return new Promise(function(aResolve, aReject) { + testUrl = "http://tracking.example.org/"; // only entitylisted for annotations, not in TP + info("Blocklisted but also entitylisted: " + testUrl); + resolve = aResolve; + SpecialPowers.addObserver(checkNormalPriority, topic); + testWindow.content.postMessage({type: "doFetch", url: testUrl}, "*"); + }); +} + +function endTest() { + info("Finishing up..."); + testWindow.close(); + testWindow = null; + SimpleTest.finish(); +} + +async function test() { + await SpecialPowers.pushPrefEnv( + {"set": [["network.http.tailing.enabled", false], + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.annotate_channels", true], + ["privacy.trackingprotection.lower_network_priority", true], + ["dom.security.https_first", false]]}); + await UrlClassifierTestUtils.addTestTrackers(); + testOnWindow(false, async function(aWindow) { + testWindow = aWindow; + await testXHR1(); + await testXHR2(); + await testFetch1(); + await testFetch2(); + await endTest(); + }); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.registerCleanupFunction(function() { + info("Cleaning up prefs..."); + UrlClassifierTestUtils.cleanupTestTrackers(); +}); +test(); + +</script> + +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html new file mode 100644 index 0000000000..75d69b2596 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html @@ -0,0 +1,102 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test Tracking Protection in Private Browsing mode</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="classifierHelper.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.window.browsingContext.topChromeWindow; +var contentPage1 = "http://www.example.com/chrome/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html"; + +const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function testOnWindow(contentPage) { + return new Promise((resolve, reject) => { + var win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("load", function innerLoad2() { + win.content.removeEventListener("load", innerLoad2); + SimpleTest.executeSoon(function() { + resolve(win); + }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { + BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage); + }); + }); + }, {capture: true, once: true}); + }); +} + +var testData = [ + { url: "apps.fbsbx.com/", + db: "test-track-simple", + }, + { url: "www.example.com/?resource=apps.fbsbx.com", + db: "test-trackwhite-simple", + }, +]; + +function checkLoads(aWindow, aWhitelisted) { + var win = aWindow.content; + + is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript"); +} + +SpecialPowers.pushPrefEnv( + // Disable STS preloadlist because apps.fbsbx.com is in the list. + {"set": [["privacy.trackingprotection.enabled", true], + ["urlclassifier.trackingTable", "test-track-simple"], + ["urlclassifier.trackingWhitelistTable", "test-trackwhite-simple"], + ["dom.security.https_first", false], + ["network.stricttransportsecurity.preloadlist", false]]}, + test); + +async function test() { + await classifierHelper.waitForInit(); + await classifierHelper.addUrlToDB(testData); + + // Load the test from a URL on the whitelist + await testOnWindow(contentPage1).then(function(aWindow) { + checkLoads(aWindow, true); + aWindow.close(); + }); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html new file mode 100644 index 0000000000..69ca337c33 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html @@ -0,0 +1,165 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> + <title>Test Tracking Protection in Private Browsing mode</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> + +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script class="testbody" type="text/javascript"> + +var mainWindow = window.browsingContext.topChromeWindow; +var contentPage1 = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html"; +var contentPage2 = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html"; + +const {UrlClassifierTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); +const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); +const {TestUtils} = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +function testOnWindow(contentPage) { + return new Promise((resolve, reject) => { + var win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function() { + TestUtils.topicObserved("browser-delayed-startup-finished", + subject => subject == win).then(() => { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener("load", function innerLoad2() { + win.content.removeEventListener("load", innerLoad2); + SimpleTest.executeSoon(function() { + resolve(win); + }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { + BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage); + }); + }); + }, {capture: true, once: true}); + }); +} + +var alwaysbadids = [ + "badscript", +]; + +function checkLoads(aWindow, aWhitelisted, tpEnabled) { + var win = aWindow.content; + if (!tpEnabled) { + is(win.document.getElementById("badscript").dataset.touched, "yes", "Should load tracking javascript"); + is(win.document.blockedNodeByClassifierCount, 0, "Should not identify any tracking elements"); + return; + } + + is(win.document.getElementById("badscript").dataset.touched, "no", "Should not load tracking javascript"); + is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript"); + + var badids = alwaysbadids.slice(); + if (!aWhitelisted) { + badids.push("goodscript"); + } + is(win.document.blockedNodeByClassifierCount, badids.length, "Should identify all tracking elements"); + + var blockedNodes = win.document.blockedNodesByClassifier; + + // Make sure that every node in blockedNodes exists in the tree + // (that may not always be the case but do not expect any nodes to disappear + // from the tree here) + var allNodeMatch = true; + for (let i = 0; i < blockedNodes.length; i++) { + let nodeMatch = false; + for (let j = 0; j < badids.length && !nodeMatch; j++) { + nodeMatch = nodeMatch || + (blockedNodes[i] == win.document.getElementById(badids[j])); + } + + allNodeMatch = allNodeMatch && nodeMatch; + } + is(allNodeMatch, true, "All annotated nodes are expected in the tree"); + + // Make sure that every node with a badid (see badids) is found in the + // blockedNodes. This tells us if we are neglecting to annotate + // some nodes + allNodeMatch = true; + for (let j = 0; j < badids.length; j++) { + let nodeMatch = false; + for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) { + nodeMatch = nodeMatch || + (blockedNodes[i] == win.document.getElementById(badids[j])); + } + + allNodeMatch = allNodeMatch && nodeMatch; + } + is(allNodeMatch, true, "All tracking nodes are expected to be annotated as such"); +} + +SpecialPowers.pushPrefEnv( + {"set": [["privacy.trackingprotection.enabled", true], + ["privacy.trackingprotection.testing.report_blocked_node", true], + ["dom.security.https_first", false], + ["channelclassifier.allowlist_example", true]]}, + test); + +async function test() { + SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers); + await UrlClassifierTestUtils.addTestTrackers(); + + // Load the test from a URL that's NOT on the whitelist with tracking protection disabled + await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", false); + await testOnWindow(contentPage2).then(function(aWindow) { + checkLoads(aWindow, false, false); + aWindow.close(); + }); + await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true); + + // Load the test from a URL that's NOT on the whitelist + await testOnWindow(contentPage2).then(function(aWindow) { + checkLoads(aWindow, false, true); + aWindow.close(); + }); + + // Load the test from a URL on the whitelist + await testOnWindow(contentPage1).then(function(aWindow) { + checkLoads(aWindow, true, true); + aWindow.close(); + }); + + // Load the test from a URL on the whitelist but without the whitelist + await SpecialPowers.setCharPref("urlclassifier.trackingWhitelistTable", ""); + await testOnWindow(contentPage1).then(function(aWindow) { + checkLoads(aWindow, false, true); + aWindow.close(); + }); + await SpecialPowers.clearUserPref("urlclassifier.trackingWhitelistTable"); + await SpecialPowers.clearUserPref("privacy.trackingprotection.enabled"); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" width="100%" height="100%" onload=""></iframe> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/threathit.sjs b/toolkit/components/url-classifier/tests/mochitest/threathit.sjs new file mode 100644 index 0000000000..a083a2e247 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/threathit.sjs @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function handleRequest(request, response) { + let params = new URLSearchParams(request.queryString); + var action = params.get("action"); + + var responseBody; + + // Store data in the server side. + if (action == "store") { + // In the server side we will store: + // All the full hashes or update for a given list + let state = params.get("list") + params.get("type"); + let dataStr = params.get("data"); + setState(state, dataStr); + } else if (action == "get") { + let state = params.get("list") + params.get("type"); + responseBody = atob(getState(state)); + response.setStatusLine(request.httpVersion, 200, "OK"); + response.bodyOutputStream.write(responseBody, responseBody.length); + } else if (action == "report") { + let state = params.get("list") + "report"; + let requestUrl = + request.scheme + + "://" + + request.host + + ":" + + request.port + + request.path + + "?" + + request.queryString; + setState(state, requestUrl); + } else if (action == "getreport") { + let state = params.get("list") + "report"; + responseBody = getState(state); + response.setHeader("Content-Type", "text/plain", false); + response.write(responseBody); + } +} diff --git a/toolkit/components/url-classifier/tests/mochitest/track.html b/toolkit/components/url-classifier/tests/mochitest/track.html new file mode 100644 index 0000000000..8785e7c5b1 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/track.html @@ -0,0 +1,7 @@ +<html> + <head> + </head> + <body> + <h1>Tracking Works!</h1> + </body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html new file mode 100644 index 0000000000..73409b5cda --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> +<meta charset="utf-8"> +<title></title> +</head> +<body> +<div id="content" style="display: none"> + +<img src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=img-src"> + +<!--nsObjectLoadingContent::OpenChannel--> +<object data="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=object-data"></object> + +<!--ScriptLoader::StartLoad--> +<script src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=script-src"></script> + +<!--nsDocShell::DoURILoad--> +<iframe src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=iframe-src"></iframe> + +<!--Loader::LoadSheet--> +<link rel="stylesheet" href="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=link-rel-stylesheet" /> + +<!--nsPrefetchNode::OpenChannel--> +<!-- Temporarily disable this because it doesn't work in fission when the scheme is https --> +<!--<link rel="prefetch" href="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=link-rel-prefetch" />--> + +<!--HTMLMediaElement::ChannelLoader::LoadInternal--> +<video src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=video-src"> +</video> + +<video src="https://mochi.test:8888/basic.vtt", crossorigin=use-credentials> + <track default src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=track-src" ></track> +</video> + +<!--SendPing--> +<a ping="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=ping" id="a-ping" href="#"></a> +<script> + (function() { + document.getElementById("a-ping").click(); + })(); +</script> + +<script> + +// FetchDriver::HttpFetch +(function() { + try { + fetch(new Request("https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=fetch"), { + credentials: "include", + }); + } catch (err) { + console.log(err); + } +})(); + +// XMLHttpRequestMainThread::CreateChannel +(function() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=xmlhttprequest"); + xhr.withCredentials = true; + xhr.send(); +})(); + +// Navigator::SendBeaconInternal +(function() { + navigator.sendBeacon("https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=send-beacon"); +})(); + +</script> + +// Fetch inside service worker's script +<iframe id="sw" src="https://example.com/tests/toolkit/components/url-classifier/tests/mochitest/sw_register.html"></iframe> +<script> + let iframe = document.getElementById("sw"); + window.onmessage = function(e) { + if (e.data.status == "registrationdone") { + iframe.remove(); + iframe = document.createElement("iframe"); + document.getElementById("content").appendChild(iframe); + iframe.src = "https://example.com/tests/toolkit/components/url-classifier/tests/mochitest/synth.html?" + + "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=fetch-in-sw"; + } + }; +</script> + +</div> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs new file mode 100644 index 0000000000..be31e666b0 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs @@ -0,0 +1,80 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const stateTotalRequests = "total-request"; +const stateCallback = "callback-response"; +const stateTrackersWithCookie = "trackers-with-cookie"; +const stateTrackersWithoutCookie = "trackers-without-cookie"; +const stateReceivedTrackers = "received-trackers"; +const stateResponseType = "response-tracker-with-cookie"; + +function reset() { + setState(stateCallback, ""); + setState(stateTrackersWithCookie, ""); + setState(stateTrackersWithoutCookie, ""); + setState(stateReceivedTrackers, ""); + setState(stateResponseType, ""); +} + +function handleRequest(aRequest, aResponse) { + let params = new URLSearchParams(aRequest.queryString); + + // init the server and tell the server the total number requests to process + // server set the cookie + if (params.has("init")) { + setState(stateTotalRequests, params.get("init")); + + aResponse.setStatusLine(aRequest.httpVersion, 200); + aResponse.setHeader("Content-Type", "text/plain", false); + + // Prepare the cookie + aResponse.setHeader("Set-Cookie", "cookie=1234; SameSite=None; Secure"); + aResponse.setHeader( + "Access-Control-Allow-Origin", + aRequest.getHeader("Origin"), + false + ); + aResponse.setHeader("Access-Control-Allow-Credentials", "true", false); + aResponse.write("begin-test"); + // register the callback response, the response will be fired after receiving + // all the request + } else if (params.has("callback")) { + aResponse.processAsync(); + aResponse.setHeader("Content-Type", "text/plain", false); + aResponse.setHeader( + "Access-Control-Allow-Origin", + aRequest.getHeader("Origin"), + false + ); + aResponse.setHeader("Access-Control-Allow-Credentials", "true", false); + + setState(stateResponseType, params.get("callback")); + setObjectState(stateCallback, aResponse); + } else { + let count = parseInt(getState(stateReceivedTrackers) || 0) + 1; + setState(stateReceivedTrackers, count.toString()); + + let state = ""; + if (aRequest.hasHeader("Cookie")) { + state = stateTrackersWithCookie; + } else { + state = stateTrackersWithoutCookie; + } + + let ids = params.get("id").concat(",", getState(state)); + setState(state, ids); + + if (getState(stateTotalRequests) == getState(stateReceivedTrackers)) { + getObjectState(stateCallback, r => { + if (getState(stateResponseType) == "with-cookie") { + r.write(getState(stateTrackersWithCookie)); + } else { + r.write(getState(stateTrackersWithoutCookie)); + } + r.finish(); + reset(); + }); + } + } +} diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html new file mode 100644 index 0000000000..ea0f92c481 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> +<title></title> + +</head> +<body> + +<!-- + This domain is not blocklisted for annotations but it is for tracking protection. + Therefore if tracking protection is accidentally enabled, this test will fail. On + the other hand, tracking.example.com will not be used in any of the same-origin + comparisons since we always look for the top window URI when there is one and + that's set to be www.itisatrap.org. +--> +<script id="badscript" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js"></script> + +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js new file mode 100644 index 0000000000..2720578eed --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js @@ -0,0 +1,9 @@ +window.addEventListener("message", function onMessage(evt) { + if (evt.data.type === "doXHR") { + var request = new XMLHttpRequest(); + request.open("GET", evt.data.url, true); + request.send(null); + } else if (evt.data.type === "doFetch") { + fetch(evt.data.url); + } +}); diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^ new file mode 100644 index 0000000000..3eced96143 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^ @@ -0,0 +1,2 @@ +Access-Control-Allow-Origin: * +Cache-Control: no-store diff --git a/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js new file mode 100644 index 0000000000..b4e8a47602 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js @@ -0,0 +1,5 @@ +/* eslint-env worker */ + +onmessage = function () { + postMessage("loaded bad file"); +}; diff --git a/toolkit/components/url-classifier/tests/mochitest/update.sjs b/toolkit/components/url-classifier/tests/mochitest/update.sjs new file mode 100644 index 0000000000..f3984b2a6f --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/update.sjs @@ -0,0 +1,71 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function handleRequest(request, response) { + var query = {}; + request.queryString.split("&").forEach(function (val) { + var idx = val.indexOf("="); + query[val.slice(0, idx)] = unescape(val.slice(idx + 1)); + }); + + // Store fullhash in the server side. + if ("list" in query && "fullhash" in query) { + // In the server side we will store: + // 1. All the full hashes for a given list + // 2. All the lists we have right now + // data is separate by '\n' + let list = query.list; + let hashes = getState(list); + + let hash = atob(query.fullhash); + hashes += hash + "\n"; + setState(list, hashes); + + let lists = getState("lists"); + if (!lists.includes(list)) { + lists += list + "\n"; + setState("lists", lists); + } + + return; + } + + var body = new BinaryInputStream(request.bodyInputStream); + var avail; + var bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + var responseBody = parseV2Request(bytes); + + response.setHeader("Content-Type", "text/plain", false); + response.write(responseBody); +} + +function parseV2Request(bytes) { + var table = String.fromCharCode.apply(this, bytes).slice(0, -2); + + var ret = ""; + getState("lists") + .split("\n") + .forEach(function (list) { + if (list == table) { + var completions = getState(list).split("\n"); + ret += "n:1000\n"; + ret += "i:" + list + "\n"; + ret += "a:1:32:" + 32 * (completions.length - 1) + "\n"; + + for (var completion of completions) { + ret += completion; + } + } + }); + + return ret; +} diff --git a/toolkit/components/url-classifier/tests/mochitest/vp9.webm b/toolkit/components/url-classifier/tests/mochitest/vp9.webm Binary files differnew file mode 100644 index 0000000000..221877e303 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/vp9.webm diff --git a/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html b/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html new file mode 100644 index 0000000000..620416fc74 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<html> +<head> +<title></title> +</head> +<body> + +<script id="badscript" data-touched="not sure" src="http://trackertest.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script> + +<script id="goodscript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script> + +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/workerFrame.html b/toolkit/components/url-classifier/tests/mochitest/workerFrame.html new file mode 100644 index 0000000000..69e8dd0074 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/workerFrame.html @@ -0,0 +1,65 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> + +function startCleanWorker() { + var worker = new Worker("cleanWorker.js"); + + worker.onmessage = function(event) { + if (event.data == "success") { + window.parent.postMessage("success:blocked importScripts('evilWorker.js')", "*"); + } else { + window.parent.postMessage("failure:failed to block importScripts('evilWorker.js')", "*"); + } + window.parent.postMessage("finish", "*"); + }; + + worker.onerror = function(event) { + window.parent.postmessage("failure:failed to load cleanWorker.js", "*"); + window.parent.postMessage("finish", "*"); + }; + + worker.postMessage(""); +} + +function startEvilWorker() { + var worker = new Worker("evilWorker.js"); + + worker.onmessage = function(event) { + window.parent.postMessage("failure:failed to block evilWorker.js", "*"); + startUnwantedWorker(); + }; + + worker.onerror = function(event) { + window.parent.postMessage("success:blocked evilWorker.js", "*"); + startUnwantedWorker(); + }; + + worker.postMessage(""); +} + +function startUnwantedWorker() { + var worker = new Worker("unwantedWorker.js"); + + worker.onmessage = function(event) { + window.parent.postMessage("failure:failed to block unwantedWorker.js", "*"); + startCleanWorker(); + }; + + worker.onerror = function(event) { + window.parent.postMessage("success:blocked unwantedWorker.js", "*"); + startCleanWorker(); + }; + + worker.postMessage(""); +} + +</script> + +</head> + +<body onload="startEvilWorker()"> +</body> +</html> |