fix(logging): close trace redaction gaps

This commit is contained in:
4gray committed 2026-07-18 16:20:19 +02:00
1 parent e25c8fc35f
commit 8a34bad28a
4 files changed
+36 -2

No files matched your search

@@ -27,4 +27,21 @@ describe('debug trace redaction', () => {
expect(output).toContain('diagnostic-request-id');
expect(output).toContain('get_profile');
});
it('redacts serialized credentials before truncating trace strings', () => {
jest.spyOn(console, 'log').mockImplementation(() => undefined);
const secret = 'long-trace-json-password-secret';
const diagnostic = JSON.stringify({
password: secret,
operation: 'get_profile',
padding: 'x'.repeat(300),
});
trace('portal', 'request', { diagnostic });
const output = JSON.stringify((console.log as jest.Mock).mock.calls);
expect(output).not.toContain(secret);
expect(output).toContain('[Redacted]');
expect(output).toContain('get_profile');
});
});
@@ -168,7 +168,7 @@ export function trace(scope: string, message: string, payload?: unknown): void {
console.log(
`${TRACE_PREFIX}[${scope}] ${message} ${safeStringifyForTrace(
summarizeForTrace(payload)
summarizeForTrace(redactSensitiveData(payload))
)}`
);
}
@@ -77,6 +77,17 @@ describe('redactSensitiveData', () => {
expect(output).toContain('401');
});
it('does not repeat a redacted Error message secret in its stack', () => {
const secret = 'error-stack-password-secret';
const error = new Error(`password=${secret}&operation=get_profile`);
const output = serialized(redactSensitiveData(error));
expect(output).not.toContain(secret);
expect(output).toContain(encodeURIComponent(REDACTED_VALUE));
expect(output).toContain('get_profile');
});
it('redacts credentials nested inside non-sensitive query values', () => {
const nestedUrl = `https://identity.example/callback?token=${TEST_SECRETS[3]}&step=authorize`;
const url = new URL('https://example.com/portal');
@@ -239,7 +239,13 @@ export function redactSensitiveData(
output['name'] = visitString(error.name, depth + 1);
output['message'] = visitString(error.message, depth + 1);
if (error.stack) {
output['stack'] = visitString(error.stack, depth + 1);
const [, ...stackFrames] = error.stack.split('\n');
output['stack'] = visitString(
[`${output['name']}: ${output['message']}`, ...stackFrames].join(
'\n'
),
depth + 1
);
}
if ('cause' in error) {
output['cause'] = visit(error.cause, depth + 1);