mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 14:16:43 +00:00
fix: redact OAuth trace error messages
This commit is contained in:
@@ -306,7 +306,11 @@ describe('handlePasteCallbackMode traceability', () => {
|
||||
},
|
||||
{
|
||||
url: /\/v0\/management\/oauth-callback$/,
|
||||
response: { status: 'error', error: 'invalid_grant: expired code' },
|
||||
response: {
|
||||
status: 'error',
|
||||
error:
|
||||
'invalid_grant: bad redirect http://localhost:1455/callback?code=oauth-code-secret&state=upstream-state-secret',
|
||||
},
|
||||
status: 400,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -120,9 +120,29 @@ describe('createOAuthTraceRecorder', () => {
|
||||
expect(snap[0].error).toEqual({ code: 'E1', message: 'boom' });
|
||||
});
|
||||
|
||||
test('Error instance accepted', () => {
|
||||
test('redacts OAuth secrets from error messages before they reach sinks', () => {
|
||||
const lines: string[] = [];
|
||||
const { rec } = makeRecorder(true, lines);
|
||||
rec.record(OAuthTracePhase.Error, undefined, {
|
||||
code: 'CALLBACK_REJECTED',
|
||||
message:
|
||||
'bad redirect http://localhost:1455/callback?code=AUTHCODE_SECRET&state=STATE_SECRET',
|
||||
});
|
||||
|
||||
const blob = JSON.stringify(rec.snapshot()) + '\n' + lines.join('\n');
|
||||
expect(blob).not.toContain('AUTHCODE_SECRET');
|
||||
expect(blob).not.toContain('STATE_SECRET');
|
||||
expect(blob).toContain(REDACTED_PLACEHOLDER);
|
||||
});
|
||||
|
||||
test('Error instance accepted and redacted', () => {
|
||||
const { rec } = makeRecorder();
|
||||
rec.record(OAuthTracePhase.Error, undefined, new Error('plain'));
|
||||
expect(rec.snapshot()[0].error?.message).toBe('plain');
|
||||
rec.record(
|
||||
OAuthTracePhase.Error,
|
||||
undefined,
|
||||
new Error('bad redirect http://localhost:1455/callback?code=AUTHCODE_SECRET')
|
||||
);
|
||||
expect(rec.snapshot()[0].error?.message).toContain(REDACTED_PLACEHOLDER);
|
||||
expect(rec.snapshot()[0].error?.message).not.toContain('AUTHCODE_SECRET');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,6 +82,7 @@ import { generateSessionId } from './project-selection-handler';
|
||||
import { createFileSink } from './oauth-trace/sink-file';
|
||||
import { createOAuthTraceRecorder, OAuthTracePhase, type OAuthTraceRecorder } from './oauth-trace';
|
||||
import { diagnoseFailure, formatErrorMessage } from './oauth-trace/diagnose-failure';
|
||||
import { redactString } from './oauth-trace/redactor';
|
||||
|
||||
interface PasteCallbackStartData {
|
||||
url?: string;
|
||||
@@ -889,8 +890,9 @@ export async function handlePasteCallbackMode(
|
||||
if (!callbackResponse.ok || callbackData.status === 'error') {
|
||||
const callbackError =
|
||||
callbackData.error || `OAuth callback failed with status ${callbackResponse.status}`;
|
||||
console.log(fail(callbackError));
|
||||
warnPossible403Ban(provider, callbackError);
|
||||
const redactedCallbackError = redactString(callbackError);
|
||||
console.log(fail(redactedCallbackError));
|
||||
warnPossible403Ban(provider, redactedCallbackError);
|
||||
trace.record(
|
||||
OAuthTracePhase.Error,
|
||||
{ status: callbackResponse.status },
|
||||
@@ -915,8 +917,9 @@ export async function handlePasteCallbackMode(
|
||||
);
|
||||
|
||||
if (tokenWaitError) {
|
||||
console.log(fail(tokenWaitError));
|
||||
warnPossible403Ban(provider, tokenWaitError);
|
||||
const redactedTokenWaitError = redactString(tokenWaitError);
|
||||
console.log(fail(redactedTokenWaitError));
|
||||
warnPossible403Ban(provider, redactedTokenWaitError);
|
||||
trace.record(
|
||||
OAuthTracePhase.Error,
|
||||
{},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { OAuthTraceEvent, OAuthTracePhase, OAuthTraceSink } from './trace-events';
|
||||
import { createMemorySink } from './sink-memory';
|
||||
import { createVerboseStdoutSink } from './sink-verbose-stdout';
|
||||
import { redactJsonShallow } from './redactor';
|
||||
import { redactJsonShallow, redactString } from './redactor';
|
||||
|
||||
export interface OAuthTraceRecorder {
|
||||
record(
|
||||
@@ -53,9 +53,9 @@ export function createOAuthTraceRecorder(options: OAuthTraceRecorderOptions): OA
|
||||
): { code?: string; message: string } | undefined {
|
||||
if (!err) return undefined;
|
||||
if (err instanceof Error) {
|
||||
return { message: err.message };
|
||||
return { message: redactString(err.message) };
|
||||
}
|
||||
return { code: err.code, message: err.message };
|
||||
return { code: err.code, message: redactString(err.message) };
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user