Respect logPrompts flag for logging sensitive fields (#26153)

Co-authored-by: David Pierce <davidapierce@google.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
This commit is contained in:
lp-peg
2026-04-30 06:43:34 +09:00
committed by GitHub
parent dce13019b9
commit 2194da2b02
4 changed files with 437 additions and 57 deletions
@@ -19,6 +19,7 @@ import {
import type { Config } from '../config/config.js';
import * as sdk from './sdk.js';
import { ClearcutLogger } from './clearcut-logger/clearcut-logger.js';
import { EventMetadataKey } from './clearcut-logger/event-metadata-key.js';
vi.mock('@opentelemetry/api-logs');
vi.mock('./sdk.js');
@@ -144,4 +145,174 @@ describe('conseca-logger', () => {
expect(mockLogger.emit).not.toHaveBeenCalled();
});
it('should omit user_prompt/trusted_content/policy from OTEL when logPrompts is disabled', () => {
const configNoPrompts = {
getTelemetryEnabled: vi.fn().mockReturnValue(true),
getSessionId: vi.fn().mockReturnValue('test-session-id'),
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
getTelemetryTracesEnabled: vi.fn().mockReturnValue(false),
isInteractive: vi.fn().mockReturnValue(true),
getExperiments: vi.fn().mockReturnValue({ experimentIds: [] }),
getContentGeneratorConfig: vi.fn().mockReturnValue({ authType: 'oauth' }),
} as unknown as Config;
const event = new ConsecaPolicyGenerationEvent(
'sensitive prompt',
'sensitive content',
'sensitive policy',
);
logConsecaPolicyGeneration(configNoPrompts, event);
const attrs = mockLogger.emit.mock.calls[0][0].attributes as Record<
string,
unknown
>;
expect(attrs['user_prompt']).toBeUndefined();
expect(attrs['trusted_content']).toBeUndefined();
expect(attrs['policy']).toBeUndefined();
expect(attrs['event.name']).toBe(EVENT_CONSECA_POLICY_GENERATION);
});
it('should omit user_prompt/trusted_content/policy from Clearcut when logPrompts is disabled', () => {
const configNoPrompts = {
getTelemetryEnabled: vi.fn().mockReturnValue(true),
getSessionId: vi.fn().mockReturnValue('test-session-id'),
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
getTelemetryTracesEnabled: vi.fn().mockReturnValue(false),
isInteractive: vi.fn().mockReturnValue(true),
getExperiments: vi.fn().mockReturnValue({ experimentIds: [] }),
getContentGeneratorConfig: vi.fn().mockReturnValue({ authType: 'oauth' }),
} as unknown as Config;
const event = new ConsecaPolicyGenerationEvent(
'sensitive prompt',
'sensitive content',
'sensitive policy',
'some error',
);
logConsecaPolicyGeneration(configNoPrompts, event);
expect(mockClearcutLogger.createLogEvent).toHaveBeenCalledWith(
expect.anything(),
[
{
gemini_cli_key: EventMetadataKey.CONSECA_ERROR,
value: 'some error',
},
],
);
});
it('should include user_prompt/trusted_content/policy in OTEL when logPrompts is enabled', () => {
const event = new ConsecaPolicyGenerationEvent(
'visible prompt',
'visible content',
'visible policy',
);
logConsecaPolicyGeneration(mockConfig, event);
const attrs = mockLogger.emit.mock.calls[0][0].attributes as Record<
string,
unknown
>;
expect(attrs['user_prompt']).toBe('visible prompt');
expect(attrs['trusted_content']).toBe('visible content');
expect(attrs['policy']).toBe('visible policy');
});
it('should omit sensitive fields from verdict OTEL when logPrompts is disabled', () => {
const configNoPrompts = {
getTelemetryEnabled: vi.fn().mockReturnValue(true),
getSessionId: vi.fn().mockReturnValue('test-session-id'),
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
getTelemetryTracesEnabled: vi.fn().mockReturnValue(false),
isInteractive: vi.fn().mockReturnValue(true),
getExperiments: vi.fn().mockReturnValue({ experimentIds: [] }),
getContentGeneratorConfig: vi.fn().mockReturnValue({ authType: 'oauth' }),
} as unknown as Config;
const event = new ConsecaVerdictEvent(
'sensitive prompt',
'sensitive policy',
'sensitive tool call',
'allow',
'sensitive rationale',
);
logConsecaVerdict(configNoPrompts, event);
const attrs = mockLogger.emit.mock.calls[0][0].attributes as Record<
string,
unknown
>;
expect(attrs['user_prompt']).toBeUndefined();
expect(attrs['policy']).toBeUndefined();
expect(attrs['tool_call']).toBeUndefined();
expect(attrs['verdict_rationale']).toBeUndefined();
// verdict (the allow/deny result) is not sensitive and should be present
expect(attrs['verdict']).toBe('allow');
});
it('should omit sensitive fields from verdict Clearcut when logPrompts is disabled', () => {
const configNoPrompts = {
getTelemetryEnabled: vi.fn().mockReturnValue(true),
getSessionId: vi.fn().mockReturnValue('test-session-id'),
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
getTelemetryTracesEnabled: vi.fn().mockReturnValue(false),
isInteractive: vi.fn().mockReturnValue(true),
getExperiments: vi.fn().mockReturnValue({ experimentIds: [] }),
getContentGeneratorConfig: vi.fn().mockReturnValue({ authType: 'oauth' }),
} as unknown as Config;
const event = new ConsecaVerdictEvent(
'sensitive prompt',
'sensitive policy',
'sensitive tool call',
'allow',
'sensitive rationale',
'some error',
);
logConsecaVerdict(configNoPrompts, event);
expect(mockClearcutLogger.createLogEvent).toHaveBeenCalledWith(
expect.anything(),
[
{
gemini_cli_key: EventMetadataKey.CONSECA_VERDICT_RESULT,
value: '"allow"',
},
{
gemini_cli_key: EventMetadataKey.CONSECA_ERROR,
value: 'some error',
},
],
);
});
it('should include sensitive fields in verdict OTEL when logPrompts is enabled', () => {
const event = new ConsecaVerdictEvent(
'visible prompt',
'visible policy',
'visible tool call',
'deny',
'visible rationale',
);
logConsecaVerdict(mockConfig, event);
const attrs = mockLogger.emit.mock.calls[0][0].attributes as Record<
string,
unknown
>;
expect(attrs['user_prompt']).toBe('visible prompt');
expect(attrs['policy']).toBe('visible policy');
expect(attrs['tool_call']).toBe('visible tool call');
expect(attrs['verdict_rationale']).toBe('visible rationale');
expect(attrs['verdict']).toBe('deny');
});
});