fix(qqbot): validate cron payload base64 · openclaw/openclaw@c822824
vincentkoc
·
2026-05-14
·
via Recent Commits to openclaw:main
File tree
extensions/qqbot/src/engine/utils
| Original file line number | Diff line number | Diff line change |
|---|
@@ -42,6 +42,7 @@ Docs: https://docs.openclaw.ai
|
42 | 42 | - QA channel: skip malformed inline inbound attachment base64 instead of staging silently corrupted media for agent turns. |
43 | 43 | - Microsoft Teams: reject malformed inline HTML image base64 padding instead of decoding corrupted `data:` image attachments. |
44 | 44 | - Voice-call realtime: ignore malformed provider media-frame base64 before forwarding audio into bridge and transcription paths. |
| 45 | +- QQBot: reject malformed stored cron payload base64 before JSON decoding structured reminder data. |
45 | 46 | - Models config/auth: stop inferring provider env-var markers from broad `^[A-Z_][A-Z0-9_]*$` strings, and resolve config-backed provider `apiKey` values only through structured env SecretRefs (`secrets.providers[id]` / `secrets.defaults`), so unrelated env vars cannot accidentally become provider credentials. Thanks @sallyom. |
46 | 47 | - Media fetch: skip allocating and buffering the response body for bodyless media responses (HEAD probes and 204-style empty bodies), avoiding wasted heap on streams that carry no payload. Thanks @shakkernerd. |
47 | 48 | - CLI/onboarding: forward provider-specific auth flags (e.g. `--openai-api-key`) through the onboarding wizard so they reach provider auth methods via `ctx.opts`, letting `--openai-api-key "$OPENAI_API_KEY"` skip the redundant "use existing env var?" prompt in non-interactive harnesses. (#81669) Thanks @sjf. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -59,6 +59,9 @@ describe("engine/utils/payload", () => {
|
59 | 59 | it("reports cron decode errors without throwing", () => { |
60 | 60 | expect(decodeCronPayload("plain")).toEqual({ isCronPayload: false }); |
61 | 61 | expect(decodeCronPayload("QQBOT_CRON:").error).toBe("Cron payload body is empty"); |
| 62 | +expect(decodeCronPayload("QQBOT_CRON:AAA@@@").error).toBe( |
| 63 | +"Failed to decode cron payload: Cron payload body is not valid base64", |
| 64 | +); |
62 | 65 | |
63 | 66 | const wrongType = Buffer.from('{"type":"media"}', "utf-8").toString("base64"); |
64 | 67 | expect(decodeCronPayload(`QQBOT_CRON:${wrongType}`).error).toBe( |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -44,6 +44,18 @@ function formatErr(e: unknown): string {
|
44 | 44 | return e instanceof Error ? e.message : String(e); |
45 | 45 | } |
46 | 46 | |
| 47 | +function normalizeBase64ForCompare(value: string): string { |
| 48 | +return value.replace(/=+$/u, "").replace(/-/gu, "+").replace(/_/gu, "/"); |
| 49 | +} |
| 50 | + |
| 51 | +function decodeStrictBase64Utf8(value: string): string { |
| 52 | +const buffer = Buffer.from(value, "base64"); |
| 53 | +if (normalizeBase64ForCompare(buffer.toString("base64")) !== normalizeBase64ForCompare(value)) { |
| 54 | +throw new Error("Cron payload body is not valid base64"); |
| 55 | +} |
| 56 | +return buffer.toString("utf-8"); |
| 57 | +} |
| 58 | + |
47 | 59 | /** Parse model output that may start with the QQ Bot structured payload prefix. */ |
48 | 60 | export function parseQQBotPayload(text: string): ParseResult { |
49 | 61 | const trimmedText = text.trim(); |
@@ -114,7 +126,7 @@ export function decodeCronPayload(message: string): {
|
114 | 126 | } |
115 | 127 | |
116 | 128 | try { |
117 | | -const jsonString = Buffer.from(base64Content, "base64").toString("utf-8"); |
| 129 | +const jsonString = decodeStrictBase64Utf8(base64Content); |
118 | 130 | const payload = JSON.parse(jsonString) as CronReminderPayload; |
119 | 131 | |
120 | 132 | if (payload.type !== "cron_reminder") { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。