From 2aeb99b11da6cd64d54b403a722efc2ea87f7bee Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 2 Sep 2022 12:21:10 +0200 Subject: [PATCH] extract some COI handle into network-util, add `--enable-coi` command line flag and adopt in main handler --- src/vs/base/browser/defaultWorkerFactory.ts | 8 ++- src/vs/base/common/network.ts | 50 +++++++++++++++++++ src/vs/platform/environment/common/argv.ts | 2 + .../environment/common/environment.ts | 2 + .../electron-main/environmentMainService.ts | 3 ++ src/vs/platform/environment/node/argv.ts | 2 + .../electron-main/protocolMainService.ts | 15 ++++-- .../contrib/webview/browser/webviewElement.ts | 7 ++- .../browser/webWorkerExtensionHost.ts | 7 ++- 9 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/vs/base/browser/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index c57e67f3698..9d2a34494be 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { COI } from 'vs/base/common/network'; import { globals } from 'vs/base/common/platform'; import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; @@ -41,7 +42,12 @@ export function getWorkerBootstrapUrl(scriptPath: string, label: string): string const blob = new Blob([js], { type: 'application/javascript' }); return URL.createObjectURL(blob); } - return scriptPath + '#' + label; + + const result = new URL(scriptPath); + COI.addSearchParam(result.searchParams, true, true); + result.hash = label; + return result.href; + } // ESM-comment-end diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 20f6b38e629..cb80e75a71d 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -251,3 +251,53 @@ class FileAccessImpl { } export const FileAccess = new FileAccessImpl(); + + +export namespace COI { + + const coiHeaders = new Map<'3' | '2' | '1' | string, Record>([ + ['1', { 'Cross-Origin-Opener-Policy': 'same-origin' }], + ['2', { 'Cross-Origin-Embedder-Policy': 'require-corp' }], + ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' }], + ]); + + export const CoopAndCoep = Object.freeze(coiHeaders.get('3')); + + const coiSearchParamName = 'vscode-coi'; + + /** + * Extract desired headers from `vscode-coi` invocation + */ + export function getHeadersFromQuery(url: string | URI | URL): Record | undefined { + let params: URLSearchParams | undefined; + if (typeof url === 'string') { + params = new URL(url).searchParams; + } else if (url instanceof URL) { + params = url.searchParams; + } else if (URI.isUri(url)) { + params = new URL(url.toString(true)).searchParams; + } + const value = params?.get(coiSearchParamName); + if (!value) { + return undefined; + } + return coiHeaders.get(value); + } + + /** + * Add the `vscode-coi` query attribute based on wanting `COOP` and `COEP`. Will be a noop when `crossOriginIsolated` + * isn't enabled the current context + */ + export function addSearchParam(urlOrSearch: URLSearchParams | Record, coop: boolean, coep: boolean): void { + if (!globalThis.crossOriginIsolated) { + // depends on the current context being COI + return; + } + const value = coop && coep ? '3' : coep ? '2' : '1'; + if (urlOrSearch instanceof URLSearchParams) { + urlOrSearch.set(coiSearchParamName, value); + } else { + (>urlOrSearch)[coiSearchParamName] = value; + } + } +} diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 7593031898e..73754ba6042 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -94,6 +94,8 @@ export interface NativeParsedArgs { 'profile'?: string; 'profile-temp'?: boolean; + 'enable-coi'?: boolean; + // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; 'no-sandbox'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index c5221639ac2..faf55adc736 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -140,6 +140,8 @@ export interface INativeEnvironmentService extends IEnvironmentService { // --- use keytar for credentials disableKeytar?: boolean; + crossOriginIsolated?: boolean; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 53aabe4147c..69d97499a31 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -60,6 +60,9 @@ export class EnvironmentMainService extends NativeEnvironmentService implements @memoize get disableKeytar(): boolean { return !!this.args['disable-keytar']; } + @memoize + get crossOriginIsolated(): boolean { return !!this.args['enable-coi']; } + @memoize get codeCachePath(): string | undefined { return process.env['VSCODE_CODE_CACHE_PATH'] || undefined; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 29ac8f8aa35..d5167a39273 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -131,6 +131,8 @@ export const OPTIONS: OptionDescriptions> = { 'editSessionId': { type: 'string' }, 'locate-shell-integration-path': { type: 'string', args: ['bash', 'pwsh', 'zsh', 'fish'] }, + 'enable-coi': { type: 'boolean' }, + // chromium flags 'no-proxy-server': { type: 'boolean' }, // Minimist incorrectly parses keys that start with `--no` diff --git a/src/vs/platform/protocol/electron-main/protocolMainService.ts b/src/vs/platform/protocol/electron-main/protocolMainService.ts index 8a21163e8f2..4ebb88fe739 100644 --- a/src/vs/platform/protocol/electron-main/protocolMainService.ts +++ b/src/vs/platform/protocol/electron-main/protocolMainService.ts @@ -7,7 +7,7 @@ import { session } from 'electron'; import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; -import { FileAccess, Schemas } from 'vs/base/common/network'; +import { COI, FileAccess, Schemas } from 'vs/base/common/network'; import { extname, normalize } from 'vs/base/common/path'; import { isLinux } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -27,7 +27,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ private readonly validExtensions = new Set(['.svg', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']); // https://github.com/microsoft/vscode/issues/119384 constructor( - @INativeEnvironmentService environmentService: INativeEnvironmentService, + @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @ILogService private readonly logService: ILogService ) { @@ -94,9 +94,18 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ private handleResourceRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback): void { const path = this.requestToNormalizedFilePath(request); + let headers: Record | undefined; + if (this.environmentService.crossOriginIsolated) { + if (path.endsWith('/workbench.html')) { + headers = COI.CoopAndCoep; + } else { + headers = COI.getHeadersFromQuery(request.url); + } + } + // first check by validRoots if (this.validRoots.findSubstr(path)) { - return callback({ path }); + return callback({ path, headers }); } // then check by validExtensions diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 08cc8253839..90948ab4008 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -12,7 +12,7 @@ import { streamToBuffer, VSBufferReadableStream } from 'vs/base/common/buffer'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; +import { COI, Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { localize } from 'vs/nls'; @@ -532,9 +532,8 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD params.purpose = options.purpose; } - if (globalThis.crossOriginIsolated) { - params['vscode-coi'] = '3'; /*COOP+COEP*/ - } + + COI.addSearchParam(params, true, true); const queryString = new URLSearchParams(params).toString(); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 00e360f20c2..eb70bb9ff17 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -27,7 +27,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { canceled, onUnexpectedError } from 'vs/base/common/errors'; import { Barrier } from 'vs/base/common/async'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { FileAccess } from 'vs/base/common/network'; +import { COI, FileAccess } from 'vs/base/common/network'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { parentOriginHash } from 'vs/workbench/browser/webview'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -86,9 +86,8 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost if (this._environmentService.debugExtensionHost && this._environmentService.debugRenderer) { suffixSearchParams.set('debugged', '1'); } - if (globalThis.crossOriginIsolated) { - suffixSearchParams.set('vscode-coi', '3' /*COOP+COEP*/); - } + COI.addSearchParam(suffixSearchParams, true, true); + const suffix = `?${suffixSearchParams.toString()}`; const iframeModulePath = 'vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html';