mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(api): avoid logging bearer tokens in request diagnostics (#1987)
This commit is contained in:
@@ -4,10 +4,20 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('@tauri-apps/api/app', () => ({ getVersion: vi.fn() }));
|
||||
|
||||
const configMock = vi.hoisted(() => ({ isDev: true }));
|
||||
|
||||
vi.mock('../../utils/config', () => ({
|
||||
APP_VERSION: '0.0.0-test',
|
||||
get IS_DEV() {
|
||||
return configMock.isDev;
|
||||
},
|
||||
}));
|
||||
|
||||
describe('apiClient version headers', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
configMock.isDev = true;
|
||||
vi.mocked(isTauri).mockReturnValue(false);
|
||||
vi.stubGlobal('fetch', vi.fn());
|
||||
});
|
||||
@@ -49,6 +59,54 @@ describe('apiClient version headers', () => {
|
||||
expect(headers).not.toHaveProperty('x-web-version');
|
||||
});
|
||||
|
||||
it('logs request diagnostics in dev without leaking authorization headers', async () => {
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
fetchMock.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
headers: new Headers({ 'content-type': 'application/json' }),
|
||||
json: async () => ({ success: true }),
|
||||
} as Response);
|
||||
|
||||
const { apiClient, setStoreForApiClient } = await import('../apiClient');
|
||||
setStoreForApiClient(() => 'secret-session-token');
|
||||
|
||||
await apiClient.post('/version-check', { ok: true });
|
||||
|
||||
const requestInit = fetchMock.mock.calls[0][1] as RequestInit;
|
||||
const requestHeaders = requestInit.headers as Record<string, string>;
|
||||
expect(requestHeaders.Authorization).toBe('Bearer secret-session-token');
|
||||
|
||||
const logMock = vi.mocked(console.log);
|
||||
expect(logMock).toHaveBeenCalledWith(
|
||||
'request',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
headers: expect.not.objectContaining({
|
||||
Authorization: expect.any(String),
|
||||
authorization: expect.any(String),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('does not log request diagnostics in production', async () => {
|
||||
configMock.isDev = false;
|
||||
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
fetchMock.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
headers: new Headers({ 'content-type': 'application/json' }),
|
||||
json: async () => ({ success: true }),
|
||||
} as Response);
|
||||
|
||||
const { apiClient, setStoreForApiClient } = await import('../apiClient');
|
||||
setStoreForApiClient(() => 'secret-session-token');
|
||||
|
||||
await apiClient.get('/version-check');
|
||||
|
||||
expect(console.log).not.toHaveBeenCalledWith('request', expect.anything());
|
||||
});
|
||||
|
||||
it('retries tauri version lookup after a transient failure', async () => {
|
||||
vi.mocked(isTauri).mockReturnValue(true);
|
||||
vi.mocked(getVersion)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ApiError } from '../types/api';
|
||||
import { IS_DEV } from '../utils/config';
|
||||
import { getBackendUrl } from './backendUrl';
|
||||
import { getClientVersionHeaders } from './clientVersionHeaders';
|
||||
|
||||
@@ -37,7 +38,7 @@ class ApiClient {
|
||||
/**
|
||||
* Build headers for the request
|
||||
*/
|
||||
private async buildHeaders(options: RequestOptions): Promise<HeadersInit> {
|
||||
private async buildHeaders(options: RequestOptions): Promise<Record<string, string>> {
|
||||
const versionHeaders = await getClientVersionHeaders();
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -56,6 +57,13 @@ class ApiClient {
|
||||
return headers;
|
||||
}
|
||||
|
||||
private sanitizeRequestLogHeaders(headers: HeadersInit): Record<string, string> {
|
||||
const safeHeaders = { ...(headers as Record<string, string>) };
|
||||
delete safeHeaders.Authorization;
|
||||
delete safeHeaders.authorization;
|
||||
return safeHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request
|
||||
*/
|
||||
@@ -66,7 +74,9 @@ class ApiClient {
|
||||
const url = `${baseUrl}${endpoint}`;
|
||||
const headers = await this.buildHeaders({ ...options, requireAuth });
|
||||
|
||||
console.log('request', { url, headers, body, method });
|
||||
if (IS_DEV) {
|
||||
console.log('request', { url, headers: this.sanitizeRequestLogHeaders(headers), method });
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
Reference in New Issue
Block a user