Skip to content

Commit 5b04bef

Browse files
committed
Improve logger data sanitization to support recursive handling and circular references
- Please refactor the `sanitize_data` function in `packages/shared/src/utils/logger.ts` to fix multiple API key leak vulnerabilities. Currently, it only checks top-level Axios errors, misses the raw `request._header` string, fails on nested errors (like `{ data: error }`), and doesn't redact Anthropic's `x-api-key`. Implement a robust recursive sanitization function with the following requirements: 1. **Recursive Deep Clone:** Traverse objects recursively so nested errors (e.g., `{ parse_error: error }`) are caught. 2. **Circular Reference Protection:** Use a `WeakSet` to prevent `Maximum call stack size exceeded` errors, as Axios error objects contain highly nested circular references (e.g., `request.res.req`). 3. **Key-based Redaction:** If an object key is `authorization` or `x-api-key` (case-insensitive), replace its value with `'[REDACTED]'`. 4. **String Regex Redaction:** If a value is a string (specifically to catch the raw HTTP string in `request._header`), run a regex replace... ├── apps │ └── editor │ └── src │ ├── services │ │ ├── model-fetcher.ts │ │ └── prompt-view-api-calls-manager.ts │ └── utils │ ├── build-request-headers.ts │ └── send-llm-message.ts └── packages └── shared ├── package.json └── src └── utils └── logger.ts
1 parent 1b5d8e7 commit 5b04bef

1 file changed

Lines changed: 30 additions & 36 deletions

File tree

‎packages/shared/src/utils/logger.ts‎

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,42 @@
11
export namespace Logger {
22
const LOG_PREFIX = '[CWC]'
33

4-
const sanitize_data = (data: any) => {
5-
if (!data || typeof data !== 'object') return data
4+
const sanitize_data = (data: any, seen = new WeakSet()): any => {
5+
if (data === null || data === undefined) return data
66

7-
if (data.isAxiosError) {
8-
const sanitized = {
9-
name: data.name,
10-
message: data.message,
11-
stack: data.stack,
12-
...data
13-
}
7+
if (typeof data === 'string') {
8+
return data
9+
.replace(/(Authorization:\s*Bearer\s+)[^\r\n]+/gi, '$1[REDACTED]')
10+
.replace(/(x-api-key:\s*)[^\r\n]+/gi, '$1[REDACTED]')
11+
}
1412

15-
if (sanitized.config?.headers) {
16-
sanitized.config = {
17-
...sanitized.config,
18-
headers: { ...sanitized.config.headers }
19-
}
20-
for (const key in sanitized.config.headers) {
21-
if (key.toLowerCase() === 'authorization') {
22-
sanitized.config.headers[key] = '[REDACTED]'
23-
}
24-
}
25-
}
13+
if (typeof data !== 'object') return data
2614

27-
if (sanitized.response?.config?.headers) {
28-
sanitized.response = {
29-
...sanitized.response,
30-
config: {
31-
...sanitized.response.config,
32-
headers: { ...sanitized.response.config.headers }
33-
}
34-
}
35-
for (const key in sanitized.response.config.headers) {
36-
if (key.toLowerCase() === 'authorization') {
37-
sanitized.response.config.headers[key] = '[REDACTED]'
38-
}
39-
}
40-
}
15+
if (seen.has(data)) return '[Circular]'
16+
seen.add(data)
17+
18+
if (Array.isArray(data)) {
19+
return data.map((item) => sanitize_data(item, seen))
20+
}
21+
22+
const sanitized: any = {}
4123

42-
return sanitized
24+
if (data instanceof Error) {
25+
sanitized.name = data.name
26+
sanitized.message = data.message
27+
sanitized.stack = data.stack
28+
}
29+
30+
for (const key of Object.keys(data)) {
31+
const lower_key = key.toLowerCase()
32+
if (lower_key === 'authorization' || lower_key === 'x-api-key') {
33+
sanitized[key] = '[REDACTED]'
34+
} else {
35+
sanitized[key] = sanitize_data(data[key], seen)
36+
}
4337
}
4438

45-
return data
39+
return sanitized
4640
}
4741

4842
export function info(params: {

0 commit comments

Comments
 (0)