Reject a binary file transfer whose byte count differs from FileBegin.size

concatByteChunks allocated FileBegin.size bytes and copied the chunks in:
a short transfer resolved as a zero-padded file, and a long one threw a
RangeError inside the transport callback and left the request pending.
Count the received bytes at FileEnd and reject the read when they differ
from the announced size.
This commit is contained in:
Michael Vandesompel
2026-10-14 14:15:11 +02:00
committed by paseo-bot[bot]
parent 2bf69e6d8e
commit fe422acbf4
2 changed files with 46 additions and 0 deletions
+34
View File
@@ -2863,6 +2863,40 @@ test("readFile with timeout 0 outlives the default request timeout", async () =>
expect(new TextDecoder().decode(result.bytes)).toBe("hello");
});
test("readFile rejects a binary transfer that ends before the announced size", async () => {
const { mock, client } = await connectFileClient("clsk_file_short");
const responsePromise = client.readFile("/tmp/project", "short.bin", "req-short");
sendBinaryFile(mock, "req-short", 10, ["hello"]);
mock.triggerMessage(
encodeFileTransferFrame({ opcode: FileTransferOpcode.FileEnd, requestId: "req-short" }),
);
await expect(responsePromise).rejects.toThrow(
"File transfer incomplete: expected 10 bytes, received 5.",
);
});
test("readFile rejects a binary transfer that exceeds the announced size", async () => {
const { mock, client } = await connectFileClient("clsk_file_long");
const responsePromise = client.readFile("/tmp/project", "long.bin", "req-long");
const settled = responsePromise.then(
() => null,
(error: unknown) => error,
);
sendBinaryFile(mock, "req-long", 4, ["hello!"]);
expect(() =>
mock.triggerMessage(
encodeFileTransferFrame({ opcode: FileTransferOpcode.FileEnd, requestId: "req-long" }),
),
).not.toThrow();
expect(await settled).toMatchObject({
message: "File transfer incomplete: expected 4 bytes, received 6.",
});
});
test("uploadFile sends metadata request and file bytes as binary chunks", async () => {
const logger = createMockLogger();
const mock = createMockTransport();
+12
View File
@@ -6549,6 +6549,18 @@ export class DaemonClient {
return;
}
const received = transfer.chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
if (received !== transfer.size) {
this.activeBinaryFileTransfers.delete(frame.requestId);
this.rejectWaitersForRequestId(
frame.requestId,
new Error(
`File transfer incomplete: expected ${transfer.size} bytes, received ${received}.`,
),
);
return;
}
const bytes = concatByteChunks(transfer.chunks, transfer.size);
this.activeBinaryFileTransfers.delete(frame.requestId);
this.completedBinaryFileReads.set(frame.requestId, {