diff options
Diffstat (limited to 'toolkit/components/url-classifier/tests/mochitest')
50 files changed, 3181 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/tests/mochitest/.eslintrc.js b/toolkit/components/url-classifier/tests/mochitest/.eslintrc.js new file mode 100644 index 0000000000..58b3df4a74 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../../testing/mochitest/mochitest.eslintrc.js", + "../../../../../testing/mochitest/chrome.eslintrc.js" + ] +}; 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..9aae1b8415 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html @@ -0,0 +1,144 @@ +<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"; + +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.blockedTrackingNodeCount, 0, + "No elements should be blocked"); + + // End (parent) test. + 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..cd57701772 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html @@ -0,0 +1,35 @@ +<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"); + + // 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://bug1281083.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script> + +</head> + +<body onload="checkLoads()"> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/chrome.ini b/toolkit/components/url-classifier/tests/mochitest/chrome.ini new file mode 100644 index 0000000000..1652e7421b --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/chrome.ini @@ -0,0 +1,23 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + allowlistAnnotatedFrame.html + classifiedAnnotatedFrame.html + classifiedAnnotatedPBFrame.html + bug_1281083.html + +[test_lookup_system_principal.html] +[test_classified_annotations.html] +tags = trackingprotection +skip-if = os == 'linux' && asan # Bug 1202548 +[test_allowlisted_annotations.html] +tags = trackingprotection +[test_privatebrowsing_trackingprotection.html] +tags = trackingprotection +[test_trackingprotection_bug1157081.html] +tags = trackingprotection +[test_trackingprotection_whitelist.html] +tags = trackingprotection +[test_safebrowsing_bug1272239.html] +[test_donottrack.html] +[test_classifier_changetablepref.html] 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..8aab13dd3b --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html @@ -0,0 +1,213 @@ +<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" +]; + +function checkLoads() { + window.parent.is( + scriptItem1, "untouched", "Should not load tracking javascript"); + window.parent.is( + scriptItem2, "untouched", "Should not load tracking javascript (2)"); + + window.parent.is( + imageItem1, "untouched", "Should not load tracking images"); + window.parent.is( + imageItem2, "untouched", "Should not load tracking images (2)"); + + window.parent.is( + frameItem1, "untouched", "Should not load tracking iframes"); + window.parent.is( + frameItem2, "untouched", "Should not load tracking iframes (2)"); + window.parent.is( + mediaItem1, "error", "Should not load tracking videos"); + window.parent.is( + xhrItem, "failed", "Should not load tracking XHRs"); + window.parent.is( + fetchItem, "error", "Should not fetch from tracking URLs"); + + var elt = document.getElementById("styleCheck"); + var style = document.defaultView.getComputedStyle(elt, ""); + window.parent.isnot( + style.visibility, "hidden", "Should not load tracking css"); + + window.parent.is(window.document.blockedTrackingNodeCount, badids.length, + "Should identify all tracking elements"); + + var blockedTrackingNodes = window.document.blockedTrackingNodes; + + // Make sure that every node in blockedTrackingNodes 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 (var i = 0; i < blockedTrackingNodes.length; i++) { + var nodeMatch = false; + for (var j = 0; j < badids.length && !nodeMatch; j++) { + nodeMatch = nodeMatch || + (blockedTrackingNodes[i] == document.getElementById(badids[j])); + } + + allNodeMatch = allNodeMatch && nodeMatch; + } + window.parent.ok(allNodeMatch, + "All annotated nodes are expected in the tree"); + + // Make sure that every node with a badid (see badids) is found in the + // blockedTrackingNodes. This tells us if we are neglecting to annotate + // some nodes + allNodeMatch = true; + for (var j = 0; j < badids.length; j++) { + var nodeMatch = false; + for (var i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) { + nodeMatch = nodeMatch || + (blockedTrackingNodes[i] == document.getElementById(badids[j])); + } + + if (!nodeMatch) { + console.log(badids[j] + " was not found in blockedTrackingNodes"); + } + allNodeMatch = allNodeMatch && nodeMatch; + } + window.parent.ok(allNodeMatch, + "All tracking nodes are expected to be annotated as such"); + + // Unset prefs, etc. + window.parent.cleanup(); + // End (parent) test. + 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> + +<!-- 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..f11ec1de3e --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html @@ -0,0 +1,24 @@ +<!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="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link> + +</head> +<body> + +<script id="badscript" data-touched="not sure" src="http://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="http://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="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test" 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..49bda38db1 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js @@ -0,0 +1,112 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { classes: Cc, interfaces: Ci, results: Cr } = Components; + +var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"] + .getService(Ci.nsIUrlClassifierDBService); + +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: function(iid) + { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsIUrlClassifierUpdateObserver)) + return this; + + throw Cr.NS_ERROR_NO_INTERFACE; + }, + updateUrlRequested: function(url) { }, + streamFinished: function(status) { }, + updateError: function(errorCode) { + sendAsyncMessage("updateError", errorCode); + }, + updateSuccess: function(requestedTimeout) { + sendAsyncMessage("updateSuccess"); + } + }; + + let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"] + .getService(Ci.nsIUrlClassifierDBService); + + 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() { + dbService.reloadDatabase(); + + sendAsyncMessage("reloadSuccess"); +} + +// 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() { + let observerService = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + + observerService.addObserver(function() { + sendAsyncMessage("safeBrowsingInited"); + }, "mozentries-update-finished", false); + + // This url must sync with the table, url in SafeBrowsing.jsm addMozEntries + const table = "test-phish-simple"; + const url = "http://itisatrap.org/firefox/its-a-trap.html"; + + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let iosvc = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + + let principal = secMan.createCodebasePrincipal( + iosvc.newURI(url, null, null), {}); + + let listener = { + QueryInterface: function(iid) + { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsIUrlClassifierUpdateObserver)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + handleEvent: function(value) + { + if (value === table) { + sendAsyncMessage("safeBrowsingInited"); + } + }, + }; + dbService.lookup(principal, table, listener); +} + +addMessageListener("doUpdate", ({ testUpdate }) => { + doUpdate(testUpdate); +}); + +addMessageListener("doReload", () => { + doReload(); +}); + +addMessageListener("waitForInit", () => { + waitForInit(); +}); 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..c7923f4484 --- /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.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()"> +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..973f0c2c4d --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js @@ -0,0 +1,201 @@ +if (typeof(classifierHelper) == "undefined") { + var classifierHelper = {}; +} + +const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js"); +var gScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL); + +const ADD_CHUNKNUM = 524; +const SUB_CHUNKNUM = 523; +const HASHLEN = 32; + +const PREFS = { + PROVIDER_LISTS : "browser.safebrowsing.provider.mozilla.lists", + DISALLOW_COMPLETIONS : "urlclassifier.disallow_completions", + PROVIDER_GETHASHURL : "browser.safebrowsing.provider.mozilla.gethashURL" +}; + +// addUrlToDB & removeUrlFromDB are 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 = function(lists, url) { + for (var list of lists) { + // Add test db to provider + var pref = SpecialPowers.getCharPref(PREFS.PROVIDER_LISTS); + pref += "," + list; + SpecialPowers.setCharPref(PREFS.PROVIDER_LISTS, pref); + + // Rename test db so we will not disallow it from completions + pref = SpecialPowers.getCharPref(PREFS.DISALLOW_COMPLETIONS); + pref = pref.replace(list, list + "-backup"); + SpecialPowers.setCharPref(PREFS.DISALLOW_COMPLETIONS, pref); + } + + // Set get hash url + 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; + + classifierHelper._updatesToCleanup.push(update); + testUpdate += + "n:1000\n" + + "i:" + LISTNAME + "\n" + + "ad:1\n" + + "a:" + ADD_CHUNKNUM + ":" + HASHLEN + ":" + CHUNKLEN + "\n" + + CHUNKDATA; + } + + classifierHelper._update(testUpdate, resolve, reject); + }); +} + +// Pass { url: ..., db: ... } to remove url from database, +// onsuccess/onerror will be called when update complete. +classifierHelper.removeUrlFromDB = function(updateData) { + return new Promise(function(resolve, reject) { + var testUpdate = ""; + for (var update of updateData) { + var LISTNAME = update.db; + var CHUNKDATA = ADD_CHUNKNUM + ":" + update.url; + var CHUNKLEN = CHUNKDATA.length; + var HASHLEN = update.len ? update.len : 32; + + testUpdate += + "n:1000\n" + + "i:" + LISTNAME + "\n" + + "s:" + SUB_CHUNKNUM + ":" + HASHLEN + ":" + CHUNKLEN + "\n" + + CHUNKDATA; + } + + classifierHelper._updatesToCleanup = + classifierHelper._updatesToCleanup.filter((v) => { + return updateData.indexOf(v) == -1; + }); + + classifierHelper._update(testUpdate, resolve, reject); + }); +}; + +// This API is used to expire all add/sub chunks we have updated +// by using addUrlToDB and removeUrlFromDB. +classifierHelper.resetDB = function() { + return new Promise(function(resolve, reject) { + var testUpdate = ""; + for (var update of classifierHelper._updatesToCleanup) { + if (testUpdate.includes(update.db)) + continue; + + testUpdate += + "n:1000\n" + + "i:" + update.db + "\n" + + "ad:" + ADD_CHUNKNUM + "\n" + + "sd:" + SUB_CHUNKNUM + "\n" + } + + classifierHelper._update(testUpdate, resolve, reject); + }); +}; + +classifierHelper.reloadDatabase = function() { + return new Promise(function(resolve, reject) { + gScript.addMessageListener("reloadSuccess", function handler() { + gScript.removeMessageListener('reloadSuccess', handler); + resolve(); + }); + + gScript.sendAsyncMessage("doReload"); + }); +} + +classifierHelper._update = function(testUpdate, onsuccess, onerror) { + // Queue the task if there is still an on-going update + classifierHelper._updates.push({"data": testUpdate, + "onsuccess": onsuccess, + "onerror": 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 + for (var pref in PREFS) { + SpecialPowers.clearUserPref(pref); + } + + if (!classifierHelper._updatesToCleanup) { + return Promise.resolve(); + } + + return classifierHelper.resetDB(); +}; + +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..6856483730 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js @@ -0,0 +1,10 @@ +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..effc3a4f8c --- /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..f6f08d7c5c --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evil.css @@ -0,0 +1 @@ +#styleCheck { visibility: hidden; }
\ No newline at end of file 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..27f2e8c43d --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evil.js @@ -0,0 +1 @@ +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..ac34977d71 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js @@ -0,0 +1,3 @@ +onmessage = function() { + postMessage("loaded bad file"); +} 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..9dcc6e0d57 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs @@ -0,0 +1,130 @@ +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 = base64ToString(query["fullhash"]); + hashes += hash + "\n"; + setState(list, hashes); + + let lists = getState("lists"); + if (lists.indexOf(list) == -1) { + 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) { + var counter = getState("counter"); + responseBody = counter == "" ? "0" : counter; + } else { + var body = new BinaryInputStream(request.bodyInputStream); + var avail; + var bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + var 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; +} + +/* Convert Base64 data to a string */ +const toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; +const base64Pad = '='; + +function base64ToString(data) { + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} 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..560ddcde6e --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html @@ -0,0 +1,62 @@ +<html> +<head> +<title></title> + +<script type="text/javascript"> + +var scriptItem = "untouched"; + +function checkLoads() { + + var title = document.getElementById("title"); + title.innerHTML = 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..015b9fe520 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/good.js @@ -0,0 +1 @@ +scriptItem = "loaded whitelisted javascript!"; 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/mochitest.ini b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini new file mode 100644 index 0000000000..c5679e86be --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini @@ -0,0 +1,39 @@ +[DEFAULT] +support-files = + classifiedAnnotatedPBFrame.html + classifierCommon.js + classifierFrame.html + classifierHelper.js + cleanWorker.js + good.js + evil.css + evil.css^headers^ + evil.js + evil.js^headers^ + evilWorker.js + import.css + raptor.jpg + track.html + 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 + +[test_classifier.html] +skip-if = (os == 'linux' && debug) #Bug 1199778 +[test_classifier_worker.html] +[test_classify_ping.html] +[test_classify_track.html] +[test_gethash.html] +[test_bug1254766.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..37a78956e0 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/ping.sjs @@ -0,0 +1,16 @@ +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/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/test_allowlisted_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html new file mode 100644 index 0000000000..ba9c86f95e --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script type="text/javascript" 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; + +Components.utils.import("resource://testing-common/UrlClassifierTestUtils.jsm"); + +// Add https://allowlisted.example.com to the permissions manager +SpecialPowers.addPermission("trackingprotection", + Ci.nsIPermissionManager.ALLOW_ACTION, + { url: "https://allowlisted.example.com" }); + +function clearPermissions() { + SpecialPowers.removePermission("trackingprotection", + { url: "https://allowlisted.example.com" }); + ok(!SpecialPowers.testPermission("trackingprotection", + Ci.nsIPermissionManager.ALLOW_ACTION, + { url: "https://allowlisted.example.com" })); +} + +SpecialPowers.pushPrefEnv( + {"set" : [["urlclassifier.trackingTable", "test-track-simple"], + ["privacy.trackingprotection.enabled", true], + ["channelclassifier.allowlist_example", 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_bug1254766.html b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html new file mode 100644 index 0000000000..1c149406ac --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html @@ -0,0 +1,305 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Test gethash.</title> + <script type="text/javascript" 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 MALWARE_LIST = "test-malware-simple"; +const MALWARE_HOST1 = "malware.example.com/"; +const MALWARE_HOST2 = "test1.example.com/"; + +const UNWANTED_LIST = "test-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(); + }); +} + +// calculate the fullhash and send it to gethash server +function addCompletionToServer(list, url) { + return new Promise(function(resolve, reject) { + var listParam = "list=" + list; + var fullhashParam = "fullhash=" + hash(url); + + var xhr = new XMLHttpRequest; + xhr.open("PUT", GETHASH_URL + "?" + listParam + "&" + fullhashParam, true); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + resolve(); + } + }; + xhr.send(); + }); +} + +function hash(str) { + function bytesFromString(str) { + var converter = + SpecialPowers.Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(SpecialPowers.Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + return converter.convertToByteArray(str); + } + + var hasher = SpecialPowers.Cc["@mozilla.org/security/hash;1"] + .createInstance(SpecialPowers.Ci.nsICryptoHash); + + var data = bytesFromString(str); + hasher.init(hasher.SHA256); + hasher.update(data, data.length); + + return hasher.finish(true); +} + +// setup function allows classifier send gethash request for test database +// also it calculate to fullhash for url and store those hashes in gethash sjs. +function setup() { + classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL); + + return Promise.all([ + addCompletionToServer(MALWARE_LIST, MALWARE_HOST1), + addCompletionToServer(MALWARE_LIST, MALWARE_HOST2), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST1), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST2), + ]); +} + +// Reset function in helper try to simulate the behavior we restart firefox +function reset() { + return classifierHelper.resetDB() + .catch(err => { + ok(false, "Couldn't update classifier. Error code: " + errorCode); + // 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: " + errorCode); + // 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 testcase is to make sure an update request will clear completion cache: +// 1. Add prefixes to DB. +// 2. Load test frame, this should trigger a gethash request +// 3. Trigger an update, completion cache should be cleared now. +// 4. Load test frame again, since cache is cleared now, gethash request should be triggered. +function testUpdateClearCache() { + return Promise.resolve() + .then(addPrefixToDB) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); }) + .then(updateUnusedUrl) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is 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) + .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) + .then(loadTestFrame) + .then(() => { + ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered."); }) + .then(reset); +} + +function runTest() { + Promise.resolve() + .then(classifierHelper.waitForInit) + .then(setup) + .then(testGethash) + .then(testUpdateClearCache) + .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], + ["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..5814fff000 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script type="text/javascript" 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; + +Components.utils.import("resource://testing-common/UrlClassifierTestUtils.jsm"); + +function cleanup() { + SpecialPowers.clearUserPref("privacy.trackingprotection.enabled"); + SpecialPowers.clearUserPref("channelclassifier.allowlist_example"); +} + +SpecialPowers.pushPrefEnv( + {"set" : [["urlclassifier.trackingTable", "test-track-simple"]]}, + test); + +function test() { + UrlClassifierTestUtils.addTestTrackers().then(() => { + 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) + SpecialPowers.setBoolPref("channelclassifier.allowlist_example", true); + document.getElementById("testFrame").src = "classifiedAnnotatedFrame.html"; + }); +} + +// Expected finish() call is in "classifiedAnnotatedFrame.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.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html new file mode 100644 index 0000000000..9533db426e --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html @@ -0,0 +1,65 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script type="text/javascript" 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.example.com/", + db: "test-malware-simple" + }, + { url: "unwanted.example.com/", + db: "test-unwanted-simple" + } +]; + +function loadTestFrame() { + document.getElementById("testFrame").src = "classifierFrame.html"; +} + +// Expected finish() call is in "classifierFrame.html". +SimpleTest.waitForExplicitFinish(); + +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"], + ["urlclassifier.phishTable", "test-phish-simple"]]}, + function() { + classifierHelper.waitForInit() + .then(() => classifierHelper.addUrlToDB(testData)) + .then(updateSuccess) + .catch(err => { + updateError(err); + }); + }); + +</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..7423d3e8ef --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html @@ -0,0 +1,149 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1281083 - Changing the urlclassifier.*Table prefs doesn't take effect before the next browser restart.</title> + <script type="text/javascript" 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 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; + +var prefService = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService); + +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 loadTestFrame() { + // 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() { + document.getElementById("testFrame").src = "bug_1281083.html"; + }); +} + +function waitForUpdateSuccess(callback) { + let nextupdatetime = + SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.nextupdatetime"); + + if (nextupdatetime !== "1") { + callback(); + return; + } + + timer.initWithCallback(function() { + waitForUpdateSuccess(callback); + }, 10, Components.interfaces.nsITimer.TYPE_ONE_SHOT); +} + +function addCompletionToServer(list, url) { + return new Promise(function(resolve, reject) { + var listParam = "list=" + list; + var fullhashParam = "fullhash=" + hash(url); + + var xhr = new XMLHttpRequest; + xhr.open("PUT", UPDATE_URL + "?" + + listParam + "&" + + fullhashParam, true); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + resolve(); + } + }; + xhr.send(); + }); +} + +function hash(str) { + function bytesFromString(str) { + var converter = + SpecialPowers.Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(SpecialPowers.Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + return converter.convertToByteArray(str); + } + + var hasher = SpecialPowers.Cc["@mozilla.org/security/hash;1"] + .createInstance(SpecialPowers.Ci.nsICryptoHash); + + var data = bytesFromString(str); + hasher.init(hasher.SHA256); + hasher.update(data, data.length); + + return hasher.finish(true); +} + +function runTest() { + /** + * In this test we try to modify only urlclassifier.*Table preference to see if + * url specified in the table will be blocked after update. + */ + var pushPrefPromise = SpecialPowers.pushPrefEnv( + {"set" : [["urlclassifier.trackingTable", testTable]]}); + + // 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. + var addCompletePromise = + addCompletionToServer(testTable, "bug1281083.example.com/"); + + Promise.all([pushPrefPromise, addCompletePromise]) + .then(() => { + loadTestFrame(); + }); +} + +// 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_worker.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html new file mode 100644 index 0000000000..1f54d45b05 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test the URI Classifier</title> + <script type="text/javascript" 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"], + ["urlclassifier.phishTable", "test-phish-simple"]]}, + function() { + classifierHelper.waitForInit() + .then(() => classifierHelper.addUrlToDB(testData)) + .then(updateSuccess) + .catch(err => { + updateError(err); + }); + }); + +window.addEventListener("message", onmessage, false); + +SimpleTest.waitForExplicitFinish(); + +</script> + +</pre> +<iframe id="testFrame" onload=""></iframe> +</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..96fa2891a7 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html @@ -0,0 +1,121 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1233914 - ping doesn't honor the TP list here.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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"; + + function testPingNonBlacklist() { + 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) { + setTimeout(function() { + isPinged(id, expectPing, msg, resolve); + }, timeout); + }); + } + + function testPingBlacklistSafebrowsingOff() { + 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) { + setTimeout(function() { + isPinged(id, expectPing, msg, resolve); + }, timeout); + }); + } + + function testPingBlacklistSafebrowsingOn() { + 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 isPinged(id, expected, msg, callback) { + var url = "http://mochi.test:8888/" + path_ping; + var xhr = new XMLHttpRequest(); + xhr.open('GET', url + "?id=" + id); + xhr.onload = function() { + var isPinged = xhr.response === "ping"; + is(expected, isPinged, msg); + + callback(); + }; + xhr.send(); + } + + function cleanup() { + SpecialPowers.clearUserPref(TP_ENABLE_PREF); + } + + function runTest() { + Promise.resolve() + .then(testPingNonBlacklist) + .then(testPingBlacklistSafebrowsingOff) + .then(testPingBlacklistSafebrowsingOn) + .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], + ["urlclassifier.trackingTable", "test-track-simple"], + ]}, runTest); + +</script> +</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..a868d79602 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html @@ -0,0 +1,162 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1262406 - Track element doesn't use the URL classifier.</title> + <script type="text/javascript" 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.src = validtrack_url; + video.appendChild(track); + + function onload() { + ok(true, "Track should be loaded when url is not in blacklist"); + 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 testBlacklistTrackSafebrowsingOff() { + SpecialPowers.setBoolPref(PREF, false); + + return new Promise(function(resolve, reject) { + var track = document.createElement("track"); + track.src = malware_url; + video.appendChild(track); + + function onload() { + ok(true, "Track should be loaded when url is in blacklist 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 testBlacklistTrackSafebrowsingOn() { + SpecialPowers.setBoolPref(PREF, true); + + return new Promise(function(resolve, reject) { + var track = document.createElement("track"); + + // 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 blacklist"); + finish(); + } + + function onerror() { + ok(true, "Track should not be loaded when url is in blacklist 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(testBlacklistTrackSafebrowsingOff) + .then(testBlacklistTrackSafebrowsingOn) + .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": [ + ["media.webvtt.regions.enabled", true], + ["urlclassifier.malwareTable", "test-malware-simple"], + ]}, runTest); + +</script> +</pre> +</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..56003e7eb6 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html @@ -0,0 +1,150 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1258033 - Fix the DNT loophole for tracking protection</title> + <script type="text/javascript" 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; + +var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + +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"; + +Components.utils.import("resource://gre/modules/Services.jsm"); + +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", false); +} + +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 onLoad() { + win.removeEventListener("load", onLoad, false); + whenDelayedStartupFinished(win, function() { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(contentPage); + 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(contentPage); }); + }); + }, 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_gethash.html b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html new file mode 100644 index 0000000000..af995e2a54 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html @@ -0,0 +1,157 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Test gethash.</title> + <script type="text/javascript" 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 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: url, len: 4 }]; + + return classifierHelper.addUrlToDB(testData) + .catch(function(err) { + ok(false, "Couldn't update classifier. Error code: " + err); + // Abort test. + SimpleTest.finish(); + }); +} + +// calculate the fullhash and send it to gethash server +function addCompletionToServer(list, url) { + return new Promise(function(resolve, reject) { + var listParam = "list=" + list; + var fullhashParam = "fullhash=" + hash(url); + + var xhr = new XMLHttpRequest; + xhr.open("PUT", GETHASH_URL + "?" + + listParam + "&" + + fullhashParam, true); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + resolve(); + } + }; + xhr.send(); + }); +} + +function hash(str) { + function bytesFromString(str) { + var converter = + SpecialPowers.Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(SpecialPowers.Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + return converter.convertToByteArray(str); + } + + var hasher = SpecialPowers.Cc["@mozilla.org/security/hash;1"] + .createInstance(SpecialPowers.Ci.nsICryptoHash); + + var data = bytesFromString(str); + hasher.init(hasher.SHA256); + hasher.update(data, data.length); + + return hasher.finish(true); +} + +function setup404() { + shouldLoad = true; + + classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], NOTEXIST_URL); + + return Promise.all([ + addPrefixToDB(MALWARE_LIST, MALWARE_HOST), + addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST) + ]); +} + +function setup() { + 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), + addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST), + ]); +} + +// manually reset DB to make sure next test won't be affected by cache. +function reset() { + return classifierHelper.resetDB; +} + +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], + ["network.predictor.enabled", false], + ["urlclassifier.gethash.timeout_ms", 30000], +]}, runTest); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/url-classifier/tests/mochitest/test_lookup_system_principal.html b/toolkit/components/url-classifier/tests/mochitest/test_lookup_system_principal.html new file mode 100644 index 0000000000..fa61e6a00b --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_lookup_system_principal.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test that lookup() on a system principal doesn't crash</title> + <script type="text/javascript" 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> +<div id="content" style="display: none"> +</div> +<pre id="test"> + +<script type="text/javascript"> + +var Cc = Components.classes; +var Ci = Components.interfaces; + +var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"] + .getService(Ci.nsIUrlClassifierDBService); + +dbService.lookup(document.nodePrincipal, "", function(arg) {}); + +ok(true, "lookup() didn't crash"); + +</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..02ef57b467 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html @@ -0,0 +1,154 @@ +<!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 type="text/javascript" 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; + +var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); +var contentPage = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html"; + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://testing-common/UrlClassifierTestUtils.jsm"); + +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", false); +} + +function testOnWindow(aPrivate, aCallback) { + var win = mainWindow.OpenBrowserWindow({private: aPrivate}); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + whenDelayedStartupFinished(win, function() { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(contentPage); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener('load', function innerLoad2() { + win.content.removeEventListener('load', innerLoad2, false); + SimpleTest.executeSoon(function() { aCallback(win); }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); }); + }); + }, 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 whitelisted tracking javascript"); + + 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.blockedTrackingNodeCount, aBlocked ? badids.length : 0, "Should identify all tracking elements"); + + var blockedTrackingNodes = win.document.blockedTrackingNodes; + + // Make sure that every node in blockedTrackingNodes 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 (var i = 0; i < blockedTrackingNodes.length; i++) { + var nodeMatch = false; + for (var j = 0; j < badids.length && !nodeMatch; j++) { + nodeMatch = nodeMatch || + (blockedTrackingNodes[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 + // blockedTrackingNodes. This tells us if we are neglecting to annotate + // some nodes + allNodeMatch = true; + for (var j = 0; j < badids.length; j++) { + var nodeMatch = false; + for (var i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) { + nodeMatch = nodeMatch || + (blockedTrackingNodes[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" : [["urlclassifier.trackingTable", "test-track-simple"], + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.pbmode.enabled", true], + ["channelclassifier.allowlist_example", true]]}, + test); + +function test() { + SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers); + UrlClassifierTestUtils.addTestTrackers().then(() => { + // Normal mode, with the pref (trackers should be loaded) + testOnWindow(false, function(aWindow) { + checkLoads(aWindow, false); + aWindow.close(); + + // Private Browsing, with the pref (trackers should be blocked) + testOnWindow(true, function(aWindow) { + checkLoads(aWindow, true); + aWindow.close(); + + // Private Browsing, without the pref (trackers should be loaded) + SpecialPowers.setBoolPref("privacy.trackingprotection.pbmode.enabled", false); + testOnWindow(true, 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_safebrowsing_bug1272239.html b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html new file mode 100644 index 0000000000..8066c2a372 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html @@ -0,0 +1,87 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1272239 - Only tables with provider could register gethash url in listmanager.</title> + <script type="text/javascript" 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" +]; + +var prefService = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService); + +// Get providers +var providers = {}; + +var branch = prefService.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 (var provider in providers) { + var pref = "browser.safebrowsing.provider." + provider + ".lists"; + var list = SpecialPowers.getCharPref(pref).split(","); + + listsToProvider = listsToProvider.concat(list.map( () => { return provider; })); + listsWithProvider = listsWithProvider.concat(list); +} + +// Get all the lists +var lists = []; +for (var pref of prefs) { + lists = lists.concat(SpecialPowers.getCharPref(pref).split(",")); +} + +var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]. + getService(Ci.nsIUrlListManager); + +for (var 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, test-malware-simple, it should not + // have a gethash url. + var url = listmanager.getGethashUrl(list); + var index = listsWithProvider.indexOf(list); + if (index >= 0) { + var provider = listsToProvider[index]; + var pref = "browser.safebrowsing.provider." + provider + ".gethashURL"; + 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_trackingprotection_bug1157081.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html new file mode 100644 index 0000000000..7611dd245b --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html @@ -0,0 +1,107 @@ +<!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 type="text/javascript" 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; + +var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); +var contentPage = "chrome://mochitests/content/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html" + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://testing-common/UrlClassifierTestUtils.jsm"); + +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", false); +} + +function testOnWindow(aCallback) { + var win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + whenDelayedStartupFinished(win, function() { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(contentPage); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener('load', function innerLoad2() { + win.content.removeEventListener('load', innerLoad2, false); + SimpleTest.executeSoon(function() { aCallback(win); }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); }); + }); + }, 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", "test-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(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..29de0dfed8 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.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 type="text/javascript" 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; + +var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); +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"; + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://testing-common/UrlClassifierTestUtils.jsm"); + +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", false); +} + +function testOnWindow(contentPage, aCallback) { + var win = mainWindow.OpenBrowserWindow(); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + whenDelayedStartupFinished(win, function() { + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(contentPage); + return; + } + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + + win.content.addEventListener('load', function innerLoad2() { + win.content.removeEventListener('load', innerLoad2, false); + SimpleTest.executeSoon(function() { aCallback(win); }); + }, false, true); + }, true); + SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); }); + }); + }, true); +} + +var alwaysbadids = [ + "badscript", +]; + +function checkLoads(aWindow, aWhitelisted) { + var win = aWindow.content; + 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.blockedTrackingNodeCount, badids.length, "Should identify all tracking elements"); + + var blockedTrackingNodes = win.document.blockedTrackingNodes; + + // Make sure that every node in blockedTrackingNodes 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 (var i = 0; i < blockedTrackingNodes.length; i++) { + var nodeMatch = false; + for (var j = 0; j < badids.length && !nodeMatch; j++) { + nodeMatch = nodeMatch || + (blockedTrackingNodes[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 + // blockedTrackingNodes. This tells us if we are neglecting to annotate + // some nodes + allNodeMatch = true; + for (var j = 0; j < badids.length; j++) { + var nodeMatch = false; + for (var i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) { + nodeMatch = nodeMatch || + (blockedTrackingNodes[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], + ["channelclassifier.allowlist_example", true]]}, + test); + +function test() { + SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers); + UrlClassifierTestUtils.addTestTrackers().then(() => { + // Load the test from a URL on the whitelist + testOnWindow(contentPage1, function(aWindow) { + checkLoads(aWindow, true); + aWindow.close(); + + // Load the test from a URL that's NOT on the whitelist + testOnWindow(contentPage2, function(aWindow) { + checkLoads(aWindow, false); + aWindow.close(); + + // Load the test from a URL on the whitelist but without the whitelist + SpecialPowers.pushPrefEnv({"set" : [["urlclassifier.trackingWhitelistTable", ""]]}, + function() { + testOnWindow(contentPage1, 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/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/unwantedWorker.js b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js new file mode 100644 index 0000000000..ac34977d71 --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js @@ -0,0 +1,3 @@ +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..53efaafdfc --- /dev/null +++ b/toolkit/components/url-classifier/tests/mochitest/update.sjs @@ -0,0 +1,114 @@ +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 = base64ToString(query["fullhash"]); + hashes += hash + "\n"; + setState(list, hashes); + + let lists = getState("lists"); + if (lists.indexOf(list) == -1) { + 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; +} + +/* Convert Base64 data to a string */ +const toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; +const base64Pad = '='; + +function base64ToString(data) { + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} 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> |