From fe329ffff0c6f8216dc45bb18fbf973812de40cd Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 29 May 2026 17:13:39 +0200 Subject: [PATCH] fix(scripts): cap clawtributor avatar probes --- scripts/update-clawtributors.ts | 40 ++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/scripts/update-clawtributors.ts b/scripts/update-clawtributors.ts index fff49f98074..1314346394e 100644 --- a/scripts/update-clawtributors.ts +++ b/scripts/update-clawtributors.ts @@ -6,6 +6,7 @@ import type { ApiContributor, Entry, MapConfig, User } from "./update-clawtribut const REPO = "openclaw/openclaw"; const PER_LINE = 10; const AVATAR_PROBE_SIZE = 40; +const AVATAR_PROBE_MAX_BYTES = 256 * 1024; const AVATAR_SIZE = 48; const CLAWTRIBUTORS_START = ""; const CLAWTRIBUTORS_END = ""; @@ -459,7 +460,7 @@ async function probeDefaultGitHubAvatar(login: string): Promise { if (!response.ok) { return false; } - const buffer = Buffer.from(await response.arrayBuffer()); + const buffer = await readAvatarProbeBuffer(response); const dimensions = readImageDimensions(buffer); return Boolean( dimensions && (dimensions.width > AVATAR_PROBE_SIZE || dimensions.height > AVATAR_PROBE_SIZE), @@ -469,6 +470,43 @@ async function probeDefaultGitHubAvatar(login: string): Promise { } } +async function readAvatarProbeBuffer(response: Response): Promise { + const contentLength = Number(response.headers.get("content-length") ?? 0); + if (Number.isFinite(contentLength) && contentLength > AVATAR_PROBE_MAX_BYTES) { + throw new Error(`avatar probe exceeded ${AVATAR_PROBE_MAX_BYTES} bytes`); + } + + const reader = response.body?.getReader?.(); + if (!reader) { + const buffer = Buffer.from(await response.arrayBuffer()); + if (buffer.byteLength > AVATAR_PROBE_MAX_BYTES) { + throw new Error(`avatar probe exceeded ${AVATAR_PROBE_MAX_BYTES} bytes`); + } + return buffer; + } + + const chunks: Buffer[] = []; + let total = 0; + for (;;) { + const { done, value } = await reader.read(); + if (done) { + break; + } + if (!value?.byteLength) { + continue; + } + const chunk = Buffer.from(value); + const nextTotal = total + chunk.byteLength; + if (nextTotal > AVATAR_PROBE_MAX_BYTES) { + await reader.cancel().catch(() => undefined); + throw new Error(`avatar probe exceeded ${AVATAR_PROBE_MAX_BYTES} bytes`); + } + chunks.push(chunk); + total = nextTotal; + } + return Buffer.concat(chunks, total); +} + async function filterVisibleEntries( entries: Entry[], hiddenLogins: ReadonlySet,