Skip to content

Commit 1abd909

Browse files
committed
refactor(telemetry): remove sensitive data from telemetry events
1 parent c850f89 commit 1abd909

File tree

8 files changed

+180
-62
lines changed

8 files changed

+180
-62
lines changed

src/services/code-index/embedders/ollama.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { EmbedderInfo, EmbeddingResponse, IEmbedder } from "../interfaces"
33
import { getModelQueryPrefix } from "../../../shared/embeddingModels"
44
import { MAX_ITEM_TOKENS } from "../constants"
55
import { t } from "../../../i18n"
6-
import { withValidationErrorHandling } from "../shared/validation-helpers"
6+
import { withValidationErrorHandling, sanitizeErrorMessage } from "../shared/validation-helpers"
77
import { TelemetryService } from "@roo-code/telemetry"
88
import { TelemetryEventName } from "@roo-code/types"
99

@@ -106,11 +106,9 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
106106
} catch (error: any) {
107107
// Capture telemetry before reformatting the error
108108
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
109-
error: error instanceof Error ? error.message : String(error),
110-
stack: error instanceof Error ? error.stack : undefined,
109+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
110+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
111111
location: "OllamaEmbedder:createEmbeddings",
112-
baseUrl: this.baseUrl,
113-
modelToUse: modelToUse,
114112
})
115113

116114
// Log the original error for debugging purposes
@@ -235,10 +233,9 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
235233
) {
236234
// Capture telemetry for connection failed error
237235
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
238-
error: error instanceof Error ? error.message : String(error),
239-
stack: error instanceof Error ? error.stack : undefined,
236+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
237+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
240238
location: "OllamaEmbedder:validateConfiguration:connectionFailed",
241-
baseUrl: this.baseUrl,
242239
})
243240
return {
244241
valid: false,
@@ -247,10 +244,9 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
247244
} else if (error?.code === "ENOTFOUND" || error?.message?.includes("ENOTFOUND")) {
248245
// Capture telemetry for host not found error
249246
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
250-
error: error instanceof Error ? error.message : String(error),
251-
stack: error instanceof Error ? error.stack : undefined,
247+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
248+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
252249
location: "OllamaEmbedder:validateConfiguration:hostNotFound",
253-
baseUrl: this.baseUrl,
254250
})
255251
return {
256252
valid: false,
@@ -259,10 +255,9 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
259255
} else if (error?.name === "AbortError") {
260256
// Capture telemetry for timeout error
261257
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
262-
error: error instanceof Error ? error.message : String(error),
263-
stack: error instanceof Error ? error.stack : undefined,
258+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
259+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
264260
location: "OllamaEmbedder:validateConfiguration:timeout",
265-
baseUrl: this.baseUrl,
266261
})
267262
// Handle timeout
268263
return {

src/services/code-index/embedders/openai-compatible.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,7 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
291291
error: error instanceof Error ? error.message : String(error),
292292
stack: error instanceof Error ? error.stack : undefined,
293293
location: "OpenAICompatibleEmbedder:_embedBatchWithRetries",
294-
model: model,
295294
attempt: attempts + 1,
296-
baseUrl: this.baseUrl,
297295
})
298296

299297
const hasMoreAttempts = attempts < MAX_RETRIES - 1
@@ -364,8 +362,6 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
364362
error: error instanceof Error ? error.message : String(error),
365363
stack: error instanceof Error ? error.stack : undefined,
366364
location: "OpenAICompatibleEmbedder:validateConfiguration",
367-
baseUrl: this.baseUrl,
368-
modelToUse: this.defaultModelId,
369365
})
370366
throw error
371367
}

src/services/code-index/embedders/openai.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
163163
error: error instanceof Error ? error.message : String(error),
164164
stack: error instanceof Error ? error.stack : undefined,
165165
location: "OpenAiEmbedder:_embedBatchWithRetries",
166-
model: model,
167166
attempt: attempts + 1,
168167
})
169168

@@ -206,7 +205,6 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
206205
error: error instanceof Error ? error.message : String(error),
207206
stack: error instanceof Error ? error.stack : undefined,
208207
location: "OpenAiEmbedder:validateConfiguration",
209-
defaultModelId: this.defaultModelId,
210208
})
211209
throw error
212210
}

src/services/code-index/processors/file-watcher.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { generateNormalizedAbsolutePath, generateRelativeFilePath } from "../sha
2525
import { isPathInIgnoredDirectory } from "../../glob/ignore-utils"
2626
import { TelemetryService } from "@roo-code/telemetry"
2727
import { TelemetryEventName } from "@roo-code/types"
28+
import { sanitizeErrorMessage } from "../shared/validation-helpers"
2829

2930
/**
3031
* Implementation of the file watcher interface
@@ -206,12 +207,9 @@ export class FileWatcher implements IFileWatcher {
206207
} catch (error) {
207208
overallBatchError = error as Error
208209
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
209-
error: error instanceof Error ? error.message : String(error),
210-
stack: error instanceof Error ? error.stack : undefined,
210+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
211+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
211212
location: "_handleBatchDeletions",
212-
filePath: pathsToExplicitlyDelete
213-
.map((path) => createHash("sha256").update(path).digest("hex"))
214-
.join(", "),
215213
})
216214
for (const path of pathsToExplicitlyDelete) {
217215
batchResults.push({ path, status: "error", error: error as Error })
@@ -258,10 +256,9 @@ export class FileWatcher implements IFileWatcher {
258256
} catch (e) {
259257
console.error(`[FileWatcher] Unhandled exception processing file ${fileDetail.path}:`, e)
260258
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
261-
error: e instanceof Error ? e.message : String(e),
262-
stack: e instanceof Error ? e.stack : undefined,
259+
error: sanitizeErrorMessage(e instanceof Error ? e.message : String(e)),
260+
stack: e instanceof Error ? sanitizeErrorMessage(e.stack || "") : undefined,
263261
location: "_processFilesAndPrepareUpserts",
264-
filePath: createHash("sha256").update(fileDetail.path).digest("hex"),
265262
})
266263
return { path: fileDetail.path, result: undefined, error: e as Error }
267264
}
@@ -308,13 +305,16 @@ export class FileWatcher implements IFileWatcher {
308305
console.error("[FileWatcher] A file processing promise was rejected:", settledResult.reason)
309306
const rejectedPath = settledResult.reason?.path || "unknown"
310307
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
311-
error:
308+
error: sanitizeErrorMessage(
312309
settledResult.reason instanceof Error
313310
? settledResult.reason.message
314311
: String(settledResult.reason),
315-
stack: settledResult.reason instanceof Error ? settledResult.reason.stack : undefined,
312+
),
313+
stack:
314+
settledResult.reason instanceof Error
315+
? sanitizeErrorMessage(settledResult.reason.stack || "")
316+
: undefined,
316317
location: "_processFilesAndPrepareUpserts",
317-
filePath: createHash("sha256").update(rejectedPath).digest("hex"),
318318
})
319319
batchResults.push({
320320
path: rejectedPath,
@@ -378,12 +378,9 @@ export class FileWatcher implements IFileWatcher {
378378
} catch (error) {
379379
overallBatchError = overallBatchError || (error as Error)
380380
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
381-
error: error instanceof Error ? error.message : String(error),
382-
stack: error instanceof Error ? error.stack : undefined,
381+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
382+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
383383
location: "_executeBatchUpsertOperations",
384-
filePath: successfullyProcessedForUpsert
385-
.map((item) => createHash("sha256").update(item.path).digest("hex"))
386-
.join(", "),
387384
})
388385
for (const { path } of successfullyProcessedForUpsert) {
389386
batchResults.push({ path, status: "error", error: error as Error })
@@ -571,10 +568,9 @@ export class FileWatcher implements IFileWatcher {
571568
}
572569
} catch (error) {
573570
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
574-
error: error instanceof Error ? error.message : String(error),
575-
stack: error instanceof Error ? error.stack : undefined,
571+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
572+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
576573
location: "processFile",
577-
filePath: createHash("sha256").update(filePath).digest("hex"),
578574
})
579575
return {
580576
path: filePath,

src/services/code-index/processors/parser.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { scannerExtensions } from "../shared/supported-extensions"
99
import { MAX_BLOCK_CHARS, MIN_BLOCK_CHARS, MIN_CHUNK_REMAINDER_CHARS, MAX_CHARS_TOLERANCE_FACTOR } from "../constants"
1010
import { TelemetryService } from "@roo-code/telemetry"
1111
import { TelemetryEventName } from "@roo-code/types"
12+
import { sanitizeErrorMessage } from "../shared/validation-helpers"
1213

1314
/**
1415
* Implementation of the code parser interface
@@ -54,10 +55,9 @@ export class CodeParser implements ICodeParser {
5455
} catch (error) {
5556
console.error(`Error reading file ${filePath}:`, error)
5657
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
57-
error: error instanceof Error ? error.message : String(error),
58-
stack: error instanceof Error ? error.stack : undefined,
58+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
59+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
5960
location: "parseFile",
60-
filePath: createHash("sha256").update(filePath).digest("hex"),
6161
})
6262
return []
6363
}
@@ -110,10 +110,9 @@ export class CodeParser implements ICodeParser {
110110
} catch (error) {
111111
console.error(`Error in pending parser load for ${filePath}:`, error)
112112
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
113-
error: error instanceof Error ? error.message : String(error),
114-
stack: error instanceof Error ? error.stack : undefined,
113+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
114+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
115115
location: "parseContent:loadParser",
116-
filePath: createHash("sha256").update(filePath).digest("hex"),
117116
})
118117
return []
119118
}
@@ -128,10 +127,9 @@ export class CodeParser implements ICodeParser {
128127
} catch (error) {
129128
console.error(`Error loading language parser for ${filePath}:`, error)
130129
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
131-
error: error instanceof Error ? error.message : String(error),
132-
stack: error instanceof Error ? error.stack : undefined,
130+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
131+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
133132
location: "parseContent:loadParser",
134-
filePath: createHash("sha256").update(filePath).digest("hex"),
135133
})
136134
return []
137135
} finally {

src/services/code-index/processors/scanner.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import { isPathInIgnoredDirectory } from "../../glob/ignore-utils"
2828
import { TelemetryService } from "@roo-code/telemetry"
2929
import { TelemetryEventName } from "@roo-code/types"
30+
import { sanitizeErrorMessage } from "../shared/validation-helpers"
3031

3132
export class DirectoryScanner implements IDirectoryScanner {
3233
constructor(
@@ -194,11 +195,9 @@ export class DirectoryScanner implements IDirectoryScanner {
194195
} catch (error) {
195196
console.error(`Error processing file ${filePath} in workspace ${scanWorkspace}:`, error)
196197
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
197-
error: error instanceof Error ? error.message : String(error),
198-
stack: error instanceof Error ? error.stack : undefined,
198+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
199+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
199200
location: "scanDirectory:processFile",
200-
filePath: createHash("sha256").update(filePath).digest("hex"),
201-
workspace: scanWorkspace,
202201
})
203202
if (onError) {
204203
onError(
@@ -257,11 +256,9 @@ export class DirectoryScanner implements IDirectoryScanner {
257256
error,
258257
)
259258
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
260-
error: error instanceof Error ? error.message : String(error),
261-
stack: error instanceof Error ? error.stack : undefined,
259+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
260+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
262261
location: "scanDirectory:deleteRemovedFiles",
263-
filePath: createHash("sha256").update(cachedFilePath).digest("hex"),
264-
workspace: scanWorkspace,
265262
})
266263
if (onError) {
267264
onError(
@@ -326,11 +323,15 @@ export class DirectoryScanner implements IDirectoryScanner {
326323
deleteError,
327324
)
328325
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
329-
error: deleteError instanceof Error ? deleteError.message : String(deleteError),
330-
stack: deleteError instanceof Error ? deleteError.stack : undefined,
326+
error: sanitizeErrorMessage(
327+
deleteError instanceof Error ? deleteError.message : String(deleteError),
328+
),
329+
stack:
330+
deleteError instanceof Error
331+
? sanitizeErrorMessage(deleteError.stack || "")
332+
: undefined,
331333
location: "processBatch:deletePointsByMultipleFilePaths",
332334
fileCount: uniqueFilePaths.length,
333-
workspace: scanWorkspace,
334335
})
335336
// Re-throw the error with workspace context
336337
throw new Error(
@@ -380,11 +381,10 @@ export class DirectoryScanner implements IDirectoryScanner {
380381
error,
381382
)
382383
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
383-
error: error instanceof Error ? error.message : String(error),
384-
stack: error instanceof Error ? error.stack : undefined,
384+
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
385+
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
385386
location: "processBatch:retry",
386387
attemptNumber: attempts,
387-
workspace: scanWorkspace,
388388
batchSize: batchBlocks.length,
389389
})
390390

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { describe, it, expect } from "vitest"
2+
import { sanitizeErrorMessage } from "../validation-helpers"
3+
4+
describe("sanitizeErrorMessage", () => {
5+
it("should sanitize Unix-style file paths", () => {
6+
const input = "Error reading file /Users/username/projects/myapp/src/index.ts"
7+
const expected = "Error reading file [REDACTED_PATH]"
8+
expect(sanitizeErrorMessage(input)).toBe(expected)
9+
})
10+
11+
it("should sanitize Windows-style file paths", () => {
12+
const input = "Cannot access C:\\Users\\username\\Documents\\project\\file.js"
13+
const expected = "Cannot access [REDACTED_PATH]"
14+
expect(sanitizeErrorMessage(input)).toBe(expected)
15+
})
16+
17+
it("should sanitize relative file paths", () => {
18+
const input = "File not found: ./src/components/Button.tsx"
19+
const expected = "File not found: [REDACTED_PATH]"
20+
expect(sanitizeErrorMessage(input)).toBe(expected)
21+
22+
const input2 = "Cannot read ../config/settings.json"
23+
const expected2 = "Cannot read [REDACTED_PATH]"
24+
expect(sanitizeErrorMessage(input2)).toBe(expected2)
25+
})
26+
27+
it("should sanitize URLs with various protocols", () => {
28+
const input = "Failed to connect to http://localhost:11434/api/embed"
29+
const expected = "Failed to connect to [REDACTED_URL]"
30+
expect(sanitizeErrorMessage(input)).toBe(expected)
31+
32+
const input2 = "Error fetching https://api.example.com:8080/v1/embeddings"
33+
const expected2 = "Error fetching [REDACTED_URL]"
34+
expect(sanitizeErrorMessage(input2)).toBe(expected2)
35+
})
36+
37+
it("should sanitize IP addresses", () => {
38+
const input = "Connection refused at 192.168.1.100"
39+
const expected = "Connection refused at [REDACTED_IP]"
40+
expect(sanitizeErrorMessage(input)).toBe(expected)
41+
})
42+
43+
it("should sanitize port numbers", () => {
44+
const input = "Server running on :8080 failed"
45+
const expected = "Server running on :[REDACTED_PORT] failed"
46+
expect(sanitizeErrorMessage(input)).toBe(expected)
47+
})
48+
49+
it("should sanitize email addresses", () => {
50+
const input = "User [email protected] not found"
51+
const expected = "User [REDACTED_EMAIL] not found"
52+
expect(sanitizeErrorMessage(input)).toBe(expected)
53+
})
54+
55+
it("should sanitize paths in quotes", () => {
56+
const input = 'Cannot open file "/home/user/documents/secret.txt"'
57+
const expected = 'Cannot open file "[REDACTED_PATH]"'
58+
expect(sanitizeErrorMessage(input)).toBe(expected)
59+
})
60+
61+
it("should handle complex error messages with multiple sensitive items", () => {
62+
const input = "Failed to fetch http://localhost:11434 from /Users/john/project at 192.168.1.1:3000"
63+
const expected = "Failed to fetch [REDACTED_URL] from [REDACTED_PATH] at [REDACTED_IP]:[REDACTED_PORT]"
64+
expect(sanitizeErrorMessage(input)).toBe(expected)
65+
})
66+
67+
it("should handle non-string inputs gracefully", () => {
68+
expect(sanitizeErrorMessage(null as any)).toBe("null")
69+
expect(sanitizeErrorMessage(undefined as any)).toBe("undefined")
70+
expect(sanitizeErrorMessage(123 as any)).toBe("123")
71+
expect(sanitizeErrorMessage({} as any)).toBe("[object Object]")
72+
})
73+
74+
it("should preserve non-sensitive error messages", () => {
75+
const input = "Invalid JSON format"
76+
expect(sanitizeErrorMessage(input)).toBe(input)
77+
78+
const input2 = "Connection timeout"
79+
expect(sanitizeErrorMessage(input2)).toBe(input2)
80+
})
81+
82+
it("should handle file paths with special characters", () => {
83+
const input = 'Error in "/path/to/file with spaces.txt"'
84+
const expected = 'Error in "[REDACTED_PATH]"'
85+
expect(sanitizeErrorMessage(input)).toBe(expected)
86+
})
87+
88+
it("should sanitize multiple occurrences of sensitive data", () => {
89+
const input = "Copy from /src/file1.js to /dest/file2.js failed"
90+
const expected = "Copy from [REDACTED_PATH] to [REDACTED_PATH] failed"
91+
expect(sanitizeErrorMessage(input)).toBe(expected)
92+
})
93+
})

0 commit comments

Comments
 (0)