Skip to content

Commit 6edd6d8

Browse files
rhoppclaude
andcommitted
Sanitize log output: avoid leaking sensitive data and fix error handling in Azure HTTP client
Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent 9cd3e5c commit 6edd6d8

File tree

3 files changed

+48
-15
lines changed

3 files changed

+48
-15
lines changed

src/api/azure/http/azure-http.client.ts

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
import { BaseHttpClient } from '../../common/http/base-http.client';
2-
import { AxiosError } from 'axios';
2+
import { ApiError } from '../../common/errors/api.errors';
33
import { AzureApiError } from '../errors/azure.errors';
44
import { LoggerFactory, Logger } from '../../../logger/logger';
55

6+
function sanitizeErrorData(data: unknown): string {
7+
if (data == null) return 'no data';
8+
if (typeof data === 'string') return data.slice(0, 2000);
9+
if (typeof data === 'object') {
10+
const { message, typeKey, errorCode, statusCode } = data as Record<string, unknown>;
11+
const parts = [
12+
message != null ? `message=${message}` : null,
13+
typeKey != null ? `typeKey=${typeKey}` : null,
14+
errorCode != null ? `errorCode=${errorCode}` : null,
15+
statusCode != null ? `statusCode=${statusCode}` : null,
16+
].filter(Boolean);
17+
return parts.length > 0 ? parts.join(', ') : '[response data omitted]';
18+
}
19+
return String(data);
20+
}
21+
622
export interface AzureHttpClientConfig {
723
organization: string;
824
host: string;
@@ -43,21 +59,36 @@ export class AzureHttpClient extends BaseHttpClient {
4359

4460
this.client.interceptors.response.use(
4561
response => response,
46-
(error: AxiosError) => {
47-
if (error.response) {
48-
this.logger.error(`Azure DevOps API Error: ${error.response.status} ${error.response.statusText} - ${JSON.stringify(error.response.data)}`);
62+
(error) => {
63+
// Build the AzureApiError first, so wrapping always succeeds regardless of logging
64+
let azureError: AzureApiError;
65+
66+
// BaseHttpClient already transforms AxiosError → ApiError
67+
// Map ApiError to Azure-specific errors first
68+
if (error instanceof ApiError) {
69+
azureError = new AzureApiError(error.message, error.status, error.data, error);
70+
try {
71+
this.logger.error(`Azure DevOps API Error: ${error.status} - ${sanitizeErrorData(error.data)}`);
72+
} catch { /* logging must not break error propagation */ }
73+
} else if (error.response) {
74+
// Fallback: Handle raw AxiosError if BaseHttpClient interceptor didn't catch it
75+
azureError = new AzureApiError(error.message, error.response.status, error.response.data, error);
76+
try {
77+
this.logger.error(`Azure DevOps API Error: ${error.response.status} ${error.response.statusText} - ${sanitizeErrorData(error.response.data)}`);
78+
} catch { /* logging must not break error propagation */ }
4979
} else if (error.request) {
50-
this.logger.error(`Azure DevOps API Error: No response received - ${JSON.stringify(error.request)}`);
80+
azureError = new AzureApiError('No response received from Azure DevOps', undefined, undefined, error);
81+
try {
82+
const { method, baseURL, url, timeout } = error.request?.config ?? error.config ?? {};
83+
this.logger.error(`Azure DevOps API Error: No response received - ${method?.toUpperCase()} ${baseURL ?? ''}${url ?? ''} (timeout: ${timeout})`);
84+
} catch { /* logging must not break error propagation */ }
5185
} else {
52-
this.logger.error(`Azure DevOps API Error: Request setup failed - ${error}`);
86+
azureError = new AzureApiError('Request setup failed', undefined, undefined, error);
87+
try {
88+
this.logger.error(`Azure DevOps API Error: Request setup failed - ${error}`);
89+
} catch { /* logging must not break error propagation */ }
5390
}
54-
// Wrap AxiosError in a custom AzureApiError
55-
const azureError = new AzureApiError(
56-
error.message,
57-
error.response?.status,
58-
error.response?.data,
59-
error
60-
);
91+
6192
return Promise.reject(azureError);
6293
}
6394
);

src/api/azure/services/azure-variable-group.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ export class AzureVariableGroupService {
3939
`${this.project}/_apis/distributedtask/variablegroups?${this.getApiVersionParam()}`,
4040
payload
4141
);
42-
this.logger.info(`AzureCI group creation response: ${JSON.stringify(response)}`);
42+
const { id, name } = (response as any) ?? {};
43+
this.logger.info(`Variable group created: id=${id}, name=${name}`);
4344
} catch (error) {
4445
this.logger.error(`Failed to create variable group '${groupName}': ${error}`);
4546
throw error;

src/api/rhdh/developerhub.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export class DeveloperHub {
4242
componentScaffoldOptions: ScaffolderScaffoldOptions
4343
): Promise<ComponentIdResponse> {
4444
try {
45-
this.logger.info(`Creating component with options: ${JSON.stringify(componentScaffoldOptions)}`);
45+
const { templateRef, values: { name, repoName, namespace, ciType, hostType } = {} } = componentScaffoldOptions ?? {};
46+
this.logger.info(`Creating component: template=${templateRef}, name=${name}, repo=${repoName}, namespace=${namespace}, ci=${ciType}, host=${hostType}`);
4647
const response: AxiosResponse<ComponentIdResponse> = await this.axios.post(
4748
`${this.url}/api/scaffolder/v2/tasks`,
4849
componentScaffoldOptions

0 commit comments

Comments
 (0)