fix(security): sanitize QQBot debug log values

Sanitizes QQBot debug log values to remediate CodeQL alert 230.
This commit is contained in:
Vincent Koc
2026-04-30 00:37:05 -07:00
committed by GitHub
parent 13e917e292
commit 2d748e4ac1
3 changed files with 61 additions and 3 deletions

View File

@@ -0,0 +1,28 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { debugLog, sanitizeDebugLogValue } from "./log.js";
const originalDebug = process.env.QQBOT_DEBUG;
afterEach(() => {
if (originalDebug === undefined) {
delete process.env.QQBOT_DEBUG;
} else {
process.env.QQBOT_DEBUG = originalDebug;
}
vi.restoreAllMocks();
});
describe("QQBot debug logging", () => {
it("neutralizes control characters in log values", () => {
expect(sanitizeDebugLogValue("before\nforged\r\tentry")).toBe("before forged entry");
});
it("sanitizes arguments before debug console output", () => {
process.env.QQBOT_DEBUG = "1";
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
debugLog("prefix", "line one\nline two");
expect(logSpy).toHaveBeenCalledWith("prefix", "line one line two");
});
});

View File

@@ -9,24 +9,53 @@
*/
const isDebug = () => !!process.env.QQBOT_DEBUG;
const MAX_LOG_VALUE_CHARS = 4096;
export function sanitizeDebugLogValue(value: unknown): string {
let text: string;
if (typeof value === "string") {
text = value;
} else if (value instanceof Error) {
text = value.stack || value.message;
} else {
try {
text = JSON.stringify(value) ?? String(value);
} catch {
text = String(value);
}
}
const sanitized = text
.replace(/\p{Cc}/gu, " ")
.replace(/\s+/g, " ")
.trim();
if (sanitized.length <= MAX_LOG_VALUE_CHARS) {
return sanitized;
}
return `${sanitized.slice(0, MAX_LOG_VALUE_CHARS)}...`;
}
function sanitizeDebugLogArgs(args: unknown[]): string[] {
return args.map(sanitizeDebugLogValue);
}
/** Debug-level log; only outputs when QQBOT_DEBUG is enabled. */
export function debugLog(...args: unknown[]): void {
if (isDebug()) {
console.log(...args);
console.log(...sanitizeDebugLogArgs(args));
}
}
/** Debug-level warning; only outputs when QQBOT_DEBUG is enabled. */
export function debugWarn(...args: unknown[]): void {
if (isDebug()) {
console.warn(...args);
console.warn(...sanitizeDebugLogArgs(args));
}
}
/** Debug-level error; only outputs when QQBOT_DEBUG is enabled. */
export function debugError(...args: unknown[]): void {
if (isDebug()) {
console.error(...args);
console.error(...sanitizeDebugLogArgs(args));
}
}