Files
ccs/tests/unit/services/logging/log-redaction-extended.test.ts
Kai (Tam Nhu) Tran 138ead0e1e fix(logging): redact browser debug endpoint (#1622)
* fix(logging): redact browser debug endpoint

* fix(logging): redact prompt argv values
2026-06-30 13:07:46 -04:00

160 lines
4.5 KiB
TypeScript

import { describe, expect, it } from 'bun:test';
import {
redactArgv,
redactContext,
} from '../../../../src/services/logging/log-redaction';
describe('log redaction (extended sensitive keys)', () => {
const newSensitiveKeys = [
'refresh_token',
'id_token',
'access_token',
'client_secret',
'bearer',
'assertion',
'copilot_token',
'copilot-token',
'cursor_session_key',
'cursor-session-key',
'x-api-key',
'x_goog_api_key',
'proxy-authorization',
'oauth_code',
'auth_code',
'auth_token',
];
it.each(newSensitiveKeys)('redacts %s key (top-level)', (key) => {
const redacted = redactContext({ [key]: 'secret-value', safe: 'ok' });
expect(redacted[key]).toBe('[redacted]');
expect(redacted.safe).toBe('ok');
});
it('redacts new keys nested under headers', () => {
const redacted = redactContext({
headers: {
'x-api-key': 'sk-xxx',
'proxy-authorization': 'Bearer abc',
'copilot-token': 'gho_xxx',
},
});
expect(redacted).toEqual({
headers: {
'x-api-key': '[redacted]',
'proxy-authorization': '[redacted]',
'copilot-token': '[redacted]',
},
});
});
it('redacts Bearer/Basic/Token scheme prefix in raw string values', () => {
const redacted = redactContext({
raw: 'Bearer eyJhbGciOiJIUzI1NiJ9.payload.sig',
basic: 'Basic dXNlcjpwYXNz',
tokenLine: 'Token abc.def',
plain: 'no-scheme-here',
});
expect(redacted.raw).toBe('Bearer [redacted]');
expect(redacted.basic).toBe('Basic [redacted]');
expect(redacted.tokenLine).toBe('Token [redacted]');
expect(redacted.plain).toBe('no-scheme-here');
});
it('passes numeric/boolean values through even if key matches', () => {
// expires_at is not a sensitive key; ensure non-string sensitive values
// would also pass through if pattern matched (defense check).
const redacted = redactContext({
access_token: 'real-secret',
expires_at: 1234567890,
enabled: true,
});
expect(redacted.access_token).toBe('[redacted]');
expect(redacted.expires_at).toBe(1234567890);
expect(redacted.enabled).toBe(true);
});
it('recurses through arrays of objects', () => {
const redacted = redactContext({
tokens: [
{ access_token: 'a' },
{ refresh_token: 'b' },
{ id_token: 'c', meta: 'kept' },
],
});
expect(redacted).toEqual({
tokens: [
{ access_token: '[redacted]' },
{ refresh_token: '[redacted]' },
{ id_token: '[redacted]', meta: 'kept' },
],
});
});
it('fuzz-style: many random token-shaped keys all redact', () => {
const variants = ['token', 'auth_token', 'refresh-token', 'id-token', 'X-API-KEY'];
for (const v of variants) {
const out = redactContext({ [v]: 'secret' });
expect(out[v]).toBe('[redacted]');
}
});
});
describe('redactArgv', () => {
it('redacts the value following sensitive flags', () => {
expect(redactArgv(['--api-key', 'secret', '--other', 'ok'])).toEqual([
'--api-key',
'[redacted]',
'--other',
'ok',
]);
});
it('redacts multiple sensitive flags', () => {
expect(
redactArgv(['--token', 'a', '--secret', 'b', '--bearer', 'c', '--keep', 'd'])
).toEqual(['--token', '[redacted]', '--secret', '[redacted]', '--bearer', '[redacted]', '--keep', 'd']);
});
it('passes argv unchanged when no sensitive flags present', () => {
expect(redactArgv(['build', '--watch', '--out', 'dist'])).toEqual([
'build',
'--watch',
'--out',
'dist',
]);
});
it('handles trailing sensitive flag with no value', () => {
expect(redactArgv(['--api-key'])).toEqual(['--api-key']);
});
it('redacts kebab and snake case flag variants', () => {
expect(redactArgv(['--api_key', 'x', '--auth-token', 'y'])).toEqual([
'--api_key',
'[redacted]',
'--auth-token',
'[redacted]',
]);
});
it('redacts prompt values passed with -p and --prompt', () => {
expect(redactArgv(['glm', '-p', 'summarize secret account notes'])).toEqual([
'glm',
'-p',
'[redacted]',
]);
expect(redactArgv(['glm', '--prompt', 'summarize secret account notes'])).toEqual([
'glm',
'--prompt',
'[redacted]',
]);
});
it('redacts inline prompt and sensitive flag assignments', () => {
expect(
redactArgv(['glm', '--prompt=summarize secret account notes', '--api-key=plainsecret'])
).toEqual(['glm', '--prompt=[redacted]', '--api-key=[redacted]']);
});
});