mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-21 01:22:54 +00:00
🔐 fix: Redact Before Colorize To Survive ANSI Word-Boundary Interference
The console pipeline runs `redactFormat → colorize({ all: true }) → printf`.
With `all: true`, winston wraps `info.message` in ANSI escapes whose
trailing `m` is a word character. That means `\b(Bearer )…` placed at the
start of a colorized segment can fall on a (word,word) boundary and miss —
the earlier line-wise `redactMessage(line)` pass in printf suffers the
same issue because it runs after colorize.
Extend `redactFormat` to run for `warn` in addition to `error`, operating
on the raw pre-colorize `info.message` + `Symbol.for('message')` strings.
The later in-printf `redactMessage(line)` stays as a backstop, but the
primary redaction now happens where the regex can actually see the text.
Metadata redaction already operates on the raw info object via
`formatConsoleMeta`, so it was never affected by ANSI — no change there.
Includes regression tests for the new warn-level behavior and for the
info/debug no-op path.
Reviewed-by: Codex (P2 finding on PR #12737, commit fdb6b361).
This commit is contained in:
parent
fdb6b36179
commit
1e43d63687
2 changed files with 35 additions and 5 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
jest.unmock('winston');
|
jest.unmock('winston');
|
||||||
|
|
||||||
const { formatConsoleMeta, redactMessage, debugTraverse } = jest.requireActual('../parsers');
|
const { formatConsoleMeta, redactMessage, redactFormat, debugTraverse } =
|
||||||
|
jest.requireActual('../parsers');
|
||||||
const SPLAT_SYMBOL = Symbol.for('splat');
|
const SPLAT_SYMBOL = Symbol.for('splat');
|
||||||
|
|
||||||
describe('formatConsoleMeta', () => {
|
describe('formatConsoleMeta', () => {
|
||||||
|
|
@ -153,6 +154,28 @@ describe('redactMessage', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('redactFormat', () => {
|
||||||
|
const runFormat = (info) => redactFormat().transform(info) || info;
|
||||||
|
|
||||||
|
it('redacts info.message for error level before any colorize step runs', () => {
|
||||||
|
const info = runFormat({ level: 'error', message: 'Bearer secretvalue' });
|
||||||
|
expect(info.message).toBe('Bearer [REDACTED]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('redacts info.message for warn level too (avoids ANSI boundary issues later)', () => {
|
||||||
|
const info = runFormat({ level: 'warn', message: 'apiKey=sk-abc123def' });
|
||||||
|
expect(info.message).toContain('sk-[REDACTED]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves info.message untouched for info and debug levels', () => {
|
||||||
|
const infoInfo = runFormat({ level: 'info', message: 'Bearer looksSensitive' });
|
||||||
|
expect(infoInfo.message).toBe('Bearer looksSensitive');
|
||||||
|
|
||||||
|
const infoDebug = runFormat({ level: 'debug', message: 'Bearer looksSensitive' });
|
||||||
|
expect(infoDebug.message).toBe('Bearer looksSensitive');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('debugTraverse', () => {
|
describe('debugTraverse', () => {
|
||||||
const runFormatter = (info) => {
|
const runFormatter = (info) => {
|
||||||
const transformed = debugTraverse.transform(info);
|
const transformed = debugTraverse.transform(info);
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,22 @@ function redactMessage(str, trimLength) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redacts sensitive information from log messages if the log level is 'error'.
|
* Redacts sensitive information from log messages when the log level is
|
||||||
|
* `error` or `warn`. Runs on the raw `info.message` before any colorize /
|
||||||
|
* splat transforms so the sensitive-token regexes don't have to contend
|
||||||
|
* with ANSI escape sequences (whose trailing `m` would otherwise defeat
|
||||||
|
* `\b` anchors).
|
||||||
|
*
|
||||||
* Note: Intentionally mutates the object.
|
* Note: Intentionally mutates the object.
|
||||||
* @param {Object} info - The log information object.
|
* @param {Object} info - The log information object.
|
||||||
* @returns {Object} - The modified log information object.
|
* @returns {Object} - The modified log information object.
|
||||||
*/
|
*/
|
||||||
const redactFormat = winston.format((info) => {
|
const redactFormat = winston.format((info) => {
|
||||||
if (info.level === 'error') {
|
if (info.level === 'error' || info.level === 'warn') {
|
||||||
info.message = redactMessage(info.message);
|
if (typeof info.message === 'string') {
|
||||||
if (info[MESSAGE_SYMBOL]) {
|
info.message = redactMessage(info.message);
|
||||||
|
}
|
||||||
|
if (typeof info[MESSAGE_SYMBOL] === 'string') {
|
||||||
info[MESSAGE_SYMBOL] = redactMessage(info[MESSAGE_SYMBOL]);
|
info[MESSAGE_SYMBOL] = redactMessage(info[MESSAGE_SYMBOL]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue