Skip to content

Commit d84c793

Browse files
committed
update and add unit tests
1 parent 183f9c5 commit d84c793

File tree

3 files changed

+91
-31
lines changed

3 files changed

+91
-31
lines changed

src/telemetry/telemetry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ export class Telemetry {
5353
} = {}
5454
): Telemetry {
5555
const mergedProperties = {
56-
...redact(MACHINE_METADATA, session.keychain.allSecrets),
57-
...redact(commonProperties, session.keychain.allSecrets),
56+
...MACHINE_METADATA,
57+
...commonProperties,
5858
};
5959
const instance = new Telemetry(session, userConfig, mergedProperties, {
6060
eventCache,
@@ -222,7 +222,7 @@ export class Telemetry {
222222
events.map((event) => ({
223223
...event,
224224
properties: {
225-
...redact(this.getCommonProperties()),
225+
...redact(this.getCommonProperties(), this.session.keychain.allSecrets),
226226
...redact(event.properties, this.session.keychain.allSecrets),
227227
},
228228
}))

tests/integration/telemetry.test.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,32 +36,4 @@ describe("Telemetry", () => {
3636
expect(telemetry.getCommonProperties().device_id).toBe(actualDeviceId);
3737
expect(telemetry["isBufferingEvents"]).toBe(false);
3838
});
39-
40-
it("should redact sensitive data", async () => {
41-
const logger = new CompositeLogger();
42-
const deviceId = DeviceId.create(logger);
43-
44-
// configure keychain with a secret that would show up in random properties
45-
const keychain = new Keychain();
46-
keychain.register(process.platform, "url");
47-
48-
const telemetry = Telemetry.create(
49-
new Session({
50-
apiBaseUrl: "",
51-
logger,
52-
exportsManager: ExportsManager.init(config, logger),
53-
connectionManager: new MCPConnectionManager(config, driverOptions, logger, deviceId),
54-
keychain: keychain,
55-
}),
56-
config,
57-
deviceId
58-
);
59-
60-
await telemetry.setupPromise;
61-
62-
// expect the platform to be redacted
63-
const commonProperties = telemetry.getCommonProperties();
64-
expect(commonProperties.platform).toBe("<url>");
65-
expect(telemetry["isBufferingEvents"]).toBe(false);
66-
});
6739
});

tests/unit/telemetry.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { NullLogger } from "../../tests/utils/index.js";
99
import type { MockedFunction } from "vitest";
1010
import type { DeviceId } from "../../src/helpers/deviceId.js";
1111
import { expectDefined } from "../integration/helpers.js";
12+
import { Keychain } from "../../src/common/keychain.js";
1213

1314
// Mock the ApiClient to avoid real API calls
1415
vi.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

Comments
 (0)