Bug 1414804 - Fix test for expired certificate "helpful" messaging to users with broken system clocks. r=Gijs
authorJohann Hofmann <jhofmann@mozilla.com>
Wed, 09 Jan 2019 17:23:18 +0000
changeset 513102 a140784a7b05
parent 513101 5345e8472f29
child 513103 3ba4f46bbfdb
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1414804
milestone66.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 1414804 - Fix test for expired certificate "helpful" messaging to users with broken system clocks. r=Gijs We initially thought we would need a new test certificate to be able to test clock skew, but I found that we can reliably produce the clock skew page by making Firefox believe that the validity range of the cert used by expired.example.com is the correct date. How we do that is described in more detail in the test comments. This patch also updates the formatter for the new clock skew page to not show hours and minutes, as it did previously on the old clock skew warnings. Differential Revision: https://phabricator.services.mozilla.com/D13690
browser/actors/NetErrorChild.jsm
browser/base/content/test/about/browser.ini
browser/base/content/test/about/browser_aboutCertError.js
browser/base/content/test/about/browser_aboutCertError_clockSkew.js
--- a/browser/actors/NetErrorChild.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -470,26 +470,26 @@ class NetErrorChild extends ActorChild {
         learnMoreLink.href = baseURL + "time-errors";
         // We check against the remote-settings server time first if available, because that allows us
         // to give the user an approximation of what the correct time is.
         let difference = Services.prefs.getIntPref(PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, 0);
         let lastFetched = Services.prefs.getIntPref(PREF_SERVICES_SETTINGS_LAST_FETCHED, 0) * 1000;
 
         let now = Date.now();
         let certRange = this._getCertValidityRange(docShell);
+        let formatter = new Services.intl.DateTimeFormat(undefined, {
+          dateStyle: "short",
+        });
 
         let approximateDate = now - difference * 1000;
         // If the difference is more than a day, we last fetched the date in the last 5 days,
         // and adjusting the date per the interval would make the cert valid, warn the user:
         if (Math.abs(difference) > 60 * 60 * 24 && (now - lastFetched) <= 60 * 60 * 24 * 5 &&
             certRange.notBefore < approximateDate && certRange.notAfter > approximateDate) {
           clockSkew = true;
-          let formatter = new Services.intl.DateTimeFormat(undefined, {
-            dateStyle: "short",
-          });
           let systemDate = formatter.format(new Date());
           // negative difference means local time is behind server time
           approximateDate = formatter.format(new Date(approximateDate));
 
           doc.getElementById("wrongSystemTime_URL").textContent = doc.location.hostname;
           doc.getElementById("wrongSystemTime_systemDate").textContent = systemDate;
           doc.getElementById("wrongSystemTime_actualDate").textContent = approximateDate;
 
@@ -509,31 +509,27 @@ class NetErrorChild extends ActorChild {
           let systemDate = new Date();
 
           // We don't check the notBefore of the cert with the build date,
           // as it is of course almost certain that it is now later than the build date,
           // so we shouldn't exclude the possibility that the cert has become valid
           // since the build date.
           if (buildDate > systemDate && new Date(certRange.notAfter) > buildDate) {
             clockSkew = true;
-            let formatter = new Services.intl.DateTimeFormat(undefined, {
-              dateStyle: "short",
-            });
 
             doc.getElementById("wrongSystemTimeWithoutReference_URL")
               .textContent = doc.location.hostname;
             doc.getElementById("wrongSystemTimeWithoutReference_systemDate")
               .textContent = formatter.format(systemDate);
           }
         }
         if (!newErrorPagesEnabled) {
           break;
         }
-        let dateOptions = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" };
-        let systemDate = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
+        let systemDate = formatter.format(new Date());
         doc.getElementById("wrongSystemTime_systemDate1").textContent = systemDate;
         if (clockSkew) {
           doc.body.classList.add("illustrated", "clockSkewError");
           let clockErrTitle = doc.getElementById("et_clockSkewError");
           let clockErrDesc = doc.getElementById("ed_clockSkewError");
           // eslint-disable-next-line no-unsanitized/property
           doc.querySelector(".title-text").textContent = clockErrTitle.textContent;
           let desc = doc.getElementById("errorShortDescText");
--- a/browser/base/content/test/about/browser.ini
+++ b/browser/base/content/test/about/browser.ini
@@ -5,16 +5,17 @@ support-files =
   searchSuggestionEngine.sjs
   searchSuggestionEngine.xml
   POSTSearchEngine.xml
   dummy_page.html
 prefs =
   browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
 
 [browser_aboutCertError.js]
+[browser_aboutCertError_clockSkew.js]
 [browser_aboutCertError_telemetry.js]
 [browser_aboutHome_search_POST.js]
 [browser_aboutHome_search_composing.js]
 [browser_aboutHome_search_searchbar.js]
 [browser_aboutHome_search_suggestion.js]
 skip-if = os == "mac" || (os == "linux" && (!debug || bits == 64)) # Bug 1399648, bug 1402502
 [browser_aboutHome_search_telemetry.js]
 [browser_aboutNetError.js]
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -6,16 +6,17 @@
 // This is testing the aboutCertError page (Bug 1207107).
 
 const GOOD_PAGE = "https://example.com/";
 const GOOD_PAGE_2 = "https://example.org/";
 const BAD_CERT = "https://expired.example.com/";
 const UNKNOWN_ISSUER = "https://self-signed.example.com ";
 const BAD_STS_CERT = "https://badchain.include-subdomains.pinning.example.com:443";
 const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
+const PREF_NEW_CERT_ERRORS = "browser.security.newcerterrorpage.enabled";
 
 add_task(async function checkReturnToAboutHome() {
   info("Loading a bad cert page directly and making sure 'return to previous page' goes to about:home");
   for (let useFrame of [false, true]) {
     let tab = await openErrorPage(BAD_CERT, useFrame);
     let browser = tab.linkedBrowser;
 
     is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
@@ -47,17 +48,17 @@ add_task(async function checkReturnToAbo
     is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
     is(gBrowser.currentURI.spec, "about:home", "Went back");
 
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 add_task(async function checkExceptionDialogButton() {
-  Services.prefs.setBoolPref("browser.security.newcerterrorpage.enabled", true);
+  Services.prefs.setBoolPref(PREF_NEW_CERT_ERRORS, true);
   info("Loading a bad cert page and making sure the exceptionDialogButton directly adds an exception");
   let tab = await openErrorPage(BAD_CERT);
   let browser = tab.linkedBrowser;
   let loaded = BrowserTestUtils.browserLoaded(browser, false, BAD_CERT);
   info("Clicking the exceptionDialogButton in advanced panel");
   await ContentTask.spawn(browser, null, async function() {
     let doc = content.document;
     let exceptionButton = doc.getElementById("exceptionDialogButton");
@@ -71,17 +72,17 @@ add_task(async function checkExceptionDi
     let doc = content.document;
     ok(!doc.documentURI.startsWith("about:certerror"), "Exception has been added");
   });
 
   let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
                               .getService(Ci.nsICertOverrideService);
   certOverrideService.clearValidityOverride("expired.example.com", -1);
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  Services.prefs.clearUserPref("browser.security.newcerterrorpage.enabled");
+  Services.prefs.clearUserPref(PREF_NEW_CERT_ERRORS);
 });
 
 add_task(async function checkReturnToPreviousPage() {
   info("Loading a bad cert page and making sure 'return to previous page' goes back");
   for (let useFrame of [false, true]) {
     let tab;
     let browser;
     if (useFrame) {
@@ -141,17 +142,17 @@ add_task(async function checkBadStsCert(
     });
 
     let message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
       let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
       let advancedButton = doc.getElementById("advancedButton");
       advancedButton.click();
       return doc.getElementById("badCertTechnicalInfo").textContent;
     });
-    if (Services.prefs.getBoolPref("browser.security.newcerterrorpage.enabled", false)) {
+    if (Services.prefs.getBoolPref(PREF_NEW_CERT_ERRORS)) {
       ok(message.includes("SSL_ERROR_BAD_CERT_DOMAIN"), "Didn't find SSL_ERROR_BAD_CERT_DOMAIN.");
       ok(message.includes("The certificate is only valid for"), "Didn't find error message.");
       ok(message.includes("a certificate that is not valid for"), "Didn't find error message.");
       ok(message.includes("badchain.include-subdomains.pinning.example.com"), "Didn't find domain in error message.");
 
       BrowserTestUtils.removeTab(gBrowser.selectedTab);
       return;
     }
@@ -172,116 +173,16 @@ add_task(async function checkAppBuildIDI
   let month = parseInt(appBuildID.substr(4, 2), 10);
   let day = parseInt(appBuildID.substr(6, 2), 10);
 
   ok(year >= 2016 && year <= 2100, "appBuildID contains a valid year");
   ok(month >= 1 && month <= 12, "appBuildID contains a valid month");
   ok(day >= 1 && day <= 31, "appBuildID contains a valid day");
 });
 
-const PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS = "services.settings.clock_skew_seconds";
-
-add_task(async function checkWrongSystemTimeWarning() {
-  async function setUpPage() {
-    let browser;
-    let certErrorLoaded;
-    await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
-      gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, BAD_CERT);
-      browser = gBrowser.selectedBrowser;
-      certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
-    }, false);
-
-    info("Loading and waiting for the cert error");
-    await certErrorLoaded;
-
-    return ContentTask.spawn(browser, null, async function() {
-      let doc = content.document;
-      let div = doc.getElementById("wrongSystemTimePanel");
-      let systemDateDiv = doc.getElementById("wrongSystemTime_systemDate");
-      let actualDateDiv = doc.getElementById("wrongSystemTime_actualDate");
-      let learnMoreLink = doc.getElementById("learnMoreLink");
-
-      return {
-        divDisplay: content.getComputedStyle(div).display,
-        text: div.textContent,
-        systemDate: systemDateDiv.textContent,
-        actualDate: actualDateDiv.textContent,
-        learnMoreLink: learnMoreLink.href,
-      };
-    });
-  }
-
-  let formatter = new Services.intl.DateTimeFormat(undefined, {
-    dateStyle: "short",
-  });
-
-  // pretend we have a positively skewed (ahead) system time
-  let serverDate = new Date("2015/10/27");
-  let serverDateFmt = formatter.format(serverDate);
-  let localDateFmt = formatter.format(new Date());
-
-  let skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
-  await SpecialPowers.pushPrefEnv({set: [[PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, skew]]});
-
-  info("Loading a bad cert page with a skewed clock");
-  let message = await setUpPage();
-
-  isnot(message.divDisplay, "none", "Wrong time message information is visible");
-  ok(message.text.includes("clock appears to show the wrong time"),
-     "Correct error message found");
-  ok(message.text.includes("expired.example.com"), "URL found in error message");
-  ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
-  ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
-  ok(message.learnMoreLink.includes("time-errors"), "time-errors in the Learn More URL");
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
-  // pretend we have a negatively skewed (behind) system time
-  serverDate = new Date();
-  serverDate.setYear(serverDate.getFullYear() + 1);
-  serverDateFmt = formatter.format(serverDate);
-
-  skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
-  await SpecialPowers.pushPrefEnv({set: [[PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, skew]]});
-
-  info("Loading a bad cert page with a skewed clock");
-  message = await setUpPage();
-
-  isnot(message.divDisplay, "none", "Wrong time message information is visible");
-  ok(message.text.includes("clock appears to show the wrong time"),
-     "Correct error message found");
-  ok(message.text.includes("expired.example.com"), "URL found in error message");
-  ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
-  ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
-  // pretend we only have a slightly skewed system time, four hours
-  skew = 60 * 60 * 4;
-  await SpecialPowers.pushPrefEnv({set: [[PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, skew]]});
-
-  info("Loading a bad cert page with an only slightly skewed clock");
-  message = await setUpPage();
-
-  is(message.divDisplay, "none", "Wrong time message information is not visible");
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
-  // now pretend we have no skewed system time
-  skew = 0;
-  await SpecialPowers.pushPrefEnv({set: [[PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, skew]]});
-
-  info("Loading a bad cert page with no skewed clock");
-  message = await setUpPage();
-
-  is(message.divDisplay, "none", "Wrong time message information is not visible");
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-}).skip(); // Skipping because of bug 1414804.
-
 add_task(async function checkAdvancedDetails() {
   info("Loading a bad cert page and verifying the main error and advanced details section");
   for (let useFrame of [false, true]) {
     let tab = await openErrorPage(BAD_CERT, useFrame);
     let browser = tab.linkedBrowser;
 
     let message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
       let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
@@ -453,17 +354,17 @@ add_task(async function checkUnknownIssu
     });
     ok(href.endsWith("security-error"), "security-error in the Learn More URL");
 
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 add_task(async function checkCautionClass() {
-  Services.prefs.setBoolPref("browser.security.newcerterrorpage.enabled", true);
+  Services.prefs.setBoolPref(PREF_NEW_CERT_ERRORS, true);
   info("Checking that are potentially more dangerous get a 'caution' class");
   for (let useFrame of [false, true]) {
     let tab = await openErrorPage(UNKNOWN_ISSUER, useFrame);
     let browser = tab.linkedBrowser;
 
     await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
       let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
       is(doc.body.classList.contains("caution"), !frame, `Cert error body has ${frame ? "no" : ""} caution class`);
@@ -476,21 +377,21 @@ add_task(async function checkCautionClas
 
     await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
       let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
       ok(!doc.body.classList.contains("caution"), "Cert error body has no caution class");
     });
 
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
-  Services.prefs.clearUserPref("browser.security.newcerterrorpage.enabled");
+  Services.prefs.clearUserPref(PREF_NEW_CERT_ERRORS);
 });
 
 add_task(async function checkViewCertificate() {
-  Services.prefs.setBoolPref("browser.security.newcerterrorpage.enabled", true);
+  Services.prefs.setBoolPref(PREF_NEW_CERT_ERRORS, true);
   info("Loading a cert error and checking that the certificate can be shown.");
   for (let useFrame of [false, true]) {
     let tab = await openErrorPage(UNKNOWN_ISSUER, useFrame);
     let browser = tab.linkedBrowser;
 
     let dialogOpened = BrowserTestUtils.domWindowOpened();
 
     await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
@@ -504,17 +405,17 @@ add_task(async function checkViewCertifi
     is(win.document.documentURI, "chrome://pippki/content/certViewer.xul",
       "Opened the cert viewer dialog");
     is(win.document.getElementById("commonname").value, "self-signed.example.com",
       "Shows the correct certificate in the dialog");
     win.close();
 
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
-  Services.prefs.clearUserPref("browser.security.newcerterrorpage.enabled");
+  Services.prefs.clearUserPref(PREF_NEW_CERT_ERRORS);
 });
 
 function getCertChain(securityInfoAsString) {
   let certChain = "";
   const serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                        .getService(Ci.nsISerializationHelper);
   let securityInfo = serhelper.deserializeObject(securityInfoAsString);
   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/about/browser_aboutCertError_clockSkew.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS = "services.settings.clock_skew_seconds";
+const PREF_SERVICES_SETTINGS_LAST_FETCHED = "services.settings.last_update_seconds";
+const PREF_NEW_CERT_ERRORS = "browser.security.newcerterrorpage.enabled";
+
+add_task(async function checkWrongSystemTimeWarning() {
+  Services.prefs.setBoolPref(PREF_NEW_CERT_ERRORS, true);
+
+  async function setUpPage() {
+    let browser;
+    let certErrorLoaded;
+    await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+      gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "https://expired.example.com/");
+      browser = gBrowser.selectedBrowser;
+      certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
+    }, false);
+
+    info("Loading and waiting for the cert error");
+    await certErrorLoaded;
+
+    return ContentTask.spawn(browser, null, async function() {
+      let doc = content.document;
+      let div = doc.getElementById("errorShortDescText");
+      let systemDateDiv = doc.getElementById("wrongSystemTime_systemDate1");
+      let learnMoreLink = doc.getElementById("learnMoreLink");
+
+      await ContentTaskUtils.waitForCondition(() => div.textContent.includes("update your computer clock"),
+         "Correct error message found");
+
+      return {
+        divDisplay: content.getComputedStyle(div).display,
+        text: div.textContent,
+        systemDate: systemDateDiv.textContent,
+        learnMoreLink: learnMoreLink.href,
+      };
+    });
+  }
+
+  // Pretend that we recently updated our kinto clock skew pref
+  Services.prefs.setIntPref(PREF_SERVICES_SETTINGS_LAST_FETCHED, Math.floor(Date.now() / 1000));
+
+  let formatter = new Services.intl.DateTimeFormat(undefined, {
+    dateStyle: "short",
+  });
+
+  // For this test, we want to trick Firefox into believing that
+  // the local system time (as returned by Date.now()) is wrong.
+  // Because we don't want to actually change the local system time,
+  // we will do the following:
+
+  // Take the validity date of our test page (expired.example.com).
+  let expiredDate = new Date("2010/01/05 12:00");
+  let localDate = Date.now();
+
+  // Compute the difference between the server date and the correct
+  // local system date.
+  let skew = Math.floor((localDate - expiredDate) / 1000);
+
+  // Make it seem like our reference server agrees that the certificate
+  // date is correct by recording the difference as clock skew.
+  Services.prefs.setIntPref(PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, skew);
+
+  let localDateFmt = formatter.format(localDate);
+
+  info("Loading a bad cert page with a skewed clock");
+  let message = await setUpPage();
+
+  isnot(message.divDisplay, "none", "Wrong time message information is visible");
+  ok(message.text.includes("update your computer clock"),
+     "Correct error message found");
+  ok(message.text.includes("expired.example.com"), "URL found in error message");
+  ok(message.systemDate.includes(localDateFmt), "Correct local date displayed");
+  ok(message.learnMoreLink.includes("time-errors"), "time-errors in the Learn More URL");
+
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  Services.prefs.clearUserPref(PREF_SERVICES_SETTINGS_LAST_FETCHED);
+  Services.prefs.clearUserPref(PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS);
+  Services.prefs.clearUserPref(PREF_NEW_CERT_ERRORS);
+});