telemetryUtils.ts ×15

Frontier kind: Code frontier

unlabeled · c_0bcb037fde36

2777 tests · 5858 LOC · 34 files · introduces 0 tests · 213 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
18 ranges213 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
791 ranges5858 lines · 34 files · Browse complete extent
All tests (intent)
2777 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

2 files ranked by introduced lines: 213 introduced LOC across 18 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/telemetry/common/telemetryUtils.ts 199 introduced LOC · 15 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- telemetryUtils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { cloneAndChange, safeStringify } from '../../../base/common/objects.js';
7 > import { isObject } from '../../../base/common/types.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { localize } from '../../../nls.js';
10 > import { IConfigurationService } from '../../configuration/common/configuration.js';
11 > import { IEnvironmentService } from '../../environment/common/environment.js';
12 > import { LoggerGroup } from '../../log/common/log.js';
13 > import { IProductService } from '../../product/common/productService.js';
14 > import { getRemoteName } from '../../remote/common/remoteHosts.js';
15 > import { verifyMicrosoftInternalDomain } from './commonProperties.js';
16 > import { ICustomEndpointTelemetryService, ITelemetryData, ITelemetryEndpoint, ITelemetryService, TelemetryConfiguration, TelemetryLevel, TELEMETRY_CRASH_REPORTER_SETTING_ID, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SETTING_ID } from './telemetry.js';
17 >
18 > /**
19 > * A special class used to denoting a telemetry value which should not be clean.
20 > * This is because that value is "Trusted" not to contain identifiable information such as paths.
21 > * NOTE: This is used as an API type as well, and should not be changed.
22 > */
23 > export class TelemetryTrustedValue<T> {
24 > // This is merely used as an identifier as the instance will be lost during serialization over the exthost
25 > public readonly isTrustedTelemetryValue = true;
26 > constructor(public readonly value: T) { }
27 > }
28 >
29 > export class NullTelemetryServiceShape implements ITelemetryService {
30 > declare readonly _serviceBrand: undefined;
31 > readonly telemetryLevel = TelemetryLevel.NONE;
32 > readonly sessionId = 'someValue.sessionId';
33 > readonly machineId = 'someValue.machineId';
34 > readonly sqmId = 'someValue.sqmId';
35 > readonly devDeviceId = 'someValue.devDeviceId';
36 > readonly firstSessionDate = 'someValue.firstSessionDate';
37 > readonly sendErrorTelemetry = false;
38 > publicLog() { }
39 > publicLog2() { }
40 > publicLogError() { }
41 > publicLogError2() { }
42 > setExperimentProperty() { }
43 > setCommonProperty() { }
44 > }
45 >
46 > export const NullTelemetryService = new NullTelemetryServiceShape();
47 >
48 > export class NullEndpointTelemetryService implements ICustomEndpointTelemetryService {
49 > _serviceBrand: undefined;
50 >
51 > async publicLog(_endpoint: ITelemetryEndpoint, _eventName: string, _data?: ITelemetryData): Promise<void> {
52 // noop
53 }
55 > async publicLogError(_endpoint: ITelemetryEndpoint, _errorEventName: string, _data?: ITelemetryData): Promise<void> {
56 // noop
57 }
59 >
60 > export const telemetryLogId = 'telemetry';
61 > export const TelemetryLogGroup: LoggerGroup = { id: telemetryLogId, name: localize('telemetryLogName', "Telemetry") };
62 >
63 > export interface ITelemetryAppender {
64 > log(eventName: string, data: ITelemetryData): void;
65 > flush(): Promise<void>;
66 > }
67 >
68 > export const NullAppender: ITelemetryAppender = { log: () => null, flush: () => Promise.resolve(undefined) };
69 >
70 >
71 > /* __GDPR__FRAGMENT__
72 > "URIDescriptor" : {
73 > "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
74 > "scheme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
75 > "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
76 > "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
77 > }
78 > */
79 > export interface URIDescriptor {
80 > mimeType?: string;
81 > scheme?: string;
82 > ext?: string;
83 > path?: string;
84 > }
85 >
86 > /**
87 > * Determines whether or not we support logging telemetry.
88 > * This checks if the product is capable of collecting telemetry but not whether or not it can send it
89 > * For checking the user setting and what telemetry you can send please check `getTelemetryLevel`.
90 > * This returns true if `--disable-telemetry` wasn't used, the product.json allows for telemetry, and we're not testing an extension
91 > * If false telemetry is disabled throughout the product
92 > * @param productService
93 > * @param environmentService
94 > * @returns false - telemetry is completely disabled, true - telemetry is logged locally, but may not be sent
95 > */
96 > export function supportsTelemetry(productService: IProductService, environmentService: IEnvironmentService): boolean {
97 // If it's OSS and telemetry isn't disabled via the CLI we will allow it for logging only purposes
98 if (!environmentService.isBuilt && !environmentService.disableTelemetry) {
101 return !(environmentService.disableTelemetry || !productService.enableTelemetry);
102 }
104 > /**
105 > * Checks to see if we're in logging only mode to debug telemetry.
106 > * This is if telemetry is enabled and we're in OSS, but no telemetry key is provided so it's not being sent just logged.
107 > * @param productService
108 > * @param environmentService
109 > * @returns True if telemetry is actually disabled and we're only logging for debug purposes
110 > */
111 > export function isLoggingOnly(productService: IProductService, environmentService: IEnvironmentService): boolean {
112 // If we're testing an extension, log telemetry for debug purposes
113 if (environmentService.extensionTestsLocationURI) {
129 return true;
130 }
132 > /**
133 > * Determines how telemetry is handled based on the user's configuration.
134 > *
135 > * @param configurationService
136 > * @returns OFF, ERROR, ON
137 > */
138 > export function getTelemetryLevel(configurationService: IConfigurationService): TelemetryLevel {
139 const newConfig = configurationService.getValue<TelemetryConfiguration>(TELEMETRY_SETTING_ID);
140 const crashReporterConfig = configurationService.getValue<boolean | undefined>(TELEMETRY_CRASH_REPORTER_SETTING_ID);
158 }
159 }
161 > export interface Properties {
162 > [key: string]: string;
163 > }
164 >
165 > export interface Measurements {
166 > [key: string]: number;
167 > }
168 >
169 > export function validateTelemetryData(data?: unknown): { properties: Properties; measurements: Measurements } {
170
171 const properties: Properties = {};
204 };
205 }
207 > interface IRemoteAuthoringConfig {
208 > remoteExtensionTips?: { readonly [remoteName: string]: unknown };
209 > virtualWorkspaceExtensionTips?: { readonly [remoteName: string]: unknown };
210 > }
211 >
212 > export function cleanRemoteAuthority(remoteAuthority: string | undefined, config: IRemoteAuthoringConfig): string {
213 if (!remoteAuthority) {
214 return 'none';
229 return 'other';
230 }
232 function flatten(obj: unknown, result: Record<string, unknown>, order: number = 0, prefix?: string): void {
233 if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
258 }
259 }
261 > /**
262 > * Whether or not this is an internal user
263 > * @param productService The product service
264 > * @param configService The config servivce
265 > * @returns true if internal, false otherwise
266 > */
267 > export function isInternalTelemetry(productService: IProductService, configService: IConfigurationService) {
268 const msftInternalDomains = productService.msftInternalDomains || [];
269 const internalTesting = configService.getValue<boolean>('telemetry.internalTesting');
270 return verifyMicrosoftInternalDomain(msftInternalDomains) || internalTesting;
271 }
273 > interface IPathEnvironment {
274 > appRoot: string;
275 > extensionsPath: string;
276 > userDataPath: string;
277 > userHome: URI;
278 > tmpDir: URI;
279 > }
280 >
281 > export function getPiiPathsFromEnvironment(paths: IPathEnvironment): string[] {
282 return [paths.appRoot, paths.extensionsPath, paths.userHome.fsPath, paths.tmpDir.fsPath, paths.userDataPath];
283 }
285 > //#region Telemetry Cleaning
286 >
287 > /**
288 > * Cleans a given stack of possible paths
289 > * @param stack The stack to sanitize
290 > * @param cleanupPatterns Cleanup patterns to remove from the stack
291 > * @returns The cleaned stack
292 > */
293 function anonymizeFilePaths(stack: string, cleanupPatterns: RegExp[]): string {
294
356 return updatedStack;
357 }
359 > const userDataRegexes = [
360 > { label: 'URL', regex: /[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s]*/ },
361 > { label: 'Google API Key', regex: /AIza[A-Za-z0-9_\\\-]{35}/ },
362 > { label: 'JWT', regex: /eyJ[0eXAiOiJKV1Qi|hbGci|a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+/ },
363 > { label: 'Slack Token', regex: /xox[pbar]\-[A-Za-z0-9]/ },
364 > { label: 'GitHub Token', regex: /(gh[psuro]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})/ },
365 > { label: 'Generic Secret', regex: /(key|token|sig|secret|signature|password|passwd|pwd|android:value)[^a-zA-Z0-9]/i },
366 > { label: 'CLI Credentials', regex: /((login|psexec|(certutil|psexec)\.exe).{1,50}(\s-u(ser(name)?)?\s+.{3,100})?\s-(admin|user|vm|root)?p(ass(word)?)?\s+["']?[^$\-\/\s]|(^|[\s\r\n\\])net(\.exe)?.{1,5}(user\s+|share\s+\/user:| user -? secrets ? set) \s + [^ $\s \/])/ },
367 > { label: 'Microsoft Entra ID', regex: /eyJ(?:0eXAiOiJKV1Qi|hbGci|[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.)/ },
368 > { label: 'Email', regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/ }
369 > ];
370 >
371 > /**
372 > * Redacts a value if it contains commonly leaked PII.
373 > * @param value The value returned (as-is) when no PII is detected
374 > * @param probe The string actually matched against the PII heuristics. Defaults
375 > * to `value`; callers may pass a value that includes a trailing delimiter (e.g. a
376 > * newline) so that heuristics relying on a non-alphanumeric boundary match the
377 > * same way they would against the original whole string.
378 > * @returns A `<REDACTED: ...>` marker if the probe matched, otherwise `value`
379 > */
380 function redactIfPossibleUserInfo(value: string, probe: string = value): string {
381 for (const secretRegex of userDataRegexes) {
386 return value;
387 }
389 > /**
390 > * Attempts to remove commonly leaked PII.
391 > *
392 > * When a match is found the check is applied per line so that a single suspicious
393 > * frame (e.g. a stack frame containing a function name such as `getStorageKey`
394 > * which matches the broad `Generic Secret` heuristic) only redacts that line —
395 > * replacing it with a `<REDACTED: ...>` marker — instead of wiping the entire
396 > * multi-line value such as a whole callstack.
397 > * @param property The property whose offending lines will be replaced with a redaction marker if they contain user data
398 > * @returns The new value for the property
399 > */
400 function removePropertiesWithPossibleUserInfo(property: string): string {
401 // If for some reason it is undefined we skip it (this shouldn't be possible);
436 return lines.join('\n');
437 }
439 >
440 > /**
441 > * Does a best possible effort to clean a data object from any possible PII.
442 > * @param data The data object to clean
443 > * @param paths Any additional patterns that should be removed from the data set
444 > * @returns A new object with the PII removed
445 > */
446 > export function cleanData(data: ITelemetryData | undefined, cleanUpPatterns: RegExp[]): Record<string, unknown> {
447 if (!data) {
448 return {};
src/vs/platform/telemetry/common/commonProperties.ts 14 introduced LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commonProperties.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { isLinuxSnap, platform, Platform, PlatformToString } from '../../../base/common/platform.js';
7 > import { env, platform as nodePlatform } from '../../../base/common/process.js';
8 > import { generateUuid } from '../../../base/common/uuid.js';
9 > import { ICommonProperties } from './telemetry.js';
10 >
11 function getPlatformDetail(hostname: string): string | undefined {
12 if (platform === Platform.Linux && /^penguin(\.|$)/i.test(hostname)) {
16 return undefined;
17 }
19 > export function resolveCommonProperties(
20 release: string,
21 hostname: string,
97 return result;
98 }
100 > export function verifyMicrosoftInternalDomain(domainList: readonly string[]): boolean {
101 const userDnsDomain = env['USERDNSDOMAIN'];
102 if (!userDnsDomain) {