workspaceTrust.ts ×61

Frontier kind: Code frontier

unlabeled · c_11eeba43ad7c

47 tests · 16766 LOC · 79 files · introduces 0 tests · 258 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
61 ranges258 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2096 ranges16766 lines · 79 files · Browse complete extent
All tests (intent)
47 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.

1 file ranked by introduced lines: 258 introduced LOC across 61 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/workspaces/common/workspaceTrust.ts 258 introduced LOC · 61 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workspaceTrust.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 { Emitter, Event } from '../../../../base/common/event.js';
7 > import { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
8 > import { LinkedList } from '../../../../base/common/linkedList.js';
9 > import { Schemas } from '../../../../base/common/network.js';
10 > import { URI } from '../../../../base/common/uri.js';
11 > import { IPath } from '../../../../platform/window/common/window.js';
12 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
13 > import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
14 > import { IRemoteAuthorityResolverService, ResolverResult } from '../../../../platform/remote/common/remoteAuthorityResolver.js';
15 > import { getRemoteAuthority } from '../../../../platform/remote/common/remoteHosts.js';
16 > import { isVirtualResource } from '../../../../platform/workspace/common/virtualWorkspace.js';
17 > import { AGENT_HOST_SCHEME } from '../../../../platform/agentHost/common/agentHostUri.js';
18 > import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
19 > import { ISingleFolderWorkspaceIdentifier, isSavedWorkspace, isSingleFolderWorkspaceIdentifier, isTemporaryWorkspace, IWorkspace, IWorkspaceContextService, IWorkspaceFolder, toWorkspaceIdentifier, WorkbenchState } from '../../../../platform/workspace/common/workspace.js';
20 > import { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorkspaceTrustInfo, IWorkspaceTrustUriInfo, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, WorkspaceTrustUriResponse, IWorkspaceTrustEnablementService, ResourceTrustRequestOptions } from '../../../../platform/workspace/common/workspaceTrust.js';
21 > import { Memento } from '../../../common/memento.js';
22 > import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
23 > import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
24 > import { isEqualAuthority } from '../../../../base/common/resources.js';
25 > import { isWeb } from '../../../../base/common/platform.js';
26 > import { IFileService } from '../../../../platform/files/common/files.js';
27 > import { promiseWithResolvers } from '../../../../base/common/async.js';
28 > import { ResourceMap } from '../../../../base/common/map.js';
29 >
30 > export const WORKSPACE_TRUST_ENABLED = 'security.workspace.trust.enabled';
31 > export const WORKSPACE_TRUST_STARTUP_PROMPT = 'security.workspace.trust.startupPrompt';
32 > export const WORKSPACE_TRUST_BANNER = 'security.workspace.trust.banner';
33 > export const WORKSPACE_TRUST_UNTRUSTED_FILES = 'security.workspace.trust.untrustedFiles';
34 > export const WORKSPACE_TRUST_EMPTY_WINDOW = 'security.workspace.trust.emptyWindow';
35 > export const WORKSPACE_TRUST_EXTENSION_SUPPORT = 'extensions.supportUntrustedWorkspaces';
36 > export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key';
37 >
38 > export class CanonicalWorkspace implements IWorkspace {
39 > constructor(
40 private readonly originalWorkspace: IWorkspace,
41 private readonly canonicalFolderUris: URI[],
42 private readonly canonicalConfiguration: URI | null | undefined
43 ) { }
45 >
46 > get folders(): IWorkspaceFolder[] {
47 return this.originalWorkspace.folders.map((folder, index) => {
48 return {
54 });
55 }
57 > get transient(): boolean | undefined {
58 return this.originalWorkspace.transient;
59 }
61 > get configuration(): URI | null | undefined {
62 return this.canonicalConfiguration ?? this.originalWorkspace.configuration;
63 }
65 > get id(): string {
66 return this.originalWorkspace.id;
67 }
69 >
70 > export class WorkspaceTrustEnablementService extends Disposable implements IWorkspaceTrustEnablementService {
71 >
72 > _serviceBrand: undefined;
73 >
74 > constructor(
75 @IConfigurationService private readonly configurationService: IConfigurationService,
76 @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
78 super();
79 }
81 > isWorkspaceTrustEnabled(): boolean {
82 if (this.environmentService.disableWorkspaceTrust) {
83 return false;
86 return !!this.configurationService.getValue(WORKSPACE_TRUST_ENABLED);
87 }
89 >
90 > export class WorkspaceTrustManagementService extends Disposable implements IWorkspaceTrustManagementService {
91 >
92 > _serviceBrand: undefined;
93 >
94 > private readonly storageKey = WORKSPACE_TRUST_STORAGE_KEY;
95 >
96 > private readonly _workspaceResolvedPromise: Promise<void>;
97 > private readonly _workspaceResolvedPromiseResolve: () => void;
98 > private readonly _workspaceTrustInitializedPromise: Promise<void>;
99 > private readonly _workspaceTrustInitializedPromiseResolve: () => void;
100 >
101 > private readonly _onDidChangeTrust = this._register(new Emitter<boolean>());
102 > readonly onDidChangeTrust = this._onDidChangeTrust.event;
103 >
104 > private readonly _onDidChangeTrustedFolders = this._register(new Emitter<void>());
105 > readonly onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event;
106 >
107 > private _canonicalStartupFiles: URI[] = [];
108 > private _canonicalWorkspace: IWorkspace;
109 > private _canonicalUrisResolved: boolean;
110 >
111 > private _isTrusted: boolean;
112 > private _trustStateInfo: IWorkspaceTrustInfo;
113 > private _remoteAuthority: ResolverResult | undefined;
114 >
115 > private readonly _storedTrustState: WorkspaceTrustMemento;
116 > private readonly _trustTransitionManager: WorkspaceTrustTransitionManager;
117 >
118 > constructor(
119 @IConfigurationService private readonly configurationService: IConfigurationService,
120 @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
143 this.registerListeners();
144 }
146 > //#region initialize
147 >
148 > private initializeWorkspaceTrust(): void {
149 // Resolve canonical Uris
150 this.resolveCanonicalUris()
183 }
184 }
186 > //#endregion
187 >
188 > //#region private interface
189 >
190 > private registerListeners(): void {
191 this._register(this.workspaceService.onDidChangeWorkspaceFolders(async () => await this.updateWorkspaceTrust()));
192 this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION_SHARED, this.storageKey, this._store)(async () => {
200 }));
201 }
203 > private async getCanonicalUri(uri: URI): Promise<URI> {
204 let canonicalUri = uri;
205 if (this.environmentService.remoteAuthority && uri.scheme === Schemas.vscodeRemote) {
215 return canonicalUri.with({ query: null, fragment: null });
216 }
218 > private async resolveCanonicalUris(): Promise<void> {
219 // Open editors
220 const filesToOpen: IPath[] = [];
249 this._canonicalWorkspace = new CanonicalWorkspace(this.workspaceService.getWorkspace(), canonicalWorkspaceFolders, canonicalWorkspaceConfiguration);
250 }
252 > private loadTrustInfo(): IWorkspaceTrustInfo {
253 const infoAsString = this.storageService.get(this.storageKey, StorageScope.APPLICATION_SHARED);
254
275 return result;
276 }
278 > private async saveTrustInfo(): Promise<void> {
279 this.storageService.store(this.storageKey, JSON.stringify(this._trustStateInfo), StorageScope.APPLICATION_SHARED, StorageTarget.MACHINE);
280 this._onDidChangeTrustedFolders.fire();
282 await this.updateWorkspaceTrust();
283 }
285 > private getWorkspaceUris(): URI[] {
286 const workspaceUris = this._canonicalWorkspace.folders.map(f => f.uri);
287 const workspaceConfiguration = this._canonicalWorkspace.configuration;
292 return workspaceUris;
293 }
295 > private calculateWorkspaceTrust(): boolean {
296 // Feature is disabled
297 if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled()) {
327 return this.getUrisTrust(this.getWorkspaceUris());
328 }
330 > private async updateWorkspaceTrust(trusted?: boolean): Promise<void> {
331 if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled()) {
332 return;
349 this._onDidChangeTrust.fire(trusted);
350 }
352 > private getUrisTrust(uris: URI[]): boolean {
353 let state = true;
354 for (const uri of uris) {
363 return state;
364 }
366 > private doGetUriTrustInfo(uri: URI): IWorkspaceTrustUriInfo {
367 // Return trusted when workspace trust is disabled
368 if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled()) {
401 return { trusted: resultState, uri: resultUri };
402 }
404 > private async doSetUrisTrust(uris: URI[], trusted: boolean): Promise<void> {
405 let changed = false;
406
433 }
434 }
436 > private isEmptyWorkspace(): boolean {
437 if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
438 return true;
446 return false;
447 }
449 > private isTrustedVirtualResource(uri: URI): boolean {
450 // `vscode-vfs` (e.g. GitHub Repositories) and `vscode-agent-host`
451 // (remote agent host folders) represent real, writable resources where
454 return isVirtualResource(uri) && uri.scheme !== 'vscode-vfs' && uri.scheme !== AGENT_HOST_SCHEME;
455 }
457 > private isTrustedByRemote(uri: URI): boolean {
458 if (!this.environmentService.remoteAuthority) {
459 return false;
466 return (isEqualAuthority(getRemoteAuthority(uri), this._remoteAuthority.authority.authority)) && !!this._remoteAuthority.options?.isTrusted;
467 }
469 > private set isTrusted(value: boolean) {
470 this._isTrusted = value;
471
480 }
481 }
483 > //#endregion
484 >
485 > //#region public interface
486 >
487 > get workspaceResolved(): Promise<void> {
488 return this._workspaceResolvedPromise;
489 }
491 > get workspaceTrustInitialized(): Promise<void> {
492 return this._workspaceTrustInitializedPromise;
493 }
495 > get acceptsOutOfWorkspaceFiles(): boolean {
496 return this._storedTrustState.acceptsOutOfWorkspaceFiles;
497 }
499 > set acceptsOutOfWorkspaceFiles(value: boolean) {
500 this._storedTrustState.acceptsOutOfWorkspaceFiles = value;
501 }
503 > isWorkspaceTrusted(): boolean {
504 return this._isTrusted;
505 }
507 > isWorkspaceTrustForced(): boolean {
508 // Remote - remote authority explicitly sets workspace trust
509 if (this.environmentService.remoteAuthority && this._remoteAuthority?.options?.isTrusted !== undefined) {
519 return false;
520 }
522 > canSetParentFolderTrust(): boolean {
523 const workspaceIdentifier = toWorkspaceIdentifier(this._canonicalWorkspace);
524
538 return true;
539 }
541 > async setParentFolderTrust(trusted: boolean): Promise<void> {
542 if (this.canSetParentFolderTrust()) {
543 const workspaceUri = (toWorkspaceIdentifier(this._canonicalWorkspace) as ISingleFolderWorkspaceIdentifier).uri;
547 }
548 }
550 > canSetWorkspaceTrust(): boolean {
551 // Remote - remote authority not yet resolved, or remote authority explicitly sets workspace trust
552 if (this.environmentService.remoteAuthority && (!this._remoteAuthority || this._remoteAuthority.options?.isTrusted !== undefined)) {
599 return true;
600 }
602 > async setWorkspaceTrust(trusted: boolean): Promise<void> {
603 // Empty workspace
604 if (this.isEmptyWorkspace()) {
610 await this.setUrisTrust(workspaceFolders, trusted);
611 }
613 > async getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo> {
614 // Return trusted when workspace trust is disabled
615 if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled()) {
624 return this.doGetUriTrustInfo(await this.getCanonicalUri(uri));
625 }
627 > async setUrisTrust(uris: URI[], trusted: boolean): Promise<void> {
628 this.doSetUrisTrust(await Promise.all(uris.map(uri => this.getCanonicalUri(uri))), trusted);
629 }
631 > getTrustedUris(): URI[] {
632 return this._trustStateInfo.uriTrustInfo.map(info => info.uri);
633 }
635 > async setTrustedUris(uris: URI[]): Promise<void> {
636 this._trustStateInfo.uriTrustInfo = [];
637 for (const uri of uris) {
658 await this.saveTrustInfo();
659 }
661 > addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {
662 return this._trustTransitionManager.addWorkspaceTrustTransitionParticipant(participant);
663 }
665 > //#endregion
666 > }
667 >
668 > export class WorkspaceTrustRequestService extends Disposable implements IWorkspaceTrustRequestService {
669 > _serviceBrand: undefined;
670 >
671 > private _openFilesTrustRequestPromise?: Promise<WorkspaceTrustUriResponse>;
672 > private _openFilesTrustRequestResolver?: (response: WorkspaceTrustUriResponse) => void;
673 >
674 > private readonly _resourcesTrustRequestPromises = new ResourceMap<Promise<boolean | undefined>>();
675 > private readonly _resourcesTrustRequestResolvers = new ResourceMap<(trusted: boolean | undefined) => void>();
676 >
677 > private _workspaceTrustRequestPromise?: Promise<boolean | undefined>;
678 > private _workspaceTrustRequestResolver?: (trusted: boolean | undefined) => void;
679 >
680 > private readonly _onDidInitiateOpenFilesTrustRequest = this._register(new Emitter<void>());
681 > readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event;
682 >
683 > private readonly _onDidInitiateResourcesTrustRequest = this._register(new Emitter<ResourceTrustRequestOptions>());
684 > readonly onDidInitiateResourcesTrustRequest = this._onDidInitiateResourcesTrustRequest.event;
685 >
686 > private readonly _onDidInitiateWorkspaceTrustRequest = this._register(new Emitter<WorkspaceTrustRequestOptions | undefined>());
687 > readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event;
688 >
689 > private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter<void>());
690 > readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event;
691 >
692 > constructor(
693 @IConfigurationService private readonly configurationService: IConfigurationService,
694 @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService
696 super();
697 }
699 > //#region Open file(s) trust request
700 >
701 > private get untrustedFilesSetting(): 'prompt' | 'open' | 'newWindow' {
702 return this.configurationService.getValue(WORKSPACE_TRUST_UNTRUSTED_FILES);
703 }
705 > private set untrustedFilesSetting(value: 'prompt' | 'open' | 'newWindow') {
706 this.configurationService.updateValue(WORKSPACE_TRUST_UNTRUSTED_FILES, value);
707 }
709 > async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse?: boolean): Promise<void> {
710 if (!this._openFilesTrustRequestResolver) {
711 return;
734 this._openFilesTrustRequestPromise = undefined;
735 }
737 > async requestOpenFilesTrust(uris: URI[]): Promise<WorkspaceTrustUriResponse> {
738 // If workspace is untrusted, there is no conflict
739 if (!this.workspaceTrustManagementService.isWorkspaceTrusted()) {
776 return this._openFilesTrustRequestPromise;
777 }
779 > //#endregion
780 >
781 > //#region Resource(s) trust request
782 >
783 > async completeResourcesTrustRequest(uri: URI, result: WorkspaceTrustUriResponse): Promise<void> {
784 const resolver = this._resourcesTrustRequestResolvers.get(uri);
785 if (!resolver) {
795 this._resourcesTrustRequestPromises.delete(uri);
796 }
798 > async requestResourcesTrust(options: ResourceTrustRequestOptions): Promise<boolean | undefined> {
799 // Check if all resources are already trusted
800 const resourcesTrustInfo = await this.workspaceTrustManagementService.getUriTrustInfo(options.uri);
818 return promise;
819 }
821 > //#endregion
822 >
823 > //#region Workspace trust request
824 >
825 > private resolveWorkspaceTrustRequest(trusted?: boolean): void {
826 if (this._workspaceTrustRequestResolver) {
827 this._workspaceTrustRequestResolver(trusted ?? this.workspaceTrustManagementService.isWorkspaceTrusted());
831 }
832 }
834 > cancelWorkspaceTrustRequest(): void {
835 if (this._workspaceTrustRequestResolver) {
836 this._workspaceTrustRequestResolver(undefined);
840 }
841 }
843 > async completeWorkspaceTrustRequest(trusted?: boolean): Promise<void> {
844 if (trusted === undefined || trusted === this.workspaceTrustManagementService.isWorkspaceTrusted()) {
845 this.resolveWorkspaceTrustRequest(trusted);
853 await this.workspaceTrustManagementService.setWorkspaceTrust(trusted);
854 }
856 > async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined> {
857 // Trusted workspace
858 if (this.workspaceTrustManagementService.isWorkspaceTrusted()) {
874 return this._workspaceTrustRequestPromise;
875 }
877 > requestWorkspaceTrustOnStartup(): void {
878 if (!this._workspaceTrustRequestPromise) {
879 // Create promise
885 this._onDidInitiateWorkspaceTrustRequestOnStartup.fire();
886 }
888 > //#endregion
889 > }
890 >
891 class WorkspaceTrustTransitionManager extends Disposable {
892
893 private readonly participants = new LinkedList<IWorkspaceTrustTransitionParticipant>();
895 > addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {
896 const remove = this.participants.push(participant);
897 return toDisposable(() => remove());
898 }
900 > async participate(trusted: boolean): Promise<void> {
901 for (const participant of this.participants) {
902 await participant.participate(trusted);
903 }
904 }
906 > override dispose(): void {
907 this.participants.clear();
908 super.dispose();
909 }
911 >
912 > interface WorkspaceTrustMementoData {
913 > acceptsOutOfWorkspaceFiles?: boolean;
914 > isEmptyWorkspaceTrusted?: boolean | undefined;
915 > }
916 >
917 > class WorkspaceTrustMemento {
918 >
919 > private readonly _memento?: Memento<WorkspaceTrustMementoData>;
920 > private readonly _mementoObject: WorkspaceTrustMementoData;
921 >
922 > private readonly _acceptsOutOfWorkspaceFilesKey = 'acceptsOutOfWorkspaceFiles';
923 > private readonly _isEmptyWorkspaceTrustedKey = 'isEmptyWorkspaceTrusted';
924 >
925 > constructor(storageService?: IStorageService) {
926 if (storageService) {
927 this._memento = new Memento('workspaceTrust', storageService);
931 }
932 }
934 > get acceptsOutOfWorkspaceFiles(): boolean {
935 return this._mementoObject[this._acceptsOutOfWorkspaceFilesKey] ?? false;
936 }
938 > set acceptsOutOfWorkspaceFiles(value: boolean) {
939 this._mementoObject[this._acceptsOutOfWorkspaceFilesKey] = value;
940
941 this._memento?.saveMemento();
942 }
944 > get isEmptyWorkspaceTrusted(): boolean | undefined {
945 return this._mementoObject[this._isEmptyWorkspaceTrustedKey];
946 }
948 > set isEmptyWorkspaceTrusted(value: boolean | undefined) {
949 this._mementoObject[this._isEmptyWorkspaceTrustedKey] = value;
950
951 this._memento?.saveMemento();
952 }
954 >
955 > registerSingleton(IWorkspaceTrustRequestService, WorkspaceTrustRequestService, InstantiationType.Delayed);