9d36d625ca49
style: align sync reassembly deadline flow
- time
- 2026-09-07 01:02 UTC · 1 month ago
- machine
- koh2
- agent
- gpt-5
- session
- red team remediation
- parent
- 69197437712d
↓ peach-browser-9d36d625.tar.gzatlas at this save
files changed1 file
src/shared/sync-transport.ts+41 −41src/shared/sync-transport.ts+41 −41
@@ -542,52 +542,52 @@542542 const ct = await (deadline543543 ? Promise.race([receiveCiphertext(), deadline])544544 : receiveCiphertext());545- const pt = session.decrypt(ct);546- if (buffered === null && pt.byteLength > 0 && pt[0] === FRAME_CONTINUATION_MARKER) {547- // Strip the continuation byte and keep the payload.548- buffered = pt.subarray(1);549- deadline = new Promise<never>((_resolve, reject) => {550- deadlineTimer = setTimeout(() => {551- session.close();552- try { ws.close(1008, "sync frame timeout"); } catch { /* ignore */ }553- reject(new SyncFrameReassemblyTimeoutError());554- }, reassemblyTimeoutMs);555- });556- continue;557- }558- if (buffered !== null) {559- if (pt.byteLength > 0 && pt[0] === FRAME_CONTINUATION_MARKER) {560- const merged: Uint8Array = new Uint8Array(buffered.length + pt.byteLength - 1);561- merged.set(buffered, 0);562- merged.set(pt.subarray(1), buffered.length);563- buffered = merged;564- // PB-26-002: the frame bound must hold DURING reassembly. The565- // post-receive check in the coordinator runs only after the full566- // frame is in memory, so a peer that completes the Noise KK567- // handshake could otherwise stream continuation chunks without568- // bound inside the session window.569- if (buffered.length > SYNC_LIMITS.MAX_FRAME_BYTES) {545+ const pt = session.decrypt(ct);546+ if (buffered === null && pt.byteLength > 0 && pt[0] === FRAME_CONTINUATION_MARKER) {547+ // Strip the continuation byte and keep the payload.548+ buffered = pt.subarray(1);549+ deadline = new Promise<never>((_resolve, reject) => {550+ deadlineTimer = setTimeout(() => {551+ session.close();552+ try { ws.close(1008, "sync frame timeout"); } catch { /* ignore */ }553+ reject(new SyncFrameReassemblyTimeoutError());554+ }, reassemblyTimeoutMs);555+ });556+ continue;557+ }558+ if (buffered !== null) {559+ if (pt.byteLength > 0 && pt[0] === FRAME_CONTINUATION_MARKER) {560+ const merged: Uint8Array = new Uint8Array(buffered.length + pt.byteLength - 1);561+ merged.set(buffered, 0);562+ merged.set(pt.subarray(1), buffered.length);563+ buffered = merged;564+ // PB-26-002: the frame bound must hold DURING reassembly. The565+ // post-receive check in the coordinator runs only after the full566+ // frame is in memory, so a peer that completes the Noise KK567+ // handshake could otherwise stream continuation chunks without568+ // bound inside the session window.569+ if (buffered.length > SYNC_LIMITS.MAX_FRAME_BYTES) {570+ throw new SyncFrameTooLargeError(571+ `sync frame exceeds ${SYNC_LIMITS.MAX_FRAME_BYTES} bytes`,572+ );573+ }574+ continue;575+ }576+ // Final chunk. PB-26-602: the continuation loop only bounds what is577+ // buffered, so the last chunk could push the frame up to 64 KiB past578+ // the cap. Bound the concatenation, not just the buffer.579+ if (buffered.length + pt.byteLength > SYNC_LIMITS.MAX_FRAME_BYTES) {570580 throw new SyncFrameTooLargeError(571581 `sync frame exceeds ${SYNC_LIMITS.MAX_FRAME_BYTES} bytes`,572582 );573583 }574- continue;584+ const reassembled = new Uint8Array(buffered.length + pt.byteLength);585+ reassembled.set(buffered, 0);586+ reassembled.set(pt, buffered.length);587+ return unpadSyncFrame(reassembled);575588 }576- // Final chunk. PB-26-602: the continuation loop only bounds what is577- // buffered, so the last chunk could push the frame up to 64 KiB past578- // the cap. Bound the concatenation, not just the buffer.579- if (buffered.length + pt.byteLength > SYNC_LIMITS.MAX_FRAME_BYTES) {580- throw new SyncFrameTooLargeError(581- `sync frame exceeds ${SYNC_LIMITS.MAX_FRAME_BYTES} bytes`,582- );583- }584- const reassembled = new Uint8Array(buffered.length + pt.byteLength);585- reassembled.set(buffered, 0);586- reassembled.set(pt, buffered.length);587- return unpadSyncFrame(reassembled);588- }589- // Single-chunk frame, exactly as before.590- return unpadSyncFrame(pt);589+ // Single-chunk frame, exactly as before.590+ return unpadSyncFrame(pt);591591 }592592 } finally {593593 if (deadlineTimer !== undefined) clearTimeout(deadlineTimer);