forked from mirrors/gecko-dev
		
	This library is now completely unused in m-c. Differential Revision: https://phabricator.services.mozilla.com/D185699
		
			
				
	
	
		
			425 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
const { SiteDataManager } = ChromeUtils.importESModule(
 | 
						|
  "resource:///modules/SiteDataManager.sys.mjs"
 | 
						|
);
 | 
						|
const { DownloadUtils } = ChromeUtils.importESModule(
 | 
						|
  "resource://gre/modules/DownloadUtils.sys.mjs"
 | 
						|
);
 | 
						|
 | 
						|
/* import-globals-from pageInfo.js */
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(this, {
 | 
						|
  LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
var security = {
 | 
						|
  async init(uri, windowInfo) {
 | 
						|
    this.uri = uri;
 | 
						|
    this.windowInfo = windowInfo;
 | 
						|
    this.securityInfo = await this._getSecurityInfo();
 | 
						|
  },
 | 
						|
 | 
						|
  viewCert() {
 | 
						|
    let certChain = this.securityInfo.certChain;
 | 
						|
    let certs = certChain.map(elem =>
 | 
						|
      encodeURIComponent(elem.getBase64DERString())
 | 
						|
    );
 | 
						|
    let certsStringURL = certs.map(elem => `cert=${elem}`);
 | 
						|
    certsStringURL = certsStringURL.join("&");
 | 
						|
    let url = `about:certificate?${certsStringURL}`;
 | 
						|
    let win = BrowserWindowTracker.getTopWindow();
 | 
						|
    win.switchToTabHavingURI(url, true, {});
 | 
						|
  },
 | 
						|
 | 
						|
  async _getSecurityInfo() {
 | 
						|
    // We don't have separate info for a frame, return null until further notice
 | 
						|
    // (see bug 138479)
 | 
						|
    if (!this.windowInfo.isTopWindow) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    var ui = security._getSecurityUI();
 | 
						|
    if (!ui) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    var isBroken = ui.state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
 | 
						|
    var isMixed =
 | 
						|
      ui.state &
 | 
						|
      (Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT |
 | 
						|
        Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
 | 
						|
    var isEV = ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
 | 
						|
 | 
						|
    let retval = {
 | 
						|
      cAName: "",
 | 
						|
      encryptionAlgorithm: "",
 | 
						|
      encryptionStrength: 0,
 | 
						|
      version: "",
 | 
						|
      isBroken,
 | 
						|
      isMixed,
 | 
						|
      isEV,
 | 
						|
      cert: null,
 | 
						|
      certificateTransparency: null,
 | 
						|
    };
 | 
						|
 | 
						|
    // Only show certificate info for secure contexts. This prevents us from
 | 
						|
    // showing certificate data for http origins when using a proxy.
 | 
						|
    // https://searchfox.org/mozilla-central/rev/9c72508fcf2bba709a5b5b9eae9da35e0c707baa/security/manager/ssl/nsSecureBrowserUI.cpp#62-64
 | 
						|
    if (!ui.isSecureContext) {
 | 
						|
      return retval;
 | 
						|
    }
 | 
						|
 | 
						|
    let secInfo = ui.secInfo;
 | 
						|
    if (!secInfo) {
 | 
						|
      return retval;
 | 
						|
    }
 | 
						|
 | 
						|
    let cert = secInfo.serverCert;
 | 
						|
    let issuerName = null;
 | 
						|
    if (cert) {
 | 
						|
      issuerName = cert.issuerOrganization || cert.issuerName;
 | 
						|
    }
 | 
						|
 | 
						|
    let certChainArray = [];
 | 
						|
    if (secInfo.succeededCertChain.length) {
 | 
						|
      certChainArray = secInfo.succeededCertChain;
 | 
						|
    } else {
 | 
						|
      certChainArray = secInfo.failedCertChain;
 | 
						|
    }
 | 
						|
 | 
						|
    retval = {
 | 
						|
      cAName: issuerName,
 | 
						|
      encryptionAlgorithm: undefined,
 | 
						|
      encryptionStrength: undefined,
 | 
						|
      version: undefined,
 | 
						|
      isBroken,
 | 
						|
      isMixed,
 | 
						|
      isEV,
 | 
						|
      cert,
 | 
						|
      certChain: certChainArray,
 | 
						|
      certificateTransparency: undefined,
 | 
						|
    };
 | 
						|
 | 
						|
    var version;
 | 
						|
    try {
 | 
						|
      retval.encryptionAlgorithm = secInfo.cipherName;
 | 
						|
      retval.encryptionStrength = secInfo.secretKeyLength;
 | 
						|
      version = secInfo.protocolVersion;
 | 
						|
    } catch (e) {}
 | 
						|
 | 
						|
    switch (version) {
 | 
						|
      case Ci.nsITransportSecurityInfo.SSL_VERSION_3:
 | 
						|
        retval.version = "SSL 3";
 | 
						|
        break;
 | 
						|
      case Ci.nsITransportSecurityInfo.TLS_VERSION_1:
 | 
						|
        retval.version = "TLS 1.0";
 | 
						|
        break;
 | 
						|
      case Ci.nsITransportSecurityInfo.TLS_VERSION_1_1:
 | 
						|
        retval.version = "TLS 1.1";
 | 
						|
        break;
 | 
						|
      case Ci.nsITransportSecurityInfo.TLS_VERSION_1_2:
 | 
						|
        retval.version = "TLS 1.2";
 | 
						|
        break;
 | 
						|
      case Ci.nsITransportSecurityInfo.TLS_VERSION_1_3:
 | 
						|
        retval.version = "TLS 1.3";
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    // Select the status text to display for Certificate Transparency.
 | 
						|
    // Since we do not yet enforce the CT Policy on secure connections,
 | 
						|
    // we must not complain on policy discompliance (it might be viewed
 | 
						|
    // as a security issue by the user).
 | 
						|
    switch (secInfo.certificateTransparencyStatus) {
 | 
						|
      case Ci.nsITransportSecurityInfo.CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE:
 | 
						|
      case Ci.nsITransportSecurityInfo
 | 
						|
        .CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS:
 | 
						|
      case Ci.nsITransportSecurityInfo
 | 
						|
        .CERTIFICATE_TRANSPARENCY_POLICY_NOT_DIVERSE_SCTS:
 | 
						|
        retval.certificateTransparency = null;
 | 
						|
        break;
 | 
						|
      case Ci.nsITransportSecurityInfo
 | 
						|
        .CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT:
 | 
						|
        retval.certificateTransparency = "Compliant";
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    return retval;
 | 
						|
  },
 | 
						|
 | 
						|
  // Find the secureBrowserUI object (if present)
 | 
						|
  _getSecurityUI() {
 | 
						|
    if (window.opener.gBrowser) {
 | 
						|
      return window.opener.gBrowser.securityUI;
 | 
						|
    }
 | 
						|
    return null;
 | 
						|
  },
 | 
						|
 | 
						|
  async _updateSiteDataInfo() {
 | 
						|
    // Save site data info for deleting.
 | 
						|
    this.siteData = await SiteDataManager.getSite(this.uri.host);
 | 
						|
 | 
						|
    let clearSiteDataButton = document.getElementById(
 | 
						|
      "security-clear-sitedata"
 | 
						|
    );
 | 
						|
    let siteDataLabel = document.getElementById(
 | 
						|
      "security-privacy-sitedata-value"
 | 
						|
    );
 | 
						|
 | 
						|
    if (!this.siteData) {
 | 
						|
      document.l10n.setAttributes(siteDataLabel, "security-site-data-no");
 | 
						|
      clearSiteDataButton.setAttribute("disabled", "true");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let { usage } = this.siteData;
 | 
						|
    if (usage > 0) {
 | 
						|
      let size = DownloadUtils.convertByteUnits(usage);
 | 
						|
      if (this.siteData.cookies.length) {
 | 
						|
        document.l10n.setAttributes(
 | 
						|
          siteDataLabel,
 | 
						|
          "security-site-data-cookies",
 | 
						|
          { value: size[0], unit: size[1] }
 | 
						|
        );
 | 
						|
      } else {
 | 
						|
        document.l10n.setAttributes(siteDataLabel, "security-site-data-only", {
 | 
						|
          value: size[0],
 | 
						|
          unit: size[1],
 | 
						|
        });
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      // We're storing cookies, else getSite would have returned null.
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        siteDataLabel,
 | 
						|
        "security-site-data-cookies-only"
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    clearSiteDataButton.removeAttribute("disabled");
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Clear Site Data and Cookies
 | 
						|
   */
 | 
						|
  clearSiteData() {
 | 
						|
    if (this.siteData) {
 | 
						|
      let { baseDomain } = this.siteData;
 | 
						|
      if (SiteDataManager.promptSiteDataRemoval(window, [baseDomain])) {
 | 
						|
        SiteDataManager.remove(baseDomain).then(() =>
 | 
						|
          this._updateSiteDataInfo()
 | 
						|
        );
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Open the login manager window
 | 
						|
   */
 | 
						|
  viewPasswords() {
 | 
						|
    LoginHelper.openPasswordManager(window, {
 | 
						|
      filterString: this.windowInfo.hostName,
 | 
						|
      entryPoint: "pageinfo",
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
async function securityOnLoad(uri, windowInfo) {
 | 
						|
  await security.init(uri, windowInfo);
 | 
						|
 | 
						|
  let info = security.securityInfo;
 | 
						|
  if (
 | 
						|
    !info ||
 | 
						|
    (uri.scheme === "about" && !uri.spec.startsWith("about:certerror"))
 | 
						|
  ) {
 | 
						|
    document.getElementById("securityTab").hidden = true;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  document.getElementById("securityTab").hidden = false;
 | 
						|
 | 
						|
  /* Set Identity section text */
 | 
						|
  setText("security-identity-domain-value", windowInfo.hostName);
 | 
						|
 | 
						|
  var validity;
 | 
						|
  if (info.cert && !info.isBroken) {
 | 
						|
    validity = info.cert.validity.notAfterLocalDay;
 | 
						|
 | 
						|
    // Try to pull out meaningful values.  Technically these fields are optional
 | 
						|
    // so we'll employ fallbacks where appropriate.  The EV spec states that Org
 | 
						|
    // fields must be specified for subject and issuer so that case is simpler.
 | 
						|
    if (info.isEV) {
 | 
						|
      setText("security-identity-owner-value", info.cert.organization);
 | 
						|
      setText("security-identity-verifier-value", info.cAName);
 | 
						|
    } else {
 | 
						|
      // Technically, a non-EV cert might specify an owner in the O field or not,
 | 
						|
      // depending on the CA's issuing policies.  However we don't have any programmatic
 | 
						|
      // way to tell those apart, and no policy way to establish which organization
 | 
						|
      // vetting standards are good enough (that's what EV is for) so we default to
 | 
						|
      // treating these certs as domain-validated only.
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        document.getElementById("security-identity-owner-value"),
 | 
						|
        "page-info-security-no-owner"
 | 
						|
      );
 | 
						|
      setText(
 | 
						|
        "security-identity-verifier-value",
 | 
						|
        info.cAName || info.cert.issuerCommonName || info.cert.issuerName
 | 
						|
      );
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    // We don't have valid identity credentials.
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      document.getElementById("security-identity-owner-value"),
 | 
						|
      "page-info-security-no-owner"
 | 
						|
    );
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      document.getElementById("security-identity-verifier-value"),
 | 
						|
      "page-info-not-specified"
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  if (validity) {
 | 
						|
    setText("security-identity-validity-value", validity);
 | 
						|
  } else {
 | 
						|
    document.getElementById("security-identity-validity-row").hidden = true;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Manage the View Cert button*/
 | 
						|
  var viewCert = document.getElementById("security-view-cert");
 | 
						|
  if (info.cert) {
 | 
						|
    viewCert.collapsed = false;
 | 
						|
  } else {
 | 
						|
    viewCert.collapsed = true;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Set Privacy & History section text */
 | 
						|
 | 
						|
  // Only show quota usage data for websites, not internal sites.
 | 
						|
  if (uri.scheme == "http" || uri.scheme == "https") {
 | 
						|
    SiteDataManager.updateSites().then(() => security._updateSiteDataInfo());
 | 
						|
  } else {
 | 
						|
    document.getElementById("security-privacy-sitedata-row").hidden = true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (realmHasPasswords(uri)) {
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      document.getElementById("security-privacy-passwords-value"),
 | 
						|
      "saved-passwords-yes"
 | 
						|
    );
 | 
						|
  } else {
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      document.getElementById("security-privacy-passwords-value"),
 | 
						|
      "saved-passwords-no"
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  document.l10n.setAttributes(
 | 
						|
    document.getElementById("security-privacy-history-value"),
 | 
						|
    "security-visits-number",
 | 
						|
    { visits: previousVisitCount(windowInfo.hostName) }
 | 
						|
  );
 | 
						|
 | 
						|
  /* Set the Technical Detail section messages */
 | 
						|
  const pkiBundle = document.getElementById("pkiBundle");
 | 
						|
  var hdr;
 | 
						|
  var msg1;
 | 
						|
  var msg2;
 | 
						|
 | 
						|
  if (info.isBroken) {
 | 
						|
    if (info.isMixed) {
 | 
						|
      hdr = pkiBundle.getString("pageInfo_MixedContent");
 | 
						|
      msg1 = pkiBundle.getString("pageInfo_MixedContent2");
 | 
						|
    } else {
 | 
						|
      hdr = pkiBundle.getFormattedString("pageInfo_BrokenEncryption", [
 | 
						|
        info.encryptionAlgorithm,
 | 
						|
        info.encryptionStrength + "",
 | 
						|
        info.version,
 | 
						|
      ]);
 | 
						|
      msg1 = pkiBundle.getString("pageInfo_WeakCipher");
 | 
						|
    }
 | 
						|
    msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
 | 
						|
  } else if (info.encryptionStrength > 0) {
 | 
						|
    hdr = pkiBundle.getFormattedString(
 | 
						|
      "pageInfo_EncryptionWithBitsAndProtocol",
 | 
						|
      [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
 | 
						|
    );
 | 
						|
    msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
 | 
						|
    msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
 | 
						|
  } else {
 | 
						|
    hdr = pkiBundle.getString("pageInfo_NoEncryption");
 | 
						|
    if (windowInfo.hostName != null) {
 | 
						|
      msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [
 | 
						|
        windowInfo.hostName,
 | 
						|
      ]);
 | 
						|
    } else {
 | 
						|
      msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
 | 
						|
    }
 | 
						|
    msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
 | 
						|
  }
 | 
						|
  setText("security-technical-shortform", hdr);
 | 
						|
  setText("security-technical-longform1", msg1);
 | 
						|
  setText("security-technical-longform2", msg2);
 | 
						|
 | 
						|
  const ctStatus = document.getElementById(
 | 
						|
    "security-technical-certificate-transparency"
 | 
						|
  );
 | 
						|
  if (info.certificateTransparency) {
 | 
						|
    ctStatus.hidden = false;
 | 
						|
    ctStatus.value = pkiBundle.getString(
 | 
						|
      "pageInfo_CertificateTransparency_" + info.certificateTransparency
 | 
						|
    );
 | 
						|
  } else {
 | 
						|
    ctStatus.hidden = true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function setText(id, value) {
 | 
						|
  var element = document.getElementById(id);
 | 
						|
  if (!element) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (element.localName == "input" || element.localName == "label") {
 | 
						|
    element.value = value;
 | 
						|
  } else {
 | 
						|
    element.textContent = value;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return true iff realm (proto://host:port) (extracted from uri) has
 | 
						|
 * saved passwords
 | 
						|
 */
 | 
						|
function realmHasPasswords(uri) {
 | 
						|
  return Services.logins.countLogins(uri.prePath, "", "") > 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return the number of previous visits recorded for host before today.
 | 
						|
 *
 | 
						|
 * @param host - the domain name to look for in history
 | 
						|
 */
 | 
						|
function previousVisitCount(host, endTimeReference) {
 | 
						|
  if (!host) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  var historyService = Cc[
 | 
						|
    "@mozilla.org/browser/nav-history-service;1"
 | 
						|
  ].getService(Ci.nsINavHistoryService);
 | 
						|
 | 
						|
  var options = historyService.getNewQueryOptions();
 | 
						|
  options.resultType = options.RESULTS_AS_VISIT;
 | 
						|
 | 
						|
  // Search for visits to this host before today
 | 
						|
  var query = historyService.getNewQuery();
 | 
						|
  query.endTimeReference = query.TIME_RELATIVE_TODAY;
 | 
						|
  query.endTime = 0;
 | 
						|
  query.domain = host;
 | 
						|
 | 
						|
  var result = historyService.executeQuery(query, options);
 | 
						|
  result.root.containerOpen = true;
 | 
						|
  var cc = result.root.childCount;
 | 
						|
  result.root.containerOpen = false;
 | 
						|
  return cc;
 | 
						|
}
 |