fix(security): sanitize QQBot debug log values · openclaw/openclaw@2d748e4
vincentkoc
·
2026-04-30
·
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 | |
43 | 43 | - Security/outbound: strip re-formed HTML tags during plain-text sanitization so nested tag fragments cannot leave a CodeQL-detected `<script>` sequence behind. Thanks @vincentkoc. |
44 | 44 | - Security/secrets: compare credential bytes with padded timing-safe buffers instead of hashing candidate passwords before equality checks. Thanks @vincentkoc. |
| 45 | +- Security/QQBot: sanitize debug log arguments before writing to `console.*`, so gateway payload fields cannot forge extra log lines when debug logging is enabled. Thanks @vincentkoc. |
45 | 46 | - CLI/agents/status: keep `openclaw agents`, text `agents list`, and plain text `status` on read-only metadata paths so human output no longer preloads plugin runtimes or live channel scans before printing. Fixes #74195. Thanks @NianJiuZst. |
46 | 47 | - Agents/local models: derive context-window guard thresholds from the effective model window with 4k/8k safety floors, so small local models are no longer rejected by fixed 16k/32k preflight cutoffs. Fixes #42999. Thanks @chengjialu8888. |
47 | 48 | - Media: treat legacy Word/OLE attachments with `application/msword` or `application/x-cfb` MIME as binary so printable-looking `.doc` files are not embedded into prompts as text. Fixes #54176; carries forward #54380. Thanks @andyliu. |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { afterEach, describe, expect, it, vi } from "vitest"; |
| 2 | +import { debugLog, sanitizeDebugLogValue } from "./log.js"; |
| 3 | + |
| 4 | +const originalDebug = process.env.QQBOT_DEBUG; |
| 5 | + |
| 6 | +afterEach(() => { |
| 7 | +if (originalDebug === undefined) { |
| 8 | +delete process.env.QQBOT_DEBUG; |
| 9 | +} else { |
| 10 | +process.env.QQBOT_DEBUG = originalDebug; |
| 11 | +} |
| 12 | +vi.restoreAllMocks(); |
| 13 | +}); |
| 14 | + |
| 15 | +describe("QQBot debug logging", () => { |
| 16 | +it("neutralizes control characters in log values", () => { |
| 17 | +expect(sanitizeDebugLogValue("before\nforged\r\tentry")).toBe("before forged entry"); |
| 18 | +}); |
| 19 | + |
| 20 | +it("sanitizes arguments before debug console output", () => { |
| 21 | +process.env.QQBOT_DEBUG = "1"; |
| 22 | +const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); |
| 23 | + |
| 24 | +debugLog("prefix", "line one\nline two"); |
| 25 | + |
| 26 | +expect(logSpy).toHaveBeenCalledWith("prefix", "line one line two"); |
| 27 | +}); |
| 28 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -9,24 +9,53 @@
|
9 | 9 | */ |
10 | 10 | |
11 | 11 | const isDebug = () => !!process.env.QQBOT_DEBUG; |
| 12 | +const MAX_LOG_VALUE_CHARS = 4096; |
| 13 | + |
| 14 | +export function sanitizeDebugLogValue(value: unknown): string { |
| 15 | +let text: string; |
| 16 | +if (typeof value === "string") { |
| 17 | +text = value; |
| 18 | +} else if (value instanceof Error) { |
| 19 | +text = value.stack || value.message; |
| 20 | +} else { |
| 21 | +try { |
| 22 | +text = JSON.stringify(value) ?? String(value); |
| 23 | +} catch { |
| 24 | +text = String(value); |
| 25 | +} |
| 26 | +} |
| 27 | + |
| 28 | +const sanitized = text |
| 29 | +.replace(/\p{Cc}/gu, " ") |
| 30 | +.replace(/\s+/g, " ") |
| 31 | +.trim(); |
| 32 | +if (sanitized.length <= MAX_LOG_VALUE_CHARS) { |
| 33 | +return sanitized; |
| 34 | +} |
| 35 | +return `${sanitized.slice(0, MAX_LOG_VALUE_CHARS)}...`; |
| 36 | +} |
| 37 | + |
| 38 | +function sanitizeDebugLogArgs(args: unknown[]): string[] { |
| 39 | +return args.map(sanitizeDebugLogValue); |
| 40 | +} |
12 | 41 | |
13 | 42 | /** Debug-level log; only outputs when QQBOT_DEBUG is enabled. */ |
14 | 43 | export function debugLog(...args: unknown[]): void { |
15 | 44 | if (isDebug()) { |
16 | | -console.log(...args); |
| 45 | +console.log(...sanitizeDebugLogArgs(args)); |
17 | 46 | } |
18 | 47 | } |
19 | 48 | |
20 | 49 | /** Debug-level warning; only outputs when QQBOT_DEBUG is enabled. */ |
21 | 50 | export function debugWarn(...args: unknown[]): void { |
22 | 51 | if (isDebug()) { |
23 | | -console.warn(...args); |
| 52 | +console.warn(...sanitizeDebugLogArgs(args)); |
24 | 53 | } |
25 | 54 | } |
26 | 55 | |
27 | 56 | /** Debug-level error; only outputs when QQBOT_DEBUG is enabled. */ |
28 | 57 | export function debugError(...args: unknown[]): void { |
29 | 58 | if (isDebug()) { |
30 | | -console.error(...args); |
| 59 | +console.error(...sanitizeDebugLogArgs(args)); |
31 | 60 | } |
32 | 61 | } |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。