Skip to content

Commit 915c199

Browse files
Copilotwenytang-ms
authored andcommitted
Harden Language Model Tool telemetry against PII leaks
Centralise all LMT telemetry through src/lmToolTelemetry.ts so user-supplied strings (target, expression, sessionName, file paths, class names, JVM stack traces, etc.) can no longer reach the telemetry pipeline. The new module exposes a typed sanitizedSend choke point that only accepts enums, booleans, numbers and opaque session IDs. Telemetry changes: - Drop sendError(error) on debug_java_application failure (stack trace leaked user class / method names). - Strip PII fields from every existing event: target, sessionName, currentFile, currentLine, simpleClassName, detectedClassName, error: String(error), input.reason. - Replace bare String(error) propagation with classifyError() -> ErrorCategory enum (mainClassMissing, classpathUnresolved, buildFailure, projectNotDetected, sessionAlreadyRunning, timeout, lsNotReady, noActiveSession, noSuspendedThread, noStackFrame, cancelled, other). - Add per-invoke recording for all 10 tools with outcome, errorCategory, durationMs, and a tool-specific enum (targetType / breakpointKind / stepKind / scopeType / evalContext / removeScope). The previous build only emitted telemetry on the launch tool and the session-info tool. - Add chatActivationSnapshot one-shot at registration time so we can measure adoption of the chat surfaces without per-turn cost (counts only). - evaluate_debug_expression: the expression text is NEVER logged. Only the evalContext enum and outcome are emitted. Policy: - src/lmToolTelemetry.ts is now the only file in the LMT code path allowed to call sendInfo. The top-of-file policy comment is the single source of truth for what may be logged. - The recorder is typed against ToolInvocationRecord so excess raw strings are rejected at compile time. Validated with: npm run tslint, npm run compile.
1 parent da84f11 commit 915c199

3 files changed

Lines changed: 627 additions & 59 deletions

File tree

src/extension.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { JavaDebugAdapterDescriptorFactory } from "./javaDebugAdapterDescriptorF
1919
import { JavaInlineValuesProvider } from "./JavaInlineValueProvider";
2020
import { logJavaException, logJavaInfo } from "./javaLogger";
2121
import { registerLanguageModelTool, registerDebugSessionTools } from "./languageModelTool";
22+
import { recordChatActivation } from "./lmToolTelemetry";
2223
import { IMainClassOption, IMainMethod, resolveMainMethod } from "./languageServerPlugin";
2324
import { mainClassPicker } from "./mainClassPicker";
2425
import { pickJavaProcess } from "./processPicker";
@@ -124,6 +125,29 @@ async function registerLanguageModelToolsWhenReady(context: vscode.ExtensionCont
124125
registerLanguageModelTool(context);
125126
const debugToolsDisposables = registerDebugSessionTools(context);
126127
context.subscriptions.push(...debugToolsDisposables);
128+
129+
// One-shot activation snapshot so we can track coverage of the new chat surfaces over time.
130+
// Counts only — no user data, no file paths, no class names.
131+
try {
132+
const pkg = context.extension?.packageJSON as {
133+
version?: string;
134+
contributes?: {
135+
languageModelTools?: unknown[];
136+
chatSkills?: unknown[];
137+
chatInstructions?: unknown[];
138+
};
139+
} | undefined;
140+
const contrib = pkg?.contributes ?? {};
141+
recordChatActivation({
142+
javaLSReadyAtActivation: !!javaExt.isActive,
143+
lmtCount: contrib.languageModelTools?.length ?? 0,
144+
chatSkillsCount: contrib.chatSkills?.length ?? 0,
145+
chatInstructionsCount: contrib.chatInstructions?.length ?? 0,
146+
extensionVersion: pkg?.version ?? "unknown",
147+
});
148+
} catch {
149+
// Telemetry must never break activation.
150+
}
127151
}
128152

129153
async function subscribeToJavaExtensionEvents(): Promise<void> {

0 commit comments

Comments
 (0)