c67f9103ca9a
fix signed revocation identifier bounds and release Browser 5.2.55
- time
- 2026-09-12 12:17 UTC · 1 month ago
- machine
- koh2
- agent
- camel-stream/auto
- session
- close revocation bounds
- parent
- 53d1a99ff53f
↓ peach-browser-c67f9103.tar.gzatlas at this save
files changed5 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/shared/device-manifest.test.ts+44 −1src/shared/device-manifest.ts+12 −7manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.2.54",4+ "version": "5.2.55",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {manifest.json+1 −1
@@ -2,7 +2,7 @@22 "manifest_version": 3,33 "minimum_chrome_version": "116",44 "name": "Peach Password Manager",5- "version": "5.2.54",5+ "version": "5.2.55",66 "description": "Zero-knowledge password manager",77 "action": {88 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "5.2.54",3+ "version": "5.2.55",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/shared/device-manifest.test.ts+44 −1
@@ -131,6 +131,49 @@131131 } as DeviceManifestError);132132 });133133 134+ it("rejects oversized revocation identifiers", async () => {135+ const m = await buildSignedDeviceManifest(136+ ISSUER_DEVICE_ID,137+ [entry()],138+ signingPrivateKey,139+ {140+ revoked: [141+ {142+ deviceId: "x".repeat(257),143+ longTermPublicKeyHex: "c".repeat(64),144+ revokedAt: Date.now(),145+ revokedByDeviceId: ISSUER_DEVICE_ID,146+ recordVersion: DEVICE_REVOCATION_RECORD_VERSION,147+ },148+ ],149+ },150+ );151+ await expect(verifyDeviceManifest(m, signingPubkey)).rejects.toMatchObject({152+ kind: "malformed",153+ } as DeviceManifestError);154+ });155+156+ it("rejects oversized revocation issuer identifiers", async () => {157+ const m = await buildSignedDeviceManifest(158+ ISSUER_DEVICE_ID,159+ [entry()],160+ signingPrivateKey,161+ {162+ revoked: [163+ {164+ deviceId: PEER_DEVICE_ID,165+ longTermPublicKeyHex: "c".repeat(64),166+ revokedAt: Date.now(),167+ revokedByDeviceId: "x".repeat(257),168+ recordVersion: DEVICE_REVOCATION_RECORD_VERSION,169+ },170+ ],171+ },172+ );173+ await expect(verifyDeviceManifest(m, signingPubkey)).rejects.toMatchObject({174+ kind: "malformed",175+ } as DeviceManifestError);176+ });134177 it("derives the same keypair across calls (determinism)", async () => {135178 const kp2 = await deriveSyncSigningKeypair(MASTER_KEY, ISSUER_DEVICE_ID);136179 expect(Array.from(kp2.publicKeyRaw)).toEqual(Array.from(signingPubkey));@@ -185,8 +228,8 @@185228 }),186229 ).toBe(CANONICAL);187230 });188-});189231 232+});190233 describe("canonicalise — null fields are omitted (cross-platform parity)", () => {191234 // Android's manifestEntryMap only inserts wsPort / relayUrl when they are192235 // non-null. Match that on the browser side so the manifest signed bysrc/shared/device-manifest.ts+12 −7
@@ -251,8 +251,12 @@251251 throw new DeviceManifestError("malformed", "revoked is not an array");252252 }253253 for (const r of manifest.body.revoked) {254- if (typeof r.deviceId !== "string" || !r.deviceId) {255- throw new DeviceManifestError("malformed", "revocation.deviceId missing");254+ if (255+ typeof r.deviceId !== "string" ||256+ !r.deviceId ||257+ r.deviceId.length > MAX_MANIFEST_STRING258+ ) {259+ throw new DeviceManifestError("malformed", "revocation.deviceId invalid");256260 }257261 if (258262 typeof r.longTermPublicKeyHex !== "string" ||@@ -266,11 +270,12 @@266270 if (typeof r.revokedAt !== "number" || !Number.isFinite(r.revokedAt)) {267271 throw new DeviceManifestError("malformed", "revocation.revokedAt invalid");268272 }269- if (typeof r.revokedByDeviceId !== "string" || !r.revokedByDeviceId) {270- throw new DeviceManifestError(271- "malformed",272- "revocation.revokedByDeviceId missing",273- );273+ if (274+ typeof r.revokedByDeviceId !== "string" ||275+ !r.revokedByDeviceId ||276+ r.revokedByDeviceId.length > MAX_MANIFEST_STRING277+ ) {278+ throw new DeviceManifestError("malformed", "revocation.revokedByDeviceId invalid");274279 }275280 if (276281 r.recordVersion != null &&