fix: reject malformed tlon sse event ids

This commit is contained in:
Peter Steinberger
2026-05-28 15:50:36 -04:00
parent b84078a975
commit 8b180fe829
2 changed files with 26 additions and 1 deletions

View File

@@ -182,6 +182,22 @@ describe("UrbitSSEClient", () => {
);
});
it("ignores malformed event ids when deciding whether to ack", async () => {
const mockUrbitFetch = vi.mocked(urbitFetch);
mockUrbitFetch.mockResolvedValue({
response: { ok: true, status: 200 } as unknown as Response,
finalUrl: "https://example.com",
release: vi.fn().mockResolvedValue(undefined),
});
const client = new UrbitSSEClient("https://example.com", "urbauth-~zod=123");
client.processEvent('id: 25abc\ndata: {"json":{"ok":true}}');
await Promise.resolve();
expect(mockUrbitFetch).not.toHaveBeenCalled();
expect((client as unknown as { lastHeardEventId: number }).lastHeardEventId).toBe(-1);
});
it("tracks lastHeardEventId and ackThreshold", () => {
const client = new UrbitSSEClient("https://example.com", "urbauth-~zod=123");

View File

@@ -31,6 +31,15 @@ function parseUrbitSsePayload(data: string): { id?: number; json?: unknown; resp
}
}
function parseUrbitSseEventId(value: string): number | null {
const trimmed = value.trim();
if (!/^\d+$/.test(trimmed)) {
return null;
}
const parsed = Number(trimmed);
return Number.isSafeInteger(parsed) ? parsed : null;
}
export class UrbitSSEClient {
url: string;
cookie: string;
@@ -257,7 +266,7 @@ export class UrbitSSEClient {
for (const line of lines) {
if (line.startsWith("id: ")) {
eventId = Number.parseInt(line.slice(4), 10);
eventId = parseUrbitSseEventId(line.slice(4));
}
if (line.startsWith("data: ")) {
data = line.slice(6);