From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- .../extensions/internal/AddonRepository.sys.mjs | 6 +- .../extensions/internal/AddonTestUtils.sys.mjs | 31 +++++- .../extensions/internal/GMPProvider.sys.mjs | 6 +- .../internal/ProductAddonChecker.sys.mjs | 37 ++++--- .../internal/SitePermsAddonProvider.sys.mjs | 4 +- .../extensions/internal/XPIDatabase.sys.mjs | 61 +++++++----- .../mozapps/extensions/internal/XPIInstall.sys.mjs | 107 ++++++++++++++++++--- .../extensions/internal/XPIProvider.sys.mjs | 6 +- .../extensions/internal/crypto-utils.sys.mjs | 10 +- 9 files changed, 204 insertions(+), 64 deletions(-) (limited to 'toolkit/mozapps/extensions/internal') diff --git a/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs b/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs index e53e4af7a4..8d4d178924 100644 --- a/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs +++ b/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs @@ -466,13 +466,13 @@ export var AddonRepository = { request.open("GET", url, true); request.responseType = "json"; - request.addEventListener("error", aEvent => { + request.addEventListener("error", () => { reject(new Error(`GET ${url} failed`)); }); - request.addEventListener("timeout", aEvent => { + request.addEventListener("timeout", () => { reject(new Error(`GET ${url} timed out`)); }); - request.addEventListener("load", aEvent => { + request.addEventListener("load", () => { let response = request.response; if (!response || (request.status != 200 && request.status != 0)) { reject(new Error(`GET ${url} failed (status ${request.status})`)); diff --git a/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs b/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs index f53d32092d..7e4adacd11 100644 --- a/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs +++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs @@ -3,7 +3,7 @@ */ /* eslint "mozilla/no-aArgs": 1 */ -/* eslint "no-unused-vars": [2, {"args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}] */ +/* eslint "no-unused-vars": [2, {"argsIgnorePattern": "^_", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}] */ /* eslint "semi": [2, "always"] */ /* eslint "valid-jsdoc": [2, {requireReturn: false}] */ @@ -424,6 +424,35 @@ export var AddonTestUtils = { }); }, + getXPIExports() { + return ChromeUtils.importESModule( + "resource://gre/modules/addons/XPIExports.sys.mjs" + ).XPIExports; + }, + + getWeakSignatureInstallPrefName() { + return this.getXPIExports().XPIInstall.getWeakSignatureInstallPrefName(); + }, + + setWeakSignatureInstallAllowed(allowed) { + const prefName = this.getWeakSignatureInstallPrefName(); + let cleanupCalled = false; + const cleanup = () => { + if (cleanupCalled) { + return; + } + this.testScope.info( + `=== clear ${prefName} pref value set by this test file ===` + ); + Services.prefs.clearUserPref(prefName); + cleanupCalled = true; + }; + this.testScope.registerCleanupFunction(cleanup); + this.testScope.info(`=== set ${prefName} pref value to ${allowed} ===`); + Services.prefs.setBoolPref(prefName, allowed); + return cleanup; + }, + /** * Iterates over the entries in a given directory. * diff --git a/toolkit/mozapps/extensions/internal/GMPProvider.sys.mjs b/toolkit/mozapps/extensions/internal/GMPProvider.sys.mjs index aaac109fc0..22ca2365d1 100644 --- a/toolkit/mozapps/extensions/internal/GMPProvider.sys.mjs +++ b/toolkit/mozapps/extensions/internal/GMPProvider.sys.mjs @@ -340,7 +340,7 @@ GMPWrapper.prototype = { return { source: "gmp-plugin" }; }, - isCompatibleWith(aAppVersion, aPlatformVersion) { + isCompatibleWith() { return true; }, @@ -377,7 +377,7 @@ GMPWrapper.prototype = { * Widevine is not yet installed, or if the user toggles prefs to enable EME. * For the function used in those cases see `checkForUpdates`. */ - findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) { + findUpdates(aListener, aReason) { this._log.trace( "findUpdates() - " + this._plugin.id + " - reason=" + aReason ); @@ -895,7 +895,7 @@ var GMPProvider = { } }, - observe(subject, topic, data) { + observe(subject, topic) { if (topic == FIRST_CONTENT_PROCESS_TOPIC) { lazy.AddonManagerPrivate.registerProvider(GMPProvider, ["plugin"]); Services.obs.notifyObservers(null, "gmp-provider-registered"); diff --git a/toolkit/mozapps/extensions/internal/ProductAddonChecker.sys.mjs b/toolkit/mozapps/extensions/internal/ProductAddonChecker.sys.mjs index 1615a551c8..64b337af3f 100644 --- a/toolkit/mozapps/extensions/internal/ProductAddonChecker.sys.mjs +++ b/toolkit/mozapps/extensions/internal/ProductAddonChecker.sys.mjs @@ -118,12 +118,18 @@ async function conservativeFetch(input) { * @param contentSignatureHeader * The contents of the 'content-signature' header received along with * `data`. + * @param trustedRoot + * The identifier of the trusted root to use for certificate validation. * @return A promise that will resolve to nothing if the signature verification * succeeds, or rejects on failure, with an Error that sets its * addonCheckerErr property disambiguate failure cases and a message * explaining the error. */ -async function verifyGmpContentSignature(data, contentSignatureHeader) { +async function verifyGmpContentSignature( + data, + contentSignatureHeader, + trustedRoot +) { if (!contentSignatureHeader) { logger.warn( "Unexpected missing content signature header during content signature validation" @@ -186,13 +192,6 @@ async function verifyGmpContentSignature(data, contentSignatureHeader) { "@mozilla.org/security/contentsignatureverifier;1" ].createInstance(Ci.nsIContentSignatureVerifier); - // See bug 1771992. In the future, this may need to handle staging and dev - // environments in addition to just production and testing. - let root = Ci.nsIContentSignatureVerifier.ContentSignatureProdRoot; - if (Services.env.exists("XPCSHELL_TEST_PROFILE_DIR")) { - root = Ci.nsIX509CertDB.AppXPCShellRoot; - } - let valid; try { valid = await verifier.asyncVerifyContentSignature( @@ -200,7 +199,7 @@ async function verifyGmpContentSignature(data, contentSignatureHeader) { signature, certChain, "aus.content-signature.mozilla.org", - root + trustedRoot ); } catch (err) { logger.warn(`Unexpected error while validating content signature: ${err}`); @@ -329,6 +328,9 @@ function downloadXMLWithRequest( * @param verifyContentSignature * When true, will verify the content signature information from the * response header. Failure to verify will result in an error. + * @param trustedContentSignatureRoot + * The trusted root to use for certificate validation. + * Must be set if verifyContentSignature is true. * @return a promise that resolves to the DOM document downloaded or rejects * with a JS exception in case of error. */ @@ -336,7 +338,8 @@ async function downloadXML( url, allowNonBuiltIn = false, allowedCerts = null, - verifyContentSignature = false + verifyContentSignature = false, + trustedContentSignatureRoot = null ) { let request = await downloadXMLWithRequest( url, @@ -346,7 +349,8 @@ async function downloadXML( if (verifyContentSignature) { await verifyGmpContentSignature( request.response, - request.getResponseHeader("content-signature") + request.getResponseHeader("content-signature"), + trustedContentSignatureRoot ); } return request.responseXML; @@ -422,7 +426,7 @@ function downloadFile(url, options = { httpsOnlyNoUpgrade: false }) { return new Promise((resolve, reject) => { let sr = new lazy.ServiceRequest(); - sr.onload = function (response) { + sr.onload = function () { logger.info("downloadFile File download. status=" + sr.status); if (sr.status != 200 && sr.status != 206) { reject(Components.Exception("File download failed", sr.status)); @@ -535,6 +539,9 @@ export const ProductAddonChecker = { * @param verifyContentSignature * When true, will verify the content signature information from the * response header. Failure to verify will result in an error. + * @param trustedContentSignatureRoot + * The trusted root to use for certificate validation. + * Must be set if verifyContentSignature is true. * @return a promise that resolves to an object containing the list of add-ons * and whether the local fallback was used, or rejects with a JS * exception in case of error. In the case of an error, a best effort @@ -545,13 +552,15 @@ export const ProductAddonChecker = { url, allowNonBuiltIn = false, allowedCerts = null, - verifyContentSignature = false + verifyContentSignature = false, + trustedContentSignatureRoot = null ) { return downloadXML( url, allowNonBuiltIn, allowedCerts, - verifyContentSignature + verifyContentSignature, + trustedContentSignatureRoot ).then(parseXML); }, diff --git a/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs b/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs index 7ca952dc8a..8af3c2affe 100644 --- a/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs +++ b/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs @@ -190,7 +190,7 @@ class SitePermsAddonWrapper { return 0; } - async updateBlocklistState(options = {}) {} + async updateBlocklistState() {} get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; @@ -277,7 +277,7 @@ class SitePermsAddonWrapper { return { source: "siteperm-addon-provider", method: "synthetic-install" }; } - isCompatibleWith(aAppVersion, aPlatformVersion) { + isCompatibleWith() { return true; } } diff --git a/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs b/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs index af0b02444a..d7541167fa 100644 --- a/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs +++ b/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs @@ -31,6 +31,7 @@ ChromeUtils.defineESModuleGetters(lazy, { DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", ExtensionData: "resource://gre/modules/Extension.sys.mjs", ExtensionUtils: "resource://gre/modules/ExtensionUtils.sys.mjs", + ObjectUtils: "resource://gre/modules/ObjectUtils.sys.mjs", PermissionsUtils: "resource://gre/modules/PermissionsUtils.sys.mjs", QuarantinedDomains: "resource://gre/modules/ExtensionPermissions.sys.mjs", }); @@ -192,6 +193,7 @@ const PROP_JSON_FIELDS = [ "targetApplications", "targetPlatforms", "signedState", + "signedTypes", "signedDate", "seen", "dependencies", @@ -1556,6 +1558,7 @@ function defineAddonWrapperProperty(name, getter) { "validInstallOrigins", "dependencies", "signedState", + "signedTypes", "sitePermissions", "siteOrigin", "isCorrectlySigned", @@ -2175,7 +2178,7 @@ export const XPIDatabase = { */ async verifySignatures() { try { - let addons = await this.getAddonList(a => true); + let addons = await this.getAddonList(() => true); let changes = { enabled: [], @@ -2188,17 +2191,31 @@ export const XPIDatabase = { continue; } - let signedState = await XPIExports.verifyBundleSignedState( - addon._sourceBundle, - addon - ); + let { signedState, signedTypes } = + await XPIExports.verifyBundleSignedState(addon._sourceBundle, addon); + + const changedProperties = []; if (signedState != addon.signedState) { addon.signedState = signedState; + changedProperties.push("signedState"); + } + + if ( + !lazy.ObjectUtils.deepEqual( + signedTypes?.toSorted(), + addon.signedTypes?.toSorted() + ) + ) { + addon.signedTypes = signedTypes; + changedProperties.push("signedTypes"); + } + + if (changedProperties.length) { lazy.AddonManagerPrivate.callAddonListeners( "onPropertyChanged", addon.wrapper, - ["signedState"] + changedProperties ); } @@ -2426,7 +2443,7 @@ export const XPIDatabase = { if (!this.addonDB) { return []; } - return _filterDB(this.addonDB, aAddon => true); + return _filterDB(this.addonDB, () => true); }, /** @@ -3084,24 +3101,11 @@ export const XPIDatabaseReconcile = { * The new state of the add-on * @param {AddonInternal?} [aNewAddon] * The manifest for the new add-on if it has already been loaded - * @param {string?} [aOldAppVersion] - * The version of the application last run with this profile or null - * if it is a new profile or the version is unknown - * @param {string?} [aOldPlatformVersion] - * The version of the platform last run with this profile or null - * if it is a new profile or the version is unknown * @returns {boolean} * A boolean indicating if flushing caches is required to complete * changing this add-on */ - addMetadata( - aLocation, - aId, - aAddonState, - aNewAddon, - aOldAppVersion, - aOldPlatformVersion - ) { + addMetadata(aLocation, aId, aAddonState, aNewAddon) { logger.debug(`New add-on ${aId} installed in ${aLocation.name}`); // We treat this is a new install if, @@ -3348,6 +3352,10 @@ export const XPIDatabaseReconcile = { let signedDateMissing = aOldAddon.signedDate === undefined && (aOldAddon.signedState || checkSigning); + // signedTypes must be set if signedState is set. + let signedTypesMissing = + aOldAddon.signedTypes === undefined && + (aOldAddon.signedState || checkSigning); // If maxVersion was inadvertently updated for a locale, force a reload // from the manifest. See Bug 1646016 for details. @@ -3360,7 +3368,12 @@ export const XPIDatabaseReconcile = { } let manifest = null; - if (checkSigning || aReloadMetadata || signedDateMissing) { + if ( + checkSigning || + aReloadMetadata || + signedDateMissing || + signedTypesMissing + ) { try { manifest = XPIExports.XPIInstall.syncLoadManifest( aAddonState, @@ -3384,6 +3397,10 @@ export const XPIDatabaseReconcile = { aOldAddon.signedDate = manifest.signedDate; } + if (signedTypesMissing) { + aOldAddon.signedTypes = manifest.signedTypes; + } + // May be updating from a version of the app that didn't support all the // properties of the currently-installed add-ons. if (aReloadMetadata) { diff --git a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs index 0402c2f2ca..4a26785da8 100644 --- a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs +++ b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs @@ -21,6 +21,7 @@ import { XPIExports } from "resource://gre/modules/addons/XPIExports.sys.mjs"; import { computeSha256HashAsString, getHashStringForCrypto, + hasStrongSignature, } from "resource://gre/modules/addons/crypto-utils.sys.mjs"; import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; import { @@ -94,6 +95,9 @@ const PREF_XPI_ENABLED = "xpinstall.enabled"; const PREF_XPI_DIRECT_WHITELISTED = "xpinstall.whitelist.directRequest"; const PREF_XPI_FILE_WHITELISTED = "xpinstall.whitelist.fileRequest"; const PREF_XPI_WHITELIST_REQUIRED = "xpinstall.whitelist.required"; +const PREF_XPI_WEAK_SIGNATURES_ALLOWED = + "xpinstall.signatures.weakSignaturesTemporarilyAllowed"; +const PREF_XPI_WEAK_SIGNATURES_ALLOWED_DEFAULT = true; const PREF_SELECTED_THEME = "extensions.activeThemeID"; @@ -230,7 +234,15 @@ class Package { let root = Ci.nsIX509CertDB.AddonsPublicRoot; if ( - !AppConstants.MOZ_REQUIRE_SIGNING && + (!AppConstants.MOZ_REQUIRE_SIGNING || + // Allow mochitests to switch to dev-root on all channels. + Cu.isInAutomation || + // Allow xpcshell tests to switch to dev-root on all channels, + // included tests where "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer" + // pref is set to false and Cu.isInAutomation is going to be false (e.g. test_signed_langpack.js). + // TODO(Bug 1598804): we should be able to remove the following checks once Cu.isAutomation is fixed. + (Services.env.exists("XPCSHELL_TEST_PROFILE_DIR") && + Services.appinfo.name === "XPCShell")) && Services.prefs.getBoolPref(PREF_XPI_SIGNATURES_DEV_ROOT, false) ) { root = Ci.nsIX509CertDB.AddonsStageRoot; @@ -287,7 +299,7 @@ DirPackage = class DirPackage extends Package { return IOUtils.read(PathUtils.join(this.filePath, ...path)); } - async verifySignedStateForRoot(addonId, root) { + async verifySignedStateForRoot() { return { signedState: AddonManager.SIGNEDSTATE_UNKNOWN, cert: null }; } }; @@ -341,8 +353,11 @@ XPIPackage = class XPIPackage extends Package { aZipReader.close(); } resolve({ - signedState: getSignedStatus(aRv, cert, addonId), cert, + signedState: getSignedStatus(aRv, cert, addonId), + signedTypes: aSignatureInfos?.map( + signatureInfo => signatureInfo.signatureAlgorithm + ), }); }, }; @@ -511,17 +526,18 @@ async function loadManifestFromWebManifest(aPackage, aLocation) { addon.siteOrigin = manifest.install_origins[0]; } - if (manifest.options_ui) { + const { optionsPageProperties } = extension; + if (optionsPageProperties) { // Store just the relative path here, the AddonWrapper getURL // wrapper maps this to a full URL. - addon.optionsURL = manifest.options_ui.page; - if (manifest.options_ui.open_in_tab) { + addon.optionsURL = optionsPageProperties.page; + if (optionsPageProperties.open_in_tab) { addon.optionsType = AddonManager.OPTIONS_TYPE_TAB; } else { addon.optionsType = AddonManager.OPTIONS_TYPE_INLINE_BROWSER; } - addon.optionsBrowserStyle = manifest.options_ui.browser_style; + addon.optionsBrowserStyle = optionsPageProperties.browser_style; } // WebExtensions don't use iconURLs @@ -693,9 +709,13 @@ var loadManifest = async function (aPackage, aLocation, aOldAddon) { addon.rootURI = aPackage.rootURI.spec; addon.location = aLocation; - let { signedState, cert } = verifiedSignedState; + let { cert, signedState, signedTypes } = verifiedSignedState; addon.signedState = signedState; addon.signedDate = cert?.validity?.notBefore / 1000 || null; + // An array of the algorithms used by the signatures found in the signed XPI files, + // as an array of integers (see nsIAppSignatureInfo_SignatureAlgorithm enum defined + // in nsIX509CertDB.idl). + addon.signedTypes = signedTypes; if (!addon.id) { if (cert) { @@ -909,18 +929,21 @@ function shouldVerifySignedState(aAddonType, aLocation) { * The nsIFile for the bundle to check, either a directory or zip file. * @param {AddonInternal} aAddon * The add-on object to verify. - * @returns {Promise} - * A Promise that resolves to an AddonManager.SIGNEDSTATE_* constant. + * @returns {Promise<{ signedState: number, signedTypes: Array}>?} + * A Promise that resolves to object including a signedState property set to + * an AddonManager.SIGNEDSTATE_* constant and a signedTypes property set to + * either an array of Ci.nsIAppSignatureInfo SignatureAlgorithm enum values + * or undefined if the file wasn't signed. */ export var verifyBundleSignedState = async function (aBundle, aAddon) { let pkg = Package.get(aBundle); try { - let { signedState } = await pkg.verifySignedState( + let { signedState, signedTypes } = await pkg.verifySignedState( aAddon.id, aAddon.type, aAddon.location ); - return signedState; + return { signedState, signedTypes }; } finally { pkg.close(); } @@ -1633,6 +1656,49 @@ class AddonInstall { "signature verification failed", ]); } + + // Restrict install for signed extension only signed with weak signature algorithms, unless the + // restriction is explicitly disabled through prefs or enterprise policies. + if ( + !XPIInstall.isWeakSignatureInstallAllowed() && + this.addon.signedDate && + !hasStrongSignature(this.addon) + ) { + const addonAllowedByPolicies = Services.policies.getExtensionSettings( + this.addon.id + )?.temporarily_allow_weak_signatures; + + const globallyAllowedByPolicies = + Services.policies.getExtensionSettings( + "*" + )?.temporarily_allow_weak_signatures; + + const allowedByPolicies = + (globallyAllowedByPolicies && + (addonAllowedByPolicies || addonAllowedByPolicies == null)) || + addonAllowedByPolicies; + + if ( + !allowedByPolicies && + (!this.existingAddon || hasStrongSignature(this.existingAddon)) + ) { + // Reject if it is a new install or installing over an existing addon including + // strong cryptographic signatures. + return Promise.reject([ + AddonManager.ERROR_CORRUPT_FILE, + "install rejected due to the package not including a strong cryptographic signature", + ]); + } + + // Still allow installs using weak signatures to install if either: + // - it is explicitly allowed through Enterprise Policies Settings + // - or there is an existing addon with a weak signature. + logger.warn( + allowedByPolicies + ? `Allow weak signature install for ${this.addon.id} XPI due to Enterprise Policies` + : `Allow weak signature install over existing "${this.existingAddon.id}" XPI` + ); + } } } finally { pkg.close(); @@ -2321,7 +2387,7 @@ var DownloadAddonInstall = class extends AddonInstall { } } - observe(aSubject, aTopic, aData) { + observe() { // Network is going offline this.cancel(); } @@ -2592,7 +2658,7 @@ var DownloadAddonInstall = class extends AddonInstall { new UpdateChecker( this.addon, { - onUpdateFinished: aAddon => this.downloadCompleted(), + onUpdateFinished: () => this.downloadCompleted(), }, AddonManager.UPDATE_WHEN_ADDON_INSTALLED ); @@ -3880,7 +3946,7 @@ class SystemAddonInstaller extends DirectoryInstaller { } // old system add-on upgrade dirs get automatically removed - uninstallAddon(aAddon) {} + uninstallAddon() {} } var AppUpdate = { @@ -4344,6 +4410,17 @@ export var XPIInstall = { return Services.prefs.getBoolPref(PREF_XPI_FILE_WHITELISTED, true); }, + isWeakSignatureInstallAllowed() { + return Services.prefs.getBoolPref( + PREF_XPI_WEAK_SIGNATURES_ALLOWED, + PREF_XPI_WEAK_SIGNATURES_ALLOWED_DEFAULT + ); + }, + + getWeakSignatureInstallPrefName() { + return PREF_XPI_WEAK_SIGNATURES_ALLOWED; + }, + /** * Called to test whether installing XPI add-ons from a URI is allowed. * diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs index 12d4fa1172..3c090b5f0f 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs +++ b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs @@ -120,7 +120,7 @@ const XPI_PERMISSION = "install"; const XPI_SIGNATURE_CHECK_PERIOD = 24 * 60 * 60; -const DB_SCHEMA = 35; +const DB_SCHEMA = 36; XPCOMUtils.defineLazyPreferenceGetter( lazy, @@ -1598,7 +1598,7 @@ var XPIStates = { * * @returns {XPIState?} */ - findAddon(aId, aFilter = location => true) { + findAddon(aId, aFilter = () => true) { // Fortunately the Map iterator returns in order of insertion, which is // also our highest -> lowest priority order. for (let location of this.locations()) { @@ -2706,7 +2706,7 @@ export var XPIProvider = { "profile-before-change", "test-load-xpi-database", ]; - let observer = (subject, topic, data) => { + let observer = (subject, topic) => { if ( topic == "xul-window-visible" && !Services.wm.getMostRecentWindow("devtools:toolbox") diff --git a/toolkit/mozapps/extensions/internal/crypto-utils.sys.mjs b/toolkit/mozapps/extensions/internal/crypto-utils.sys.mjs index b1304bcdfc..9ef096df0f 100644 --- a/toolkit/mozapps/extensions/internal/crypto-utils.sys.mjs +++ b/toolkit/mozapps/extensions/internal/crypto-utils.sys.mjs @@ -8,6 +8,14 @@ const CryptoHash = Components.Constructor( "initWithString" ); +const XPI_WEAK_SIGNATURES = [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1]; + +export function hasStrongSignature(addon) { + return !!addon.signedTypes?.filter( + algorithm => !XPI_WEAK_SIGNATURES.includes(algorithm) + ).length; +} + export function computeHashAsString(hashType, input) { const data = new Uint8Array(new TextEncoder().encode(input)); const crypto = CryptoHash(hashType); @@ -42,7 +50,7 @@ export function computeSha1HashAsString(input) { /** * Returns the string representation (hex) of a given CryptoHashInstance. * - * @param {CryptoHash} aCrypto + * @param {nsICryptoHash} aCrypto * @returns {string} * The hex representation of a SHA256 hash. */ -- cgit v1.2.3