@@ -9,6 +9,7 @@ import { NullLogger } from "../../tests/utils/index.js";
99import type { MockedFunction } from "vitest" ;
1010import type { DeviceId } from "../../src/helpers/deviceId.js" ;
1111import { expectDefined } from "../integration/helpers.js" ;
12+ import { Keychain } from "../../src/common/keychain.js" ;
1213
1314// Mock the ApiClient to avoid real API calls
1415vi . mock ( "../../src/common/atlas/apiClient.js" ) ;
@@ -140,6 +141,7 @@ describe("Telemetry", () => {
140141 close : vi . fn ( ) . mockResolvedValue ( undefined ) ,
141142 setAgentRunner : vi . fn ( ) . mockResolvedValue ( undefined ) ,
142143 logger : new NullLogger ( ) ,
144+ keychain : new Keychain ( ) ,
143145 } as unknown as Session ;
144146
145147 telemetry = Telemetry . create ( session , config , mockDeviceId , {
@@ -345,4 +347,90 @@ describe("Telemetry", () => {
345347 verifyMockCalls ( ) ;
346348 } ) ;
347349 } ) ;
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+ const sensitiveStaticProps = {
362+ mcp_server_version : "secret-server-version" ,
363+ mcp_server_name : "secret-server-name" ,
364+ platform : "linux-secret-password" , // OS info might contain sensitive paths
365+ arch : "x64-secret-key" , // Architecture info might contain sensitive details
366+ os_type : "linux-secret-token" , // OS type might contain sensitive info
367+ os_version : "secret-password-version" , // OS version might contain sensitive details
368+ } ;
369+
370+ telemetry = Telemetry . create ( session , config , mockDeviceId , {
371+ eventCache : mockEventCache as unknown as EventCache ,
372+ commonProperties : sensitiveStaticProps ,
373+ } ) ;
374+
375+ await telemetry . setupPromise ;
376+
377+ telemetry . emitEvents ( [ createTestEvent ( ) ] ) ;
378+
379+ const calls = mockApiClient . sendEvents . mock . calls ;
380+ expect ( calls ) . toHaveLength ( 1 ) ;
381+
382+ // get event properties
383+ const sentEvent = calls [ 0 ] ?. [ 0 ] [ 0 ] as { properties : Record < string , unknown > } ;
384+ expectDefined ( sentEvent ) ;
385+
386+ const eventProps = sentEvent . properties ;
387+ expect ( eventProps . mcp_server_version ) . toBe ( "<password>" ) ;
388+ expect ( eventProps . mcp_server_name ) . toBe ( "<password>" ) ;
389+ expect ( eventProps . platform ) . toBe ( "linux-<password>" ) ;
390+ expect ( eventProps . arch ) . toBe ( "x64-<password>" ) ;
391+ expect ( eventProps . os_type ) . toBe ( "linux-<password>" ) ;
392+ expect ( eventProps . os_version ) . toBe ( "<password>-version" ) ;
393+ } ) ;
394+
395+ it ( "should redact sensitive data from CommonProperties" , async ( ) => {
396+ // register the common properties as sensitive data
397+ session . keychain . register ( "test-device-id" , "password" ) ;
398+ session . keychain . register ( session . sessionId , "password" ) ;
399+
400+ telemetry . emitEvents ( [ createTestEvent ( ) ] ) ;
401+
402+ const calls = mockApiClient . sendEvents . mock . calls ;
403+ expect ( calls ) . toHaveLength ( 1 ) ;
404+
405+ // get event properties
406+ const sentEvent = calls [ 0 ] ?. [ 0 ] [ 0 ] as { properties : Record < string , unknown > } ;
407+ expectDefined ( sentEvent ) ;
408+
409+ const eventProps = sentEvent . properties ;
410+
411+ expect ( eventProps . device_id ) . toBe ( "<password>" ) ;
412+ expect ( eventProps . session_id ) . toBe ( "<password>" ) ;
413+ } ) ;
414+
415+ it ( "should redact sensitive data that is added to events" , ( ) => {
416+ session . keychain . register ( "test-device-id" , "password" ) ;
417+ session . keychain . register ( session . sessionId , "password" ) ;
418+ session . keychain . register ( "test-component" , "password" ) ;
419+
420+ telemetry . emitEvents ( [ createTestEvent ( ) ] ) ;
421+
422+ const calls = mockApiClient . sendEvents . mock . calls ;
423+ expect ( calls ) . toHaveLength ( 1 ) ;
424+
425+ // get event properties
426+ const sentEvent = calls [ 0 ] ?. [ 0 ] [ 0 ] as { properties : Record < string , unknown > } ;
427+ expectDefined ( sentEvent ) ;
428+
429+ const eventProps = sentEvent . properties ;
430+ expect ( eventProps . device_id ) . toBe ( "<password>" ) ;
431+ expect ( eventProps . session_id ) . toBe ( "<password>" ) ;
432+ expect ( eventProps . component ) . toBe ( "<password>" ) ;
433+ } ) ;
434+ } ) ;
435+ } ) ;
348436} ) ;
0 commit comments