@@ -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,91 @@ 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+ // 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+ } ) ;
348437} ) ;
0 commit comments