mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:30:42 +00:00
fix(logging): redact secrets at subsystem console sink (#73284)
createSubsystemLogger writes through writeConsoleLine, which intentionally bypasses the patched console.* capture handler in src/logging/console.ts to avoid recursion. That bypass also skipped the sink-boundary redactSensitiveText() gate, so secrets reaching subsystem loggers as message strings or formatted meta could appear verbatim on the terminal — a follow-up to the file-transport redaction landed in #67953, tracked under #64046. Apply redactSensitiveText() at the writeConsoleLine() exit, immediately after the existing Windows surrogate sanitization and before dispatching to the rawConsole sink. This covers all subsystem console paths (trace/debug/info/warn/error/fatal and .raw) because they share the same writeConsoleLine() exit, matching the redact-at-sink-boundary pattern already used in console.ts and the file transport. Closes #73284
This commit is contained in:
committed by
Peter Steinberger
parent
3c636208b0
commit
f2df49ab4b
@@ -24,6 +24,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Feishu/inbound files: recover CJK filenames from plain `Content-Disposition: filename=` download headers when Feishu exposes UTF-8 bytes through Latin-1 header decoding, while leaving valid Latin-1 and JSON-derived names unchanged. (#48578, #50435, #59431) Thanks @alex-xuweilong, @lishuaigit, and @DoChaoing.
|
||||
- Channels/Telegram: normalize accidental full `/bot<TOKEN>` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris.
|
||||
- Zalo Personal: persist refreshed `zca-js` session cookies after QR login, session restore, and successful API calls so gateway restarts restore the freshest local session. (#73277) Thanks @darkamenosa.
|
||||
- Logging/security: redact sensitive tokens (sk-\* keys, Bearer/Authorization values, etc.) at the subsystem console sink so `createSubsystemLogger().info/warn/error` output that bypasses the patched console-capture handler still applies the same redaction the file transport already does. Fixes #73284; refs #67953 and #64046. Thanks @edwin-rivera-dev.
|
||||
|
||||
## 2026.4.27
|
||||
|
||||
|
||||
@@ -205,6 +205,34 @@ describe("createSubsystemLogger().isEnabled", () => {
|
||||
expect(warn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("redacts sensitive tokens at the console sink so subsystem writes do not leak secrets (#73284)", () => {
|
||||
setLoggerOverride({ level: "silent", consoleLevel: "warn" });
|
||||
const warn = installConsoleMethodSpy("warn");
|
||||
const log = createSubsystemLogger("gateway");
|
||||
const secret = "sk-supersecretvaluefortest12345";
|
||||
|
||||
log.warn(`token=${secret}`);
|
||||
|
||||
expect(warn).toHaveBeenCalledTimes(1);
|
||||
const written = String(warn.mock.calls[0]?.[0] ?? "");
|
||||
expect(written).not.toContain(secret);
|
||||
expect(written).toMatch(/sk-sup…2345|\*\*\*/);
|
||||
});
|
||||
|
||||
it("redacts Bearer tokens on subsystem error console writes", () => {
|
||||
setLoggerOverride({ level: "silent", consoleLevel: "error" });
|
||||
const error = installConsoleMethodSpy("error");
|
||||
const log = createSubsystemLogger("gateway").child("auth");
|
||||
const bearer = "Bearer abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
log.error(`Authorization failed: ${bearer}`);
|
||||
|
||||
expect(error).toHaveBeenCalledTimes(1);
|
||||
const written = String(error.mock.calls[0]?.[0] ?? "");
|
||||
expect(written).not.toContain("abcdefghijklmnopqrstuvwxyz");
|
||||
expect(written).toContain("Bearer ");
|
||||
});
|
||||
|
||||
it("keeps long-lived subsystem loggers on the current-day rolling file", () => {
|
||||
const logDir = path.dirname(logPathTracker.nextPath());
|
||||
const firstDay = path.join(logDir, "openclaw-2026-01-01.log");
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "./console.js";
|
||||
import { type LogLevel, levelToMinLevel } from "./levels.js";
|
||||
import { getChildLogger, isFileLogLevelEnabled } from "./logger.js";
|
||||
import { redactSensitiveText } from "./redact.js";
|
||||
import { loggingState } from "./state.js";
|
||||
|
||||
type LogObj = { date?: Date } & Record<string, unknown>;
|
||||
@@ -255,13 +256,19 @@ function writeConsoleLine(level: LogLevel, line: string) {
|
||||
process.platform === "win32" && process.env.GITHUB_ACTIONS === "true"
|
||||
? line.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "?").replace(/[\uD800-\uDFFF]/g, "?")
|
||||
: line;
|
||||
// Subsystem console output bypasses the patched console.* capture handler in
|
||||
// ./console.ts to avoid recursion, so the sink-boundary redaction applied
|
||||
// there does not run for these writes (#73284). Redact at this exit instead
|
||||
// so secrets reaching subsystem loggers as message strings or formatted meta
|
||||
// do not appear verbatim on the terminal.
|
||||
const redacted = redactSensitiveText(sanitized);
|
||||
const sink = loggingState.rawConsole ?? console;
|
||||
if (loggingState.forceConsoleToStderr || level === "error" || level === "fatal") {
|
||||
(sink.error ?? console.error)(sanitized);
|
||||
(sink.error ?? console.error)(redacted);
|
||||
} else if (level === "warn") {
|
||||
(sink.warn ?? console.warn)(sanitized);
|
||||
(sink.warn ?? console.warn)(redacted);
|
||||
} else {
|
||||
(sink.log ?? console.log)(sanitized);
|
||||
(sink.log ?? console.log)(redacted);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user