diff --git a/CHANGELOG.md b/CHANGELOG.md index ddc0924..1f76bf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ # Changelog - +## 0.0.28 + * new option `--coi` to enable cross origin isolation. ## 0.0.22 - * new option `--printServerLog` replacing `--hideServerLog` - * new option `--browser` replacing `--browserType` + * new option `--printServerLog` replacing `--hideServerLog`. + * new option `--browser` replacing `--browserType`. ## 0.0.20 - * new option `--extensionId publisher.name[@prerelease]` to include one or more extensions + * new option `--extensionId publisher.name[@prerelease]` to include one or more extensions. ## 0.0.18 * new option `--browserType none` to start the server without opening a browser. @@ -17,7 +18,7 @@ ## 0.0.16 * new option `--sourcesPath`: If provided, runs the server from VS Code sources at the given location. - * option `--version` is deprecated and replaced with `quality`. Supported values: `stable`, `insiders`. Instead of `sources` use `--insiders` + * option `--version` is deprecated and replaced with `quality`. Supported values: `stable`, `insiders`. Instead of `sources` use `--insiders`. ## 0.0.14 * new option `--extensionPath` : A path pointing to a folder containing additional extensions to include. Argument can be provided multiple times. diff --git a/README.md b/README.md index 61465a1..0787d01 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ CLI options: | --sourcesPath | If set, runs the server from VS Code sources located at the given path. Make sure the sources and extensions are compiled (`yarn compile` and `yarn compile-web`). | | --headless | If set, hides the browser. Defaults to true when an extensionTestsPath is provided, otherwise false. | | --permission | Permission granted to the opened browser: e.g. `clipboard-read`, `clipboard-write`. See [full list of options](https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions). Argument can be provided multiple times. | +| --coi | If set, enables cross origin isolation. Defaults to false. | | --folder-uri | URI of the workspace to open VS Code on. Ignored when `folderPath` is provided. | | --extensionPath | A path pointing to a folder containing additional extensions to include. Argument can be provided multiple times. | | --extensionId | The id of an extension include. The format is `${publisher}.${name}`. Append `@prerelease` to use the prerelease version. | diff --git a/package.json b/package.json index 4d74d8b..27c3df3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/test-web", - "version": "0.0.27", + "version": "0.0.28", "scripts": { "install-extensions": "yarn --cwd=fs-provider && yarn --cwd=sample", "compile": "tsc -p ./ && yarn compile-fs-provider", diff --git a/src/index.ts b/src/index.ts index 7f771d9..619b1ec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -70,6 +70,11 @@ export interface Options { */ headless?: boolean; + /** + * If set, opens the page with cross origin isolation enabled. + */ + coi?: boolean; + /** * @deprecated. Use `printServerLog` instead. */ @@ -154,7 +159,8 @@ export async function runTests(options: Options & { extensionTestsPath: string } folderMountPath: options.folderPath, printServerLog: options.printServerLog ?? options.hideServerLog === false, extensionPaths: options.extensionPaths, - extensionIds: options.extensionIds + extensionIds: options.extensionIds, + coi: !!options.coi }; @@ -212,7 +218,8 @@ export async function open(options: Options): Promise { folderMountPath: options.folderPath, printServerLog: options.printServerLog ?? options.hideServerLog === false, extensionPaths: options.extensionPaths, - extensionIds: options.extensionIds + extensionIds: options.extensionIds, + coi: !!options.coi }; const host = options.host ?? 'localhost'; @@ -487,6 +494,7 @@ interface CommandLineOptions { host?: string; port?: string; verbose?: boolean; + coi?: boolean; help?: boolean; } @@ -500,6 +508,7 @@ function showHelp() { console.log(` --open-devtools: If set, opens the dev tools. [Optional]`); console.log(` --headless: Whether to hide the browser. Defaults to true when an extensionTestsPath is provided, otherwise false. [Optional]`); console.log(` --permission: Permission granted in the opened browser: e.g. 'clipboard-read', 'clipboard-write'. [Optional, Multiple]`); + console.log(` --coi: Enables cross origin isolation [Optional]`); console.log(` --folder-uri: workspace to open VS Code on. Ignored when folderPath is provided. [Optional]`); console.log(` --extensionPath: A path pointing to a folder containing additional extensions to include [Optional, Multiple]`); console.log(` --extensionId: The id of an extension include. The format is '\${publisher}.\${name}'. Append '@prerelease' to use a prerelease version [Optional, Multiple]`); @@ -518,7 +527,7 @@ async function cliMain(): Promise { const options: minimist.Opts = { string: ['extensionDevelopmentPath', 'extensionTestsPath', 'browser', 'browserType', 'quality', 'version', 'waitForDebugger', 'folder-uri', 'permission', 'extensionPath', 'extensionId', 'sourcesPath', 'host', 'port'], - boolean: ['open-devtools', 'headless', 'hideServerLog', 'printServerLog', 'help', 'verbose'], + boolean: ['open-devtools', 'headless', 'hideServerLog', 'printServerLog', 'help', 'verbose', 'coi'], unknown: arg => { if (arg.startsWith('-')) { console.log(`Unknown argument ${arg}`); @@ -548,6 +557,7 @@ async function cliMain(): Promise { const verbose = validateBooleanOrUndefined(args, 'verbose'); const port = validatePortNumber(args.port); const host = validateStringOrUndefined(args, 'host'); + const coi = validateBooleanOrUndefined(args, 'coi'); const waitForDebugger = validatePortNumber(args.waitForDebugger); @@ -583,6 +593,7 @@ async function cliMain(): Promise { extensionIds, vsCodeDevPath, verbose, + coi, host, port }).catch(e => { @@ -605,6 +616,7 @@ async function cliMain(): Promise { extensionIds, vsCodeDevPath, verbose, + coi, host, port }) diff --git a/src/server/app.ts b/src/server/app.ts index 1bbf3d2..28668cb 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -28,6 +28,7 @@ export default async function createApp(config: IConfig): Promise { credentials: true, origin: (ctx: Koa.Context) => { if ( + /^https:\/\/[^.]+\.vscode-cdn\.net$/.test(ctx.get('Origin')) || // needed for the webviewContent /^https:\/\/[^.]+\.vscode-webview\.net$/.test(ctx.get('Origin')) ) { return ctx.get('Origin'); @@ -38,9 +39,9 @@ export default async function createApp(config: IConfig): Promise { }) ); - // this is here such that the iframe worker can fetch the extension files + // CSP: frame-ancestors app.use((ctx, next) => { - ctx.set('Access-Control-Allow-Origin', '*'); + ctx.set('Content-Security-Policy', `frame-ancestors 'none'`); return next(); }); diff --git a/src/server/main.ts b/src/server/main.ts index d5cb1a1..1cba41a 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -14,6 +14,7 @@ export interface IConfig { readonly folderUri: string | undefined; readonly folderMountPath: string | undefined; readonly printServerLog: boolean; + readonly coi: boolean; } export interface GalleryExtensionInfo { diff --git a/src/server/workbench.ts b/src/server/workbench.ts index 895c6de..9a25d3a 100644 --- a/src/server/workbench.ts +++ b/src/server/workbench.ts @@ -145,6 +145,10 @@ export default function (config: IConfig): Router.Middleware { router.get('/', async ctx => { const options = await getWorkbenchOptions(ctx, config); ctx.body = await ctx.state.workbench.render(options); + if (config.coi) { + ctx.set('Cross-Origin-Opener-Policy', 'same-origin'); + ctx.set('Cross-Origin-Embedder-Policy', 'require-corp'); + } }); return router.routes();