diff --git a/packages/review/src/sharing/client.test.ts b/packages/review/src/sharing/client.test.ts index 1b1c182e9..8a8db9f67 100644 --- a/packages/review/src/sharing/client.test.ts +++ b/packages/review/src/sharing/client.test.ts @@ -1,7 +1,7 @@ import { randomUUID } from "node:crypto"; import { mkdtemp, rm } from "node:fs/promises"; -import { afterEach, expect, it } from "vitest"; +import { afterEach, expect, it, vi } from "vitest"; import { createShareFixture } from "../../test/fixtures/share/create.js"; import { @@ -255,6 +255,25 @@ it.each([409, 503])( }, ); +it("rejects an invalid bundle before contacting the share service", async () => { + const root = await mkdtemp("/tmp/share-client-"); + cleanup.push(() => rm(root, { recursive: true, force: true })); + const fixture = await createShareFixture(root); + cleanup.push(async () => { + await fixture.data.close(); + fixture.store.close(); + }); + + const bundle = await exportShare(fixture); + bundle.objects.clear(); + const network = vi.fn(); + + await expect( + new ShareClient("https://app.dev.fast", undefined, network).create(bundle), + ).rejects.toBeInstanceOf(SharePreflightError); + expect(network).not.toHaveBeenCalled(); +}); + it("bounds concurrent downloads and still rejects corrupt objects", async () => { const root = await mkdtemp("/tmp/share-download-"); cleanup.push(() => rm(root, { recursive: true, force: true })); diff --git a/packages/review/src/sharing/client.ts b/packages/review/src/sharing/client.ts index 3a7145710..20545b4bb 100644 --- a/packages/review/src/sharing/client.ts +++ b/packages/review/src/sharing/client.ts @@ -183,7 +183,17 @@ export class ShareClient { requestId: string = randomUUID(), beforeUpload?: () => Promise, ) { - validateShareBundle(bundle); + try { + validateShareBundle(bundle); + } catch (cause) { + throw new SharePreflightError( + new Error( + "Whiteboard could not prepare this review for sharing. Please report this issue.", + { cause }, + ), + ); + } + const manifestBytes = Buffer.from(JSON.stringify(bundle.manifest)); const created = createdSchema.parse(