Bug 1372073 - Add tests for Media Capture and Streams fingerprinting resistance. r=arthuredelstein,jib
authorChung-Sheng Fu <cfu@mozilla.com>
Tue, 19 Sep 2017 15:12:52 +0800
changeset 391515 3f811aa66cdbc37b799c132c15d33562a97d8c03
parent 391514 e04a7ec9c152777566405216ecfa79840b317c56
child 391516 951e8ff8216963c5a1abf9b36d3d08682bb997f6
push id32892
push userncsoregi@mozilla.com
push dateMon, 13 Nov 2017 22:56:04 +0000
treeherdermozilla-central@3dddb19f2a07 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarthuredelstein, jib
bugs1372073
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1372073 - Add tests for Media Capture and Streams fingerprinting resistance. r=arthuredelstein,jib MozReview-Commit-ID: DY9JpwJevkS
dom/media/tests/mochitest/mochitest.ini
dom/media/tests/mochitest/test_fingerprinting_resistance.html
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -301,8 +301,9 @@ skip-if = (android_version == '18') # an
 # Bug 1227781: Crash with bogus TURN server.
 [test_peerConnection_bug1227781.html]
 [test_peerConnection_stats.html]
 skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator, Bug 1373858)
 [test_peerConnection_sender_and_receiver_stats.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_verifyDescriptions.html]
 skip-if = (android_version == '18')
+[test_fingerprinting_resistance.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_fingerprinting_resistance.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<script>
+/* global SimpleTest SpecialPowers */
+
+async function testEnumerateDevices(expectDevices) {
+  let devices = await navigator.mediaDevices.enumerateDevices();
+  if (!expectDevices) {
+    SimpleTest.is(devices.length, 0, "testEnumerateDevices: No devices");
+    return;
+  }
+  let cams = devices.filter((device) => device.kind == "videoinput");
+  let mics = devices.filter((device) => device.kind == "audioinput");
+  SimpleTest.ok((cams.length == 1) && (mics.length == 1),
+                "testEnumerateDevices: a microphone and a camera");
+}
+
+async function testGetUserMedia(expectDevices) {
+  const constraints = [
+    {audio: true}, {video: true}, {audio: true, video: true}
+  ];
+  for (let constraint of constraints) {
+    let message = "getUserMedia(" + JSON.stringify(constraint) + ")";
+    try {
+      let stream = await navigator.mediaDevices.getUserMedia(constraint);
+      SimpleTest.ok(expectDevices, message + " resolved");
+      if (!expectDevices) {
+        continue;
+      }
+
+      // We only do testGetUserMedia(true) when privacy.resistFingerprinting
+      // is true, test if MediaStreamTrack.label is spoofed.
+      for (let track of stream.getTracks()) {
+        switch (track.kind) {
+          case "audio":
+            SimpleTest.is(track.label, "Internal Microphone", "AudioStreamTrack.label");
+            break;
+          case "video":
+            SimpleTest.is(track.label, "Internal Camera", "VideoStreamTrack.label");
+            break;
+          default:
+            SimpleTest.ok(false, "Unknown kind: " + track.kind);
+            break;
+        }
+        track.stop();
+      }
+    } catch (e) {
+      if (!expectDevices) {
+        SimpleTest.is(e.name, "NotAllowedError", message + " throws NotAllowedError");
+      } else {
+        SimpleTest.ok(false, message + " failed: " + e);
+      }
+    }
+  }
+}
+
+async function testDevices() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["privacy.resistFingerprinting", true],
+      ["media.navigator.streams.fake", true]
+    ]
+  });
+  await testEnumerateDevices(true); // should list a microphone and a camera
+  await testGetUserMedia(true); // should get audio and video streams
+}
+
+async function testNoDevices() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["privacy.resistFingerprinting", false],
+      ["media.navigator.streams.fake", false],
+      ["media.audio_loopback_dev", "foo"],
+      ["media.video_loopback_dev", "bar"]
+    ]
+  });
+  await testEnumerateDevices(false); // should list nothing
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["privacy.resistFingerprinting", true]
+    ]
+  });
+  await testEnumerateDevices(true); // should list a microphone and a camera
+  await testGetUserMedia(false); // should reject with NotAllowedError
+}
+
+createHTML({
+  title: "Neutralize the threat of fingerprinting of media devices API when 'privacy.resistFingerprinting' is true",
+  bug: "1372073"
+});
+
+runTest(async () => {
+  // Make sure enumerateDevices and getUserMedia work when
+  // privacy.resistFingerprinting is true.
+  await testDevices();
+
+  // Test that absence of devices can't be detected.
+  await testNoDevices();
+});
+</script>
+</body>
+</html>