diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 688400fc6d5c2..05620afadfca4 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -448,10 +448,11 @@ namespace ts { readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** - * This should be cryptographically secure. * A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm) */ createHash?(data: string): string; + /** This must be cryptographically secure. Only implement this method using `crypto.createHash("sha256")`. */ + createSHA256Hash?(data: string): string; getMemoryUsage?(): number; exit(exitCode?: number): void; realpath?(path: string): string; @@ -527,7 +528,7 @@ namespace ts { const _path = require("path"); const _os = require("os"); // crypto can be absent on reduced node installations - let _crypto: any; + let _crypto: typeof import("crypto"); try { _crypto = require("crypto"); } @@ -590,6 +591,7 @@ namespace ts { readDirectory, getModifiedTime, createHash: _crypto ? createMD5HashUsingNativeCrypto : generateDjb2Hash, + createSHA256Hash: _crypto ? createSHA256Hash : undefined, getMemoryUsage() { if (global.gc) { global.gc(); @@ -1072,11 +1074,17 @@ namespace ts { return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`; } - function createMD5HashUsingNativeCrypto(data: string) { + function createMD5HashUsingNativeCrypto(data: string): string { const hash = _crypto.createHash("md5"); hash.update(data); return hash.digest("hex"); } + + function createSHA256Hash(data: string): string { + const hash = _crypto.createHash("sha256"); + hash.update(data); + return hash.digest("hex"); + } } function getChakraSystem(): System { diff --git a/src/harness/unittests/telemetry.ts b/src/harness/unittests/telemetry.ts index e3649ae1c7974..53025e15a9c54 100644 --- a/src/harness/unittests/telemetry.ts +++ b/src/harness/unittests/telemetry.ts @@ -59,7 +59,6 @@ namespace ts.projectSystem { // TODO: Apparently compilerOptions is mutated, so have to repeat it here! et.assertProjectInfoTelemetryEvent({ - projectId: Harness.mockHash("/hunter2/foo.csproj"), compilerOptions: { strict: true }, compileOnSave: true, // These properties can't be present for an external project, so they are undefined instead of false. @@ -69,7 +68,7 @@ namespace ts.projectSystem { exclude: undefined, configFileName: "other", projectType: "external", - }); + }, "/hunter2/foo.csproj"); // Also test that opening an external project only sends an event once. @@ -202,7 +201,6 @@ namespace ts.projectSystem { const et = new TestServerEventManager([jsconfig, file]); et.service.openClientFile(file.path); et.assertProjectInfoTelemetryEvent({ - projectId: Harness.mockHash("/jsconfig.json"), fileStats: fileStats({ js: 1 }), compilerOptions: autoJsCompilerOptions, typeAcquisition: { @@ -211,7 +209,7 @@ namespace ts.projectSystem { exclude: false, }, configFileName: "jsconfig.json", - }); + }, "/jsconfig.json"); }); it("detects whether language service was disabled", () => { @@ -222,7 +220,6 @@ namespace ts.projectSystem { et.service.openClientFile(file.path); et.getEvent(server.ProjectLanguageServiceStateEvent); et.assertProjectInfoTelemetryEvent({ - projectId: Harness.mockHash("/jsconfig.json"), fileStats: fileStats({ js: 1 }), compilerOptions: autoJsCompilerOptions, configFileName: "jsconfig.json", @@ -232,7 +229,7 @@ namespace ts.projectSystem { exclude: false, }, languageServiceEnabled: false, - }); + }, "/jsconfig.json"); }); describe("open files telemetry", () => { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 980a27446f7f1..8524d46e094d8 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -214,9 +214,9 @@ namespace ts.projectSystem { assert.equal(eventData.triggerFile, triggerFile); } - assertProjectInfoTelemetryEvent(partial: Partial, configFile?: string): void { + assertProjectInfoTelemetryEvent(partial: Partial, configFile = "/tsconfig.json"): void { assert.deepEqual(this.getEvent(server.ProjectInfoTelemetryEvent), { - projectId: Harness.mockHash(configFile || "/tsconfig.json"), + projectId: sys.createSHA256Hash(configFile), fileStats: fileStats({ ts: 1 }), compilerOptions: {}, extends: false, diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index ef3d4955e420c..96873f1c08325 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -811,6 +811,10 @@ interface Array {}` return Harness.mockHash(s); } + createSHA256Hash(s: string): string { + return sys.createSHA256Hash(s); + } + watchFile(fileName: string, cb: FileWatcherCallback, pollingInterval: number) { if (this.dynamicPriorityWatchFile) { return this.dynamicPriorityWatchFile(fileName, cb, pollingInterval); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a5a4bceb43ce6..459b4b12d8314 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1429,12 +1429,12 @@ namespace ts.server { } this.seenProjects.set(projectKey, true); - if (!this.eventHandler) { + if (!this.eventHandler || !this.host.createSHA256Hash) { return; } const data: ProjectInfoTelemetryEventData = { - projectId: this.host.createHash(projectKey), + projectId: this.host.createSHA256Hash(projectKey), fileStats: countEachFileTypes(project.getScriptInfos()), compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilationSettings()), typeAcquisition: convertTypeAcquisition(project.getTypeAcquisition()), diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c069dbf4d0473..6a9f6117d396b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2980,10 +2980,11 @@ declare namespace ts { readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** - * This should be cryptographically secure. * A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm) */ createHash?(data: string): string; + /** This must be cryptographically secure. Only implement this method using `crypto.createHash("sha256")`. */ + createSHA256Hash?(data: string): string; getMemoryUsage?(): number; exit(exitCode?: number): void; realpath?(path: string): string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 4887de7fd2fc2..db6ce733f229c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2980,10 +2980,11 @@ declare namespace ts { readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** - * This should be cryptographically secure. * A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm) */ createHash?(data: string): string; + /** This must be cryptographically secure. Only implement this method using `crypto.createHash("sha256")`. */ + createSHA256Hash?(data: string): string; getMemoryUsage?(): number; exit(exitCode?: number): void; realpath?(path: string): string;