mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(buzz): bound relay information responses (#119182)
Reject malformed relay metadata and cancel oversized response streams at the existing 16 MiB provider boundary. Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -133,7 +133,6 @@ extensions/buzz/src/gateway.ts 1
|
||||
extensions/buzz/src/inbound.ts 1
|
||||
extensions/buzz/src/message-event.ts 2
|
||||
extensions/buzz/src/qa/adapter.runtime.ts 2
|
||||
extensions/buzz/src/relay-auth.ts 1
|
||||
extensions/buzz/src/room-membership.ts 2
|
||||
extensions/buzz/src/setup-core.ts 1
|
||||
extensions/buzz/src/setup-surface.ts 1
|
||||
|
||||
@@ -109,13 +109,12 @@ describe("Buzz archived room lifecycle", () => {
|
||||
relayMocks.roomMetadataEvents = [];
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -204,13 +204,12 @@ describe("Buzz reconnect history catch-up", () => {
|
||||
relayMocks.connected = true;
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ const BOT_PUBLIC_KEY = getPublicKey(Uint8Array.from(Buffer.from(PRIVATE_KEY, "he
|
||||
const SENDER_PUBLIC_KEY = getPublicKey(Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")));
|
||||
const SENDER_SECRET_KEY = Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex"));
|
||||
const RELAY_PUBLIC_KEY = "f".repeat(64);
|
||||
const BUZZ_RELAY_INFO_MAX_BYTES = 16 * 1024 * 1024;
|
||||
const tempDirs = new Set<string>();
|
||||
let previousStateDir: string | undefined;
|
||||
let stateDir: string;
|
||||
@@ -209,13 +210,12 @@ describe("Buzz bus lifecycle", () => {
|
||||
relayMocks.stallRoomEoseChannelId = undefined;
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -291,6 +291,58 @@ describe("Buzz bus lifecycle", () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("cancels oversized NIP-11 relay information before consuming the entire response", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
const cancel = vi.fn();
|
||||
const chunk = new Uint8Array(1024 * 1024).fill("x".charCodeAt(0));
|
||||
let emittedChunks = 0;
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
pull(controller) {
|
||||
if (emittedChunks === 0) {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(`{"self":"${RELAY_PUBLIC_KEY}","description":"`),
|
||||
);
|
||||
} else if (emittedChunks <= 17) {
|
||||
controller.enqueue(chunk);
|
||||
} else {
|
||||
controller.enqueue(new TextEncoder().encode('"}'));
|
||||
controller.close();
|
||||
}
|
||||
emittedChunks += 1;
|
||||
},
|
||||
cancel,
|
||||
});
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn<typeof fetch>(async () => new Response(body)),
|
||||
);
|
||||
|
||||
await expect(startTestBus()).rejects.toThrow(
|
||||
`Buzz relay information: JSON response exceeds ${BUZZ_RELAY_INFO_MAX_BYTES} bytes`,
|
||||
);
|
||||
|
||||
expect(cancel).toHaveBeenCalledOnce();
|
||||
expect(emittedChunks).toBeLessThan(19);
|
||||
expect(relayMocks.close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["truncated JSON", '{"self":'],
|
||||
["null", "null"],
|
||||
["an array", "[]"],
|
||||
["a primitive", "true"],
|
||||
])("rejects malformed NIP-11 relay information containing %s", async (_label, body) => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn<typeof fetch>(async () => new Response(body)),
|
||||
);
|
||||
|
||||
await expect(startTestBus()).rejects.toThrow("Buzz relay information: malformed JSON response");
|
||||
|
||||
expect(relayMocks.close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("publishes and closes a standalone authenticated send", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
|
||||
|
||||
@@ -118,13 +118,12 @@ describe("Buzz mention delivery", () => {
|
||||
relayMocks.send.mockResolvedValue();
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -88,13 +88,12 @@ describe("Buzz live directory", () => {
|
||||
gatewayMocks.activeBus = undefined;
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
relayMocks.subscribe.mockImplementation(
|
||||
(
|
||||
|
||||
@@ -89,13 +89,12 @@ describe("Buzz QA relay driver", () => {
|
||||
relayMocks.replayedMessage = undefined;
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type EventTemplate, finalizeEvent, Relay, type VerifiedEvent } from "nostr-tools";
|
||||
import { readProviderJsonObjectResponse } from "openclaw/plugin-sdk/provider-http";
|
||||
import {
|
||||
fetchWithSsrFGuard,
|
||||
ssrfPolicyFromHttpBaseUrlAllowedOrigin,
|
||||
@@ -91,10 +92,7 @@ async function resolveBuzzRelayPublicKey(params: {
|
||||
await response.body?.cancel().catch(() => undefined);
|
||||
throw new Error(`Buzz relay information request failed with HTTP ${response.status}`);
|
||||
}
|
||||
const document = (await response.json()) as {
|
||||
self?: unknown;
|
||||
software?: unknown;
|
||||
};
|
||||
const document = await readProviderJsonObjectResponse(response, "Buzz relay information");
|
||||
const relayPublicKey =
|
||||
typeof document.self === "string" ? document.self.trim().toLowerCase() : "";
|
||||
if (HEX_PUBLIC_KEY_PATTERN.test(relayPublicKey)) {
|
||||
|
||||
@@ -68,13 +68,12 @@ describe("discoverBuzzRooms", () => {
|
||||
relayMocks.subscribe.mockReset();
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
vi.fn(async () =>
|
||||
Response.json({
|
||||
self: RELAY_PUBLIC_KEY,
|
||||
software: "https://github.com/block/buzz",
|
||||
}),
|
||||
})),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user