Skip to content

Commit 620ecd5

Browse files
minhanh-phanstainless-app[bot]
authored andcommitted
fix(logs/azure): redact sensitive header when DEBUG is set (#1218)
1 parent 55f084d commit 620ecd5

File tree

2 files changed

+128
-2
lines changed

2 files changed

+128
-2
lines changed

Diff for: src/core.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -1148,9 +1148,43 @@ function applyHeadersMut(targetHeaders: Headers, newHeaders: Headers): void {
11481148
}
11491149
}
11501150

1151+
const SENSITIVE_HEADERS = new Set(['authorization', 'api-key']);
1152+
11511153
export function debug(action: string, ...args: any[]) {
11521154
if (typeof process !== 'undefined' && process?.env?.['DEBUG'] === 'true') {
1153-
console.log(`OpenAI:DEBUG:${action}`, ...args);
1155+
const modifiedArgs = args.map((arg) => {
1156+
if (!arg) {
1157+
return arg;
1158+
}
1159+
1160+
// Check for sensitive headers in request body 'headers' object
1161+
if (arg['headers']) {
1162+
// clone so we don't mutate
1163+
const modifiedArg = { ...arg, headers: { ...arg['headers'] } };
1164+
1165+
for (const header in arg['headers']) {
1166+
if (SENSITIVE_HEADERS.has(header.toLowerCase())) {
1167+
modifiedArg['headers'][header] = 'REDACTED';
1168+
}
1169+
}
1170+
1171+
return modifiedArg;
1172+
}
1173+
1174+
let modifiedArg = null;
1175+
1176+
// Check for sensitive headers in headers object
1177+
for (const header in arg) {
1178+
if (SENSITIVE_HEADERS.has(header.toLowerCase())) {
1179+
// avoid making a copy until we need to
1180+
modifiedArg ??= { ...arg };
1181+
modifiedArg[header] = 'REDACTED';
1182+
}
1183+
}
1184+
1185+
return modifiedArg ?? arg;
1186+
});
1187+
console.log(`OpenAI:DEBUG:${action}`, ...modifiedArgs);
11541188
}
11551189
}
11561190

Diff for: tests/index.test.ts

+93-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import OpenAI from 'openai';
44
import { APIUserAbortError } from 'openai';
5-
import { Headers } from 'openai/core';
5+
import { debug, Headers } from 'openai/core';
66
import defaultFetch, { Response, type RequestInit, type RequestInfo } from 'node-fetch';
77

88
describe('instantiate client', () => {
@@ -424,3 +424,95 @@ describe('retries', () => {
424424
expect(count).toEqual(3);
425425
});
426426
});
427+
428+
describe('debug()', () => {
429+
const env = process.env;
430+
const spy = jest.spyOn(console, 'log');
431+
432+
beforeEach(() => {
433+
jest.resetModules();
434+
process.env = { ...env };
435+
process.env['DEBUG'] = 'true';
436+
});
437+
438+
afterEach(() => {
439+
process.env = env;
440+
});
441+
442+
test('body request object with Authorization header', function () {
443+
// Test request body includes headers object with Authorization
444+
const headersTest = {
445+
headers: {
446+
Authorization: 'fakeAuthorization',
447+
},
448+
};
449+
debug('request', headersTest);
450+
expect(spy).toHaveBeenCalledWith('OpenAI:DEBUG:request', {
451+
headers: {
452+
Authorization: 'REDACTED',
453+
},
454+
});
455+
});
456+
457+
test('body request object with api-key header', function () {
458+
// Test request body includes headers object with api-ley
459+
const apiKeyTest = {
460+
headers: {
461+
'api-key': 'fakeKey',
462+
},
463+
};
464+
debug('request', apiKeyTest);
465+
expect(spy).toHaveBeenCalledWith('OpenAI:DEBUG:request', {
466+
headers: {
467+
'api-key': 'REDACTED',
468+
},
469+
});
470+
});
471+
472+
test('header object with Authorization header', function () {
473+
// Test headers object with authorization header
474+
const authorizationTest = {
475+
authorization: 'fakeValue',
476+
};
477+
debug('request', authorizationTest);
478+
expect(spy).toHaveBeenCalledWith('OpenAI:DEBUG:request', {
479+
authorization: 'REDACTED',
480+
});
481+
});
482+
483+
test('input args are not mutated', function () {
484+
const authorizationTest = {
485+
authorization: 'fakeValue',
486+
};
487+
const client = new OpenAI({
488+
baseURL: 'http://localhost:5000/',
489+
defaultHeaders: authorizationTest,
490+
apiKey: 'api-key',
491+
});
492+
493+
const { req } = client.buildRequest({ path: '/foo', method: 'post' });
494+
debug('request', authorizationTest);
495+
expect((req.headers as Headers)['authorization']).toEqual('fakeValue');
496+
expect(spy).toHaveBeenCalledWith('OpenAI:DEBUG:request', {
497+
authorization: 'REDACTED',
498+
});
499+
});
500+
501+
test('input headers are not mutated', function () {
502+
const authorizationTest = {
503+
authorization: 'fakeValue',
504+
};
505+
const client = new OpenAI({
506+
baseURL: 'http://localhost:5000/',
507+
defaultHeaders: authorizationTest,
508+
apiKey: 'api-key',
509+
});
510+
511+
const { req } = client.buildRequest({ path: '/foo', method: 'post' });
512+
debug('request', { headers: req.headers });
513+
expect((req.headers as Headers)['authorization']).toEqual('fakeValue');
514+
expect(spy).toHaveBeenCalledWith('OpenAI:DEBUG:request', {
515+
authorization: 'REDACTED',
516+
});
517+
});
518+
});

0 commit comments

Comments
 (0)