From 19c5d4f938ff68b0a56758fbaee84fbbfacf1133 Mon Sep 17 00:00:00 2001 From: happyworker <208suo@208suo.com> Date: Thu, 10 Oct 2024 17:51:17 +0800 Subject: [PATCH 1/2] Fix CVE-2023-28161 --- CVE-2023-28161.patch | 466 +++++++++++++++++++++++++++++++++++++++++++ firefox.spec | 7 +- 2 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 CVE-2023-28161.patch diff --git a/CVE-2023-28161.patch b/CVE-2023-28161.patch new file mode 100644 index 0000000..ad733d1 --- /dev/null +++ b/CVE-2023-28161.patch @@ -0,0 +1,466 @@ +From 336cdeb24ae45e7635deb023c091fa9e45480046 Mon Sep 17 00:00:00 2001 +From: Paul Zuehlcke +Date: Thu, 10 Oct 2024 17:47:09 +0800 +Subject: [PATCH] - Switch SitePermissions TemporaryPermissions and GloballyBlockedPermissions from prePath to origin key. r=prathiksha, a=dmeehan + + +--- + browser/actors/WebRTCParent.jsm | 3 +- + browser/modules/SitePermissions.jsm | 112 +++++++++++------- + .../unit/test_SitePermissions_temporary.js | 55 +++++---- + 3 files changed, 105 insertions(+), 65 deletions(-) + +diff --git a/browser/actors/WebRTCParent.jsm b/browser/actors/WebRTCParent.jsm +index 8891c60..1e79116 100644 +--- a/browser/actors/WebRTCParent.jsm ++++ b/browser/actors/WebRTCParent.jsm +@@ -371,8 +371,7 @@ class WebRTCParent extends JSWindowActorParent { + SitePermissions.ALLOW, + SitePermissions.SCOPE_TEMPORARY, + this.browsingContext.top.embedderElement, +- gracePeriodMs, +- aPermissionPrincipal.URI ++ gracePeriodMs + ); + } + } +diff --git a/browser/modules/SitePermissions.jsm b/browser/modules/SitePermissions.jsm +index a49236b..18fe069 100644 +--- a/browser/modules/SitePermissions.jsm ++++ b/browser/modules/SitePermissions.jsm +@@ -32,18 +32,19 @@ const TemporaryPermissions = { + // This is a three level deep map with the following structure: + // + // Browser => { +- // : { ++ // : { + // : {state: Number, expireTimeout: Number} + // } + // } + // + // Only the top level browser elements are stored via WeakMap. The WeakMap +- // value is an object with URI baseDomains or prePaths as keys. The keys of ++ // value is an object with URI baseDomains or origins as keys. The keys of + // that object are ids that identify permissions that were set for the + // specific URI. The final value is an object containing the permission state + // and the id of the timeout which will cause permission expiry. + // BLOCK permissions are keyed under baseDomain to prevent bypassing the block +- // (see Bug 1492668). Any other permissions are keyed under URI prePath. ++ // (see Bug 1492668). Any other permissions are keyed under origin. ++ + _stateByBrowser: new WeakMap(), + + // Extract baseDomain from uri. Fallback to hostname on conversion error. +@@ -63,24 +64,33 @@ const TemporaryPermissions = { + + /** + * Generate keys to store temporary permissions under. The strict key is +- * nsIURI.prePath, non-strict is URI baseDomain. +- * @param {nsIURI} uri - URI to derive keys from. ++ * origin, non-strict is baseDomain. ++ * @param {nsIPrincipal} principal - principal to derive keys from. + * @returns {Object} keys - Object containing the generated permission keys. + * @returns {string} keys.strict - Key to be used for strict matching. + * @returns {string} keys.nonStrict - Key to be used for non-strict matching. +- * @throws {Error} - Throws if URI is undefined or no valid permission key can ++ * @throws {Error} - Throws if principal is undefined or no valid permission key can + * be generated. + */ +- _getKeysFromURI(uri) { +- return { strict: uri.prePath, nonStrict: this._uriToBaseDomain(uri) }; ++ _getKeysFromPrincipal(principal) { ++ return { strict: principal.origin, nonStrict: principal.baseDomain }; + }, + + // Sets a new permission for the specified browser. +- set(browser, id, state, expireTimeMS, browserURI, expireCallback) { ++ set( ++ browser, ++ id, ++ state, ++ expireTimeMS, ++ principal = browser.contentPrincipal, ++ expireCallback ++ ) { ++ + if ( + !browser || +- !browserURI || +- !SitePermissions.isSupportedScheme(browserURI.scheme) ++ !principal || ++ !SitePermissions.isSupportedPrincipal(principal) ++ + ) { + return; + } +@@ -90,13 +100,13 @@ const TemporaryPermissions = { + this._stateByBrowser.set(browser, entry); + } + let { uriToPerm } = entry; +- // We store blocked permissions by baseDomain. Other states by URI prePath. +- let { strict, nonStrict } = this._getKeysFromURI(browserURI); ++ // We store blocked permissions by baseDomain. Other states by origin. ++ let { strict, nonStrict } = this._getKeysFromPrincipal(principal); + let setKey; + let deleteKey; + // Differenciate between block and non-block permissions. If we store a +- // block permission we need to delete old entries which may be set under URI +- // prePath before setting the new permission for baseDomain. For non-block ++ // block permission we need to delete old entries which may be set under ++ // origin before setting the new permission for baseDomain. For non-block + // permissions this is swapped. + if (state == SitePermissions.BLOCK) { + setKey = nonStrict; +@@ -134,9 +144,9 @@ const TemporaryPermissions = { + state, + }; + +- // If we set a permission state for a prePath we need to reset the old state ++ // If we set a permission state for a origin we need to reset the old state + // which may be set for baseDomain and vice versa. An individual permission +- // must only ever be keyed by either prePath or baseDomain. ++ // must only ever be keyed by either origin or baseDomain. + let permissions = uriToPerm[deleteKey]; + if (!permissions) { + return; +@@ -152,14 +162,18 @@ const TemporaryPermissions = { + remove(browser, id) { + if ( + !browser || +- !SitePermissions.isSupportedScheme(browser.currentURI.scheme) || ++ !SitePermissions.isSupportedPrincipal(browser.contentPrincipal) || + !this._stateByBrowser.has(browser) + ) { + return; + } + // Permission can be stored by any of the two keys (strict and non-strict). + // getKeysFromURI can throw. We let the caller handle the exception. +- let { strict, nonStrict } = this._getKeysFromURI(browser.currentURI); ++ let { strict, nonStrict } = this._getKeysFromPrincipal( ++ browser.contentPrincipal ++ ); ++ ++ + let { uriToPerm } = this._stateByBrowser.get(browser); + for (let key of [nonStrict, strict]) { + if (uriToPerm[key]?.[id] != null) { +@@ -180,15 +194,18 @@ const TemporaryPermissions = { + get(browser, id) { + if ( + !browser || +- !browser.currentURI || +- !SitePermissions.isSupportedScheme(browser.currentURI.scheme) || ++ !browser.contentPrincipal || ++ !SitePermissions.isSupportedPrincipal(browser.contentPrincipal) || + !this._stateByBrowser.has(browser) + ) { + return null; + } + let { uriToPerm } = this._stateByBrowser.get(browser); + +- let { strict, nonStrict } = this._getKeysFromURI(browser.currentURI); ++ let { strict, nonStrict } = this._getKeysFromPrincipal( ++ browser.contentPrincipal ++ ); ++ + for (let key of [nonStrict, strict]) { + if (uriToPerm[key]) { + let permission = uriToPerm[key][id]; +@@ -210,14 +227,17 @@ const TemporaryPermissions = { + getAll(browser) { + let permissions = []; + if ( +- !SitePermissions.isSupportedScheme(browser.currentURI.scheme) || ++ !SitePermissions.isSupportedPrincipal(browser.contentPrincipal) || + !this._stateByBrowser.has(browser) + ) { + return permissions; + } + let { uriToPerm } = this._stateByBrowser.get(browser); + +- let { strict, nonStrict } = this._getKeysFromURI(browser.currentURI); ++ let { strict, nonStrict } = this._getKeysFromPrincipal( ++ browser.contentPrincipal ++ ); ++ + for (let key of [nonStrict, strict]) { + if (uriToPerm[key]) { + let perms = uriToPerm[key]; +@@ -314,19 +334,23 @@ const GloballyBlockedPermissions = { + this._stateByBrowser.set(browser, {}); + } + let entry = this._stateByBrowser.get(browser); +- let prePath = browser.currentURI.prePath; +- if (!entry[prePath]) { +- entry[prePath] = {}; +- } ++ let origin = browser.contentPrincipal.origin; ++ if (!entry[origin]) { ++ entry[origin] = {}; ++ } ++ ++ + +- if (entry[prePath][id]) { ++ if (entry[origin][id]) { + return; + } +- entry[prePath][id] = true; ++ entry[origin][id] = true; + + // Clear the flag and remove the listener once the user has navigated. + // WebProgress will report various things including hashchanges to us, the + // navigation we care about is either leaving the current page or reloading. ++ let { prePath } = browser.currentURI; ++ + browser.addProgressListener( + { + QueryInterface: ChromeUtils.generateQI([ +@@ -342,7 +366,7 @@ const GloballyBlockedPermissions = { + ); + + if (aWebProgress.isTopLevel && (hasLeftPage || isReload)) { +- GloballyBlockedPermissions.remove(browser, id, prePath); ++ GloballyBlockedPermissions.remove(browser, id, origin); + browser.removeProgressListener(this); + } + }, +@@ -352,13 +376,14 @@ const GloballyBlockedPermissions = { + }, + + // Removes a permission with the specified id for the specified browser. +- remove(browser, id, prePath = null) { ++ remove(browser, id, origin = null) { + let entry = this._stateByBrowser.get(browser); +- if (!prePath) { +- prePath = browser.currentURI.prePath; +- } +- if (entry && entry[prePath]) { +- delete entry[prePath][id]; ++ if (!origin) { ++ origin = browser.contentPrincipal.origin; ++ } ++ ++ if (entry && entry[origin]) { ++ delete entry[origin][id]; + } + }, + +@@ -368,9 +393,11 @@ const GloballyBlockedPermissions = { + getAll(browser) { + let permissions = []; + let entry = this._stateByBrowser.get(browser); +- let prePath = browser.currentURI.prePath; +- if (entry && entry[prePath]) { +- let timeStamps = entry[prePath]; ++ ++ let origin = browser.contentPrincipal.origin; ++ if (entry && entry[origin]) { ++ let timeStamps = entry[origin]; ++ + for (let id of Object.keys(timeStamps)) { + permissions.push({ + id, +@@ -788,8 +815,7 @@ var SitePermissions = { + state, + scope = this.SCOPE_PERSISTENT, + browser = null, +- expireTimeMS = SitePermissions.temporaryPermissionExpireTime, +- browserURI = browser?.currentURI ++ expireTimeMS = SitePermissions.temporaryPermissionExpireTime + ) { + if (!principal && !browser) { + throw new Error( +@@ -836,7 +862,7 @@ var SitePermissions = { + permissionID, + state, + expireTimeMS, +- browserURI, ++ principal ?? browser.contentPrincipal, + // On permission expiry + origBrowser => { + if (!origBrowser.ownerGlobal) { +diff --git a/browser/modules/test/unit/test_SitePermissions_temporary.js b/browser/modules/test/unit/test_SitePermissions_temporary.js +index fa76627..5d48d01 100644 +--- a/browser/modules/test/unit/test_SitePermissions_temporary.js ++++ b/browser/modules/test/unit/test_SitePermissions_temporary.js +@@ -39,6 +39,19 @@ function createDummyBrowser(spec) { + }; + } + ++function navigateDummyBrowser(browser, uri) { ++ // Callers may pass in either uri strings or nsIURI objects. ++ if (typeof uri == "string") { ++ uri = Services.io.newURI(uri); ++ } ++ browser.currentURI = uri; ++ browser.contentPrincipal = Services.scriptSecurityManager.createContentPrincipal( ++ browser.currentURI, ++ {} ++ ); ++} ++ ++ + /** + * Tests that temporary permissions with different block states are stored + * (set, overwrite, delete) correctly. +@@ -120,11 +133,11 @@ add_task(async function testAllowBlock() { + ok(uriToPerm, "Entry should have uriToPerm object."); + Assert.equal(Object.keys(uriToPerm).length, 2, "uriToPerm has 2 entries."); + +- let permissionsA = uriToPerm[BROWSER_A.currentURI.prePath]; ++ let permissionsA = uriToPerm[BROWSER_A.contentPrincipal.origin]; + let permissionsB = + uriToPerm[Services.eTLD.getBaseDomain(BROWSER_A.currentURI)]; + +- ok(permissionsA, "Allow should be keyed under prePath"); ++ ok(permissionsA, "Allow should be keyed under origin"); + ok(permissionsB, "Block should be keyed under baseDomain"); + + let permissionA = permissionsA[PERM_A]; +@@ -168,7 +181,7 @@ add_task(async function testAllowBlock() { + ); + + // Overwrite permission B - this time with a non-block state which means it +- // should be keyed by URI prePath now. ++ // should be keyed by URI origin now. + SitePermissions.setForPrincipal( + null, + PERM_B, +@@ -185,11 +198,11 @@ add_task(async function testAllowBlock() { + "Should not longer have baseDomain permission entry" + ); + +- permissionsB = uriToPerm[BROWSER_A.currentURI.prePath]; ++ permissionsB = uriToPerm[BROWSER_A.contentPrincipal.origin]; + permissionB = permissionsB[PERM_B]; + Assert.ok( + permissionsB && permissionB, +- "Overwritten permission should be keyed under prePath" ++ "Overwritten permission should be keyed under origin" + ); + Assert.equal( + permissionB.state, +@@ -409,7 +422,7 @@ add_task(async function testCallbackOnExpiry() { + PERM_A, + SitePermissions.BLOCK, + 100, +- BROWSER_A.currentURI, ++ undefined, + resolve + ); + }); +@@ -419,7 +432,7 @@ add_task(async function testCallbackOnExpiry() { + PERM_A, + SitePermissions.BLOCK, + 100, +- BROWSER_B.currentURI, ++ BROWSER_B.contentPrincipal, + resolve + ); + }); +@@ -452,7 +465,7 @@ add_task(async function testCallbackOnExpiryUpdatedBrowser() { + PERM_A, + SitePermissions.BLOCK, + 200, +- BROWSER_A.currentURI, ++ undefined, + resolve + ); + }); +@@ -506,7 +519,7 @@ add_task(async function testInvalidExpiryTime() { + }); + + /** +- * Tests that we block by base domain but allow by prepath. ++ * Tests that we block by base domain but allow by origin. + */ + add_task(async function testTemporaryPermissionScope() { + let states = { +@@ -515,10 +528,10 @@ add_task(async function testTemporaryPermissionScope() { + "https://example.com", + "https://example.com/sub/path", + "https://example.com:443", ++ "https://name:password@example.com", + ], + different: [ + "https://example.com", +- "https://name:password@example.com", + "https://test1.example.com", + "http://example.com", + "http://example.org", +@@ -549,9 +562,13 @@ add_task(async function testTemporaryPermissionScope() { + + Object.entries(lists).forEach(([type, list]) => { + let expectSet = type == "same"; ++ ok(true, "origin:" + browser.contentPrincipal.origin); ++ + + for (let uri of list) { +- let browser = createDummyBrowser(uri); ++ navigateDummyBrowser(browser, otherUri); ++ ok(true, "new origin:" + browser.contentPrincipal.origin); ++ + SitePermissions.setForPrincipal( + null, + PERM_A, +@@ -590,19 +607,18 @@ add_task(async function testTemporaryPermissionScope() { + }); + + /** +- * Tests that we can override the URI to use for keying temporary permissions. ++ * Tests that we can override the principal to use for keying temporary permissions. + */ + add_task(async function testOverrideBrowserURI() { + let testBrowser = createDummyBrowser("https://old.example.com/foo"); + let overrideURI = Services.io.newURI("https://test.example.org/test/path"); + SitePermissions.setForPrincipal( +- null, ++ Services.scriptSecurityManager.createContentPrincipal(overrideURI, {}), + PERM_A, + SitePermissions.ALLOW, + SitePermissions.SCOPE_TEMPORARY, + testBrowser, +- EXPIRY_MS_A, +- overrideURI ++ EXPIRY_MS_A + ); + + Assert.deepEqual( +@@ -615,7 +631,7 @@ add_task(async function testOverrideBrowserURI() { + ); + + // "Navigate" to new URI +- testBrowser.currentURI = overrideURI; ++ navigateDummyBrowser(testBrowser, overrideURI); + + Assert.deepEqual( + SitePermissions.getForPrincipal(null, PERM_A, testBrowser), +@@ -638,13 +654,12 @@ add_task(async function testPermissionUnsupportedScheme() { + + // Incompatible override URI should not throw or store any permissions. + SitePermissions.setForPrincipal( +- null, ++ Services.scriptSecurityManager.createContentPrincipal(aboutURI, {}), + PERM_A, + SitePermissions.ALLOW, + SitePermissions.SCOPE_TEMPORARY, + BROWSER_A, +- EXPIRY_MS_B, +- aboutURI ++ EXPIRY_MS_B + ); + Assert.ok( + SitePermissions._temporaryPermissions._stateByBrowser.has(BROWSER_A), +@@ -662,7 +677,7 @@ add_task(async function testPermissionUnsupportedScheme() { + ); + + // Change browser URI to about:blank. +- browser.currentURI = aboutURI; ++ navigateDummyBrowser(browser, aboutURI); + + // Setting permission for browser with unsupported URI should not throw. + SitePermissions.setForPrincipal( +-- +2.27.0 + diff --git a/firefox.spec b/firefox.spec index de6c258..0ae7e35 100644 --- a/firefox.spec +++ b/firefox.spec @@ -148,7 +148,7 @@ Summary: Mozilla Firefox Web browser Name: firefox Version: 102.15.0 -Release: 7 +Release: 8 URL: https://www.mozilla.org/firefox/ License: MPLv1.1 or GPLv2+ or LGPLv2+ @@ -247,6 +247,7 @@ Patch430: CVE-2023-7104.patch Patch431: CVE-2022-3479.patch Patch432: CVE-2023-44488.patch Patch433: CVE-2024-0745.patch +Patch434: CVE-2023-28161.patch # PGO/LTO patches Patch600: pgo.patch @@ -547,6 +548,7 @@ to run Firefox explicitly on X11. %patch431 -p1 %patch432 -p1 %patch433 -p1 +%patch434 -p1 # PGO patches %if %{build_with_pgo} @@ -1141,6 +1143,9 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %endif %changelog +* Thu Oct 10 2024 happyworker <208suo@208suo.com> - 102.15.0-8 +- Fix CVE-2023-28161 + * Tue Aug 27 2024 lvfei - 102.15.0-7 - Fix CVE-2024-0745 -- Gitee From f6dcbc9f5381e47b5a606f6dc4045e0985990e36 Mon Sep 17 00:00:00 2001 From: wj00037 <1292876134@qq.com> Date: Fri, 1 Nov 2024 15:50:38 +0800 Subject: [PATCH 2/2] add .lfsconfig --- .lfsconfig | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .lfsconfig diff --git a/.lfsconfig b/.lfsconfig new file mode 100644 index 0000000..dc5494e --- /dev/null +++ b/.lfsconfig @@ -0,0 +1,2 @@ +[lfs] + url = https://openeuler-bigfiles.test.osinfra.cn/src-openEuler/firefox -- Gitee