Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/integrity/electron-browser/integrityService.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
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 { localize } from '../../../../nls.js';
7
import Severity from '../../../../base/common/severity.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { ChecksumPair, IIntegrityService, IntegrityTestResult } from '../common/integrity.js';
10
import { ILifecycleService, LifecyclePhase } from '../../lifecycle/common/lifecycle.js';
11
import { IProductService } from '../../../../platform/product/common/productService.js';
12
import { INotificationService, NotificationPriority } from '../../../../platform/notification/common/notification.js';
13
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
14
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
15
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
16
import { FileAccess, AppResourcePath } from '../../../../base/common/network.js';
17
import { IChecksumService } from '../../../../platform/checksum/common/checksumService.js';
18
import { ILogService } from '../../../../platform/log/common/log.js';
19
20
interface IStorageData {
21
readonly dontShowPrompt: boolean;
22
readonly commit: string | undefined;
23
}
24
25
class IntegrityStorage {
26
27
private static readonly KEY = 'integrityService';
28
29
private value: IStorageData | null;
30
31
constructor(private readonly storageService: IStorageService) {
32
this.value = this._read();
33
}
34
35
private _read(): IStorageData | null {
36
const jsonValue = this.storageService.get(IntegrityStorage.KEY, StorageScope.APPLICATION);
37
if (!jsonValue) {
38
return null;
39
}
40
41
try {
42
return JSON.parse(jsonValue);
43
} catch (err) {
44
return null;
45
}
46
}
47
48
get(): IStorageData | null {
49
return this.value;
50
}
51
52
set(data: IStorageData | null): void {
53
this.value = data;
54
this.storageService.store(IntegrityStorage.KEY, JSON.stringify(this.value), StorageScope.APPLICATION, StorageTarget.MACHINE);
55
}
56
}
57
58
export class IntegrityService implements IIntegrityService {
59
60
declare readonly _serviceBrand: undefined;
61
62
private readonly storage: IntegrityStorage;
63
64
private readonly isPurePromise: Promise<IntegrityTestResult>;
65
isPure(): Promise<IntegrityTestResult> { return this.isPurePromise; }
66
67
constructor(
68
@INotificationService private readonly notificationService: INotificationService,
69
@IStorageService storageService: IStorageService,
70
@ILifecycleService private readonly lifecycleService: ILifecycleService,
71
@IOpenerService private readonly openerService: IOpenerService,
72
@IProductService private readonly productService: IProductService,
73
@IChecksumService private readonly checksumService: IChecksumService,
74
@ILogService private readonly logService: ILogService
75
) {
76
this.storage = new IntegrityStorage(storageService);
77
this.isPurePromise = this._isPure();
78
79
this._compute();
80
}
81
82
private async _compute(): Promise<void> {
83
const { isPure } = await this.isPure();
84
if (isPure) {
85
return; // all is good
86
}
87
88
this.logService.warn(`
89
90
----------------------------------------------
91
*** Installation has been modified on disk ***
92
----------------------------------------------
93
94
`);
95
96
const storedData = this.storage.get();
97
if (storedData?.dontShowPrompt && storedData.commit === this.productService.commit) {
98
return; // Do not prompt
99
}
100
101
this._showNotification();
102
}
103
104
private async _isPure(): Promise<IntegrityTestResult> {
105
const expectedChecksums = this.productService.checksums || {};
106
107
await this.lifecycleService.when(LifecyclePhase.Eventually);
108
109
const allResults = await Promise.all(Object.keys(expectedChecksums).map(filename => this._resolve(<AppResourcePath>filename, expectedChecksums[filename])));
110
111
let isPure = true;
112
for (let i = 0, len = allResults.length; i < len; i++) {
113
if (!allResults[i].isPure) {
114
isPure = false;
115
break;
116
}
117
}
118
119
return {
120
isPure,
121
proof: allResults
122
};
123
}
124
125
private async _resolve(filename: AppResourcePath, expected: string): Promise<ChecksumPair> {
126
const fileUri = FileAccess.asFileUri(filename);
127
128
try {
129
const checksum = await this.checksumService.checksum(fileUri);
130
131
return IntegrityService._createChecksumPair(fileUri, checksum, expected);
132
} catch (error) {
133
return IntegrityService._createChecksumPair(fileUri, '', expected);
134
}
135
}
136
137
private static _createChecksumPair(uri: URI, actual: string, expected: string): ChecksumPair {
138
return {
139
uri: uri,
140
actual: actual,
141
expected: expected,
142
isPure: (actual === expected)
143
};
144
}
145
146
private _showNotification(): void {
147
const checksumFailMoreInfoUrl = this.productService.checksumFailMoreInfoUrl;
148
const message = localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", this.productService.nameShort);
149
if (checksumFailMoreInfoUrl) {
150
this.notificationService.prompt(
151
Severity.Warning,
152
message,
153
[
154
{
155
label: localize('integrity.moreInformation', "More Information"),
156
run: () => this.openerService.open(URI.parse(checksumFailMoreInfoUrl))
157
},
158
{
159
label: localize('integrity.dontShowAgain', "Don't Show Again"),
160
isSecondary: true,
161
run: () => this.storage.set({ dontShowPrompt: true, commit: this.productService.commit })
162
}
163
],
164
{
165
sticky: true,
166
priority: NotificationPriority.URGENT
167
}
168
);
169
} else {
170
this.notificationService.notify({
171
severity: Severity.Warning,
172
message,
173
sticky: true,
174
priority: NotificationPriority.URGENT
175
});
176
}
177
}
178
}
179
180
registerSingleton(IIntegrityService, IntegrityService, InstantiationType.Delayed);
181
182