@@ -9,6 +9,7 @@ import { NullLogger } from "../../tests/utils/index.js";
9
9
import type { MockedFunction } from "vitest" ;
10
10
import type { DeviceId } from "../../src/helpers/deviceId.js" ;
11
11
import { expectDefined } from "../integration/helpers.js" ;
12
+ import { Keychain } from "../../src/common/keychain.js" ;
12
13
13
14
// Mock the ApiClient to avoid real API calls
14
15
vi . mock ( "../../src/common/atlas/apiClient.js" ) ;
@@ -140,6 +141,7 @@ describe("Telemetry", () => {
140
141
close : vi . fn ( ) . mockResolvedValue ( undefined ) ,
141
142
setAgentRunner : vi . fn ( ) . mockResolvedValue ( undefined ) ,
142
143
logger : new NullLogger ( ) ,
144
+ keychain : new Keychain ( ) ,
143
145
} as unknown as Session ;
144
146
145
147
telemetry = Telemetry . create ( session , config , mockDeviceId , {
@@ -345,4 +347,91 @@ describe("Telemetry", () => {
345
347
verifyMockCalls ( ) ;
346
348
} ) ;
347
349
} ) ;
350
+
351
+ describe ( "when secrets are registered" , ( ) => {
352
+ describe ( "comprehensive redaction coverage" , ( ) => {
353
+ it ( "should redact sensitive data from CommonStaticProperties" , async ( ) => {
354
+ session . keychain . register ( "secret-server-version" , "password" ) ;
355
+ session . keychain . register ( "secret-server-name" , "password" ) ;
356
+ session . keychain . register ( "secret-password" , "password" ) ;
357
+ session . keychain . register ( "secret-key" , "password" ) ;
358
+ session . keychain . register ( "secret-token" , "password" ) ;
359
+ session . keychain . register ( "secret-password-version" , "password" ) ;
360
+
361
+ // Simulates sensitive data across random properties
362
+ const sensitiveStaticProps = {
363
+ mcp_server_version : "secret-server-version" ,
364
+ mcp_server_name : "secret-server-name" ,
365
+ platform : "linux-secret-password" ,
366
+ arch : "x64-secret-key" ,
367
+ os_type : "linux-secret-token" ,
368
+ os_version : "secret-password-version" ,
369
+ } ;
370
+
371
+ telemetry = Telemetry . create ( session , config , mockDeviceId , {
372
+ eventCache : mockEventCache as unknown as EventCache ,
373
+ commonProperties : sensitiveStaticProps ,
374
+ } ) ;
375
+
376
+ await telemetry . setupPromise ;
377
+
378
+ telemetry . emitEvents ( [ createTestEvent ( ) ] ) ;
379
+
380
+ const calls = mockApiClient . sendEvents . mock . calls ;
381
+ expect ( calls ) . toHaveLength ( 1 ) ;
382
+
383
+ // get event properties
384
+ const sentEvent = calls [ 0 ] ?. [ 0 ] [ 0 ] as { properties : Record < string , unknown > } ;
385
+ expectDefined ( sentEvent ) ;
386
+
387
+ const eventProps = sentEvent . properties ;
388
+ expect ( eventProps . mcp_server_version ) . toBe ( "<password>" ) ;
389
+ expect ( eventProps . mcp_server_name ) . toBe ( "<password>" ) ;
390
+ expect ( eventProps . platform ) . toBe ( "linux-<password>" ) ;
391
+ expect ( eventProps . arch ) . toBe ( "x64-<password>" ) ;
392
+ expect ( eventProps . os_type ) . toBe ( "linux-<password>" ) ;
393
+ expect ( eventProps . os_version ) . toBe ( "<password>-version" ) ;
394
+ } ) ;
395
+
396
+ it ( "should redact sensitive data from CommonProperties" , ( ) => {
397
+ // register the common properties as sensitive data
398
+ session . keychain . register ( "test-device-id" , "password" ) ;
399
+ session . keychain . register ( session . sessionId , "password" ) ;
400
+
401
+ telemetry . emitEvents ( [ createTestEvent ( ) ] ) ;
402
+
403
+ const calls = mockApiClient . sendEvents . mock . calls ;
404
+ expect ( calls ) . toHaveLength ( 1 ) ;
405
+
406
+ // get event properties
407
+ const sentEvent = calls [ 0 ] ?. [ 0 ] [ 0 ] as { properties : Record < string , unknown > } ;
408
+ expectDefined ( sentEvent ) ;
409
+
410
+ const eventProps = sentEvent . properties ;
411
+
412
+ expect ( eventProps . device_id ) . toBe ( "<password>" ) ;
413
+ expect ( eventProps . session_id ) . toBe ( "<password>" ) ;
414
+ } ) ;
415
+
416
+ it ( "should redact sensitive data that is added to events" , ( ) => {
417
+ session . keychain . register ( "test-device-id" , "password" ) ;
418
+ session . keychain . register ( session . sessionId , "password" ) ;
419
+ session . keychain . register ( "test-component" , "password" ) ;
420
+
421
+ telemetry . emitEvents ( [ createTestEvent ( ) ] ) ;
422
+
423
+ const calls = mockApiClient . sendEvents . mock . calls ;
424
+ expect ( calls ) . toHaveLength ( 1 ) ;
425
+
426
+ // get event properties
427
+ const sentEvent = calls [ 0 ] ?. [ 0 ] [ 0 ] as { properties : Record < string , unknown > } ;
428
+ expectDefined ( sentEvent ) ;
429
+
430
+ const eventProps = sentEvent . properties ;
431
+ expect ( eventProps . device_id ) . toBe ( "<password>" ) ;
432
+ expect ( eventProps . session_id ) . toBe ( "<password>" ) ;
433
+ expect ( eventProps . component ) . toBe ( "<password>" ) ;
434
+ } ) ;
435
+ } ) ;
436
+ } ) ;
348
437
} ) ;
0 commit comments