diff --git a/packages/mini-browser/README.md b/packages/mini-browser/README.md index a00bf0bbc0ad0..faa2bd6eff656 100644 --- a/packages/mini-browser/README.md +++ b/packages/mini-browser/README.md @@ -14,12 +14,20 @@ The `@theia/mini-browser` extension provides a browser widget with the corresponding backend endpoints. -### Environment Variables +## Environment Variables - `THEIA_MINI_BROWSER_HOST_PATTERN` - - A string pattern possibly containing `{{hostname}}` which will be replaced. This is the host for which the `mini-browser` will serve. - - It is a good practice to host the `mini-browser` handlers on a sub-domain as it is more secure. - - Defaults to `{{uuid}}.mini-browser.{{hostname}}`. + + A string pattern possibly containing `{{uuid}}` and `{{hostname}}` which will be replaced. This is the host for which the `mini-browser` will serve. + It is a good practice to host the `mini-browser` handlers on a sub-domain as it is more secure. + Defaults to `{{uuid}}.mini-browser.{{hostname}}`. + +## Security Warnings + +- Potentially Insecure Host Pattern + + When you change the host pattern via the `THEIA_MINI_BROWSER_HOST_PATTERN` environment variable warnings will be emitted both from the frontend and from the backend. + You can disable those warnings by setting `warnOnPotentiallyInsecureHostPattern: false` in the appropriate application configurations in your application's `package.json`. ## Additional Information diff --git a/packages/mini-browser/src/browser/environment/mini-browser-environment.ts b/packages/mini-browser/src/browser/environment/mini-browser-environment.ts index c22f747a4d792..d70b3a9869177 100644 --- a/packages/mini-browser/src/browser/environment/mini-browser-environment.ts +++ b/packages/mini-browser/src/browser/environment/mini-browser-environment.ts @@ -14,35 +14,51 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { Endpoint, FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { MiniBrowserEndpoint } from '../../common/mini-browser-endpoint'; import { v4 } from 'uuid'; +import { MiniBrowserEndpoint } from '../../common/mini-browser-endpoint'; /** - * Fetch values from the backend's environment. + * Fetch values from the backend's environment and caches them locally. + * Helps with deploying various mini-browser endpoints. */ @injectable() export class MiniBrowserEnvironment implements FrontendApplicationContribution { protected _hostPatternPromise: Promise; - protected _hostPattern: string; + protected _hostPattern?: string; @inject(EnvVariablesServer) - protected readonly environment: EnvVariablesServer; + protected environment: EnvVariablesServer; @postConstruct() protected postConstruct(): void { - this._hostPatternPromise = this.environment.getValue(MiniBrowserEndpoint.HOST_PATTERN_ENV) - .then(envVar => envVar?.value || MiniBrowserEndpoint.HOST_PATTERN_DEFAULT); + this._hostPatternPromise = this.getHostPattern() + .then(pattern => this._hostPattern = pattern); + } + + get hostPatternPromise(): Promise { + return this._hostPatternPromise; + } + + get hostPattern(): string | undefined { + return this._hostPattern; } async onStart(): Promise { - this._hostPattern = await this._hostPatternPromise; + await this._hostPatternPromise; } + /** + * Throws if `hostPatternPromise` is not yet resolved. + */ getEndpoint(uuid: string, hostname?: string): Endpoint { + if (this._hostPattern === undefined) { + throw new Error('MiniBrowserEnvironment is not finished initializing'); + } return new Endpoint({ path: MiniBrowserEndpoint.PATH, host: this._hostPattern @@ -51,10 +67,20 @@ export class MiniBrowserEnvironment implements FrontendApplicationContribution { }); } + /** + * Throws if `hostPatternPromise` is not yet resolved. + */ getRandomEndpoint(): Endpoint { return this.getEndpoint(v4()); } + protected async getHostPattern(): Promise { + return environment.electron.is() + ? MiniBrowserEndpoint.HOST_PATTERN_DEFAULT + : this.environment.getValue(MiniBrowserEndpoint.HOST_PATTERN_ENV) + .then(envVar => envVar?.value || MiniBrowserEndpoint.HOST_PATTERN_DEFAULT); + } + protected getDefaultHostname(): string { return self.location.host; } diff --git a/packages/mini-browser/src/browser/location-mapper-service.ts b/packages/mini-browser/src/browser/location-mapper-service.ts index a20bdf1c8c8d1..1322341154204 100644 --- a/packages/mini-browser/src/browser/location-mapper-service.ts +++ b/packages/mini-browser/src/browser/location-mapper-service.ts @@ -129,13 +129,13 @@ export class LocationWithoutSchemeMapper implements LocationMapper { export class FileLocationMapper implements LocationMapper { @inject(MiniBrowserEnvironment) - protected readonly miniBrowserEnvironment: MiniBrowserEnvironment; + protected miniBrowserEnvironment: MiniBrowserEnvironment; canHandle(location: string): MaybePromise { return location.startsWith('file://') ? 1 : 0; } - map(location: string): MaybePromise { + async map(location: string): Promise { const uri = new URI(location); if (uri.scheme !== 'file') { throw new Error(`Only URIs with 'file' scheme can be mapped to an URL. URI was: ${uri}.`); diff --git a/packages/mini-browser/src/browser/mini-browser-frontend-module.ts b/packages/mini-browser/src/browser/mini-browser-frontend-module.ts index f3af38aabcdd6..535cb8be71ae2 100644 --- a/packages/mini-browser/src/browser/mini-browser-frontend-module.ts +++ b/packages/mini-browser/src/browser/mini-browser-frontend-module.ts @@ -39,9 +39,9 @@ import { LocationMapper, LocationWithoutSchemeMapper, } from './location-mapper-service'; +import { MiniBrowserFrontendSecurityWarnings } from './mini-browser-frontend-security-warnings'; export default new ContainerModule(bind => { - bind(MiniBrowserContent).toSelf(); bind(MiniBrowserContentFactory).toFactory(context => (props: MiniBrowserProps) => { const { container } = context; @@ -77,5 +77,10 @@ export default new ContainerModule(bind => { bind(LocationMapper).toService(LocationWithoutSchemeMapper); bind(LocationMapperService).toSelf().inSingletonScope(); - bind(MiniBrowserService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, MiniBrowserServicePath)).inSingletonScope(); + bind(MiniBrowserService).toDynamicValue( + ctx => WebSocketConnectionProvider.createProxy(ctx.container, MiniBrowserServicePath) + ).inSingletonScope(); + + bind(MiniBrowserFrontendSecurityWarnings).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(MiniBrowserFrontendSecurityWarnings); }); diff --git a/packages/mini-browser/src/browser/mini-browser-frontend-security-warnings.ts b/packages/mini-browser/src/browser/mini-browser-frontend-security-warnings.ts new file mode 100644 index 0000000000000..ed6da3af330f1 --- /dev/null +++ b/packages/mini-browser/src/browser/mini-browser-frontend-security-warnings.ts @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (C) 2021 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { MessageService } from '@theia/core'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { MiniBrowserEndpoint } from '../common/mini-browser-endpoint'; +import { MiniBrowserEnvironment } from './environment/mini-browser-environment'; + +@injectable() +export class MiniBrowserFrontendSecurityWarnings implements FrontendApplicationContribution { + + @inject(WindowService) + protected windowService: WindowService; + + @inject(MessageService) + protected messageService: MessageService; + + @inject(MiniBrowserEnvironment) + protected miniBrowserEnvironment: MiniBrowserEnvironment; + + initialize(): void { + this.checkHostPattern(); + } + + protected async checkHostPattern(): Promise { + if (FrontendApplicationConfigProvider.get()['warnOnPotentiallyInsecureHostPattern'] === false) { + return; + } + const hostPattern = await this.miniBrowserEnvironment.hostPatternPromise; + if (hostPattern !== MiniBrowserEndpoint.HOST_PATTERN_DEFAULT) { + this.messageService.warn(`\ +The mini-browser endpoint's host pattern has been changed to \`${hostPattern}\`; changing the pattern can lead to security vulnerabilities. \ +See \`@theia/mini-browser/README.md\` for more information.`, + /* actions: */ 'Ok', 'Go To README', + ).then(action => { + if (action === 'Go To README') { + this.windowService.openNewWindow('https://www.npmjs.com/package/@theia/mini-browser', { external: true }); + } + }); + } + } +} diff --git a/packages/mini-browser/src/electron-browser/environment/electron-mini-browser-environment.ts b/packages/mini-browser/src/electron-browser/environment/electron-mini-browser-environment.ts index a4c5eb5fc854b..7fa7760dd5316 100644 --- a/packages/mini-browser/src/electron-browser/environment/electron-mini-browser-environment.ts +++ b/packages/mini-browser/src/electron-browser/environment/electron-mini-browser-environment.ts @@ -40,7 +40,7 @@ export class ElectronMiniBrowserEnvironment extends MiniBrowserEnvironment { protected getDefaultHostname(): string { const query = self.location.search - .substr(1) + .substr(1) // remove leading `?` .split('&') .map(entry => entry .split('=', 2) diff --git a/packages/mini-browser/src/node/mini-browser-backend-module.ts b/packages/mini-browser/src/node/mini-browser-backend-module.ts index 0856094ce4f20..755b7fed0866f 100644 --- a/packages/mini-browser/src/node/mini-browser-backend-module.ts +++ b/packages/mini-browser/src/node/mini-browser-backend-module.ts @@ -22,6 +22,7 @@ import { MiniBrowserService, MiniBrowserServicePath } from '../common/mini-brows import { MiniBrowserEndpoint, MiniBrowserEndpointHandler, HtmlHandler, ImageHandler, PdfHandler, SvgHandler } from './mini-browser-endpoint'; import { WsRequestValidatorContribution } from '@theia/core/lib/node/ws-request-validators'; import { MiniBrowserWsRequestValidator } from './mini-browser-ws-validator'; +import { MiniBrowserBackendSecurityWarnings } from './mini-browser-backend-security-warnings'; export default new ContainerModule(bind => { bind(MiniBrowserEndpoint).toSelf().inSingletonScope(); @@ -35,4 +36,6 @@ export default new ContainerModule(bind => { bind(MiniBrowserEndpointHandler).to(ImageHandler).inSingletonScope(); bind(MiniBrowserEndpointHandler).to(PdfHandler).inSingletonScope(); bind(MiniBrowserEndpointHandler).to(SvgHandler).inSingletonScope(); + bind(MiniBrowserBackendSecurityWarnings).toSelf().inSingletonScope(); + bind(BackendApplicationContribution).toService(MiniBrowserBackendSecurityWarnings); }); diff --git a/packages/mini-browser/src/node/mini-browser-backend-security-warnings.ts b/packages/mini-browser/src/node/mini-browser-backend-security-warnings.ts new file mode 100644 index 0000000000000..8f681a7d704d8 --- /dev/null +++ b/packages/mini-browser/src/node/mini-browser-backend-security-warnings.ts @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (C) 2021 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider'; +import { injectable } from '@theia/core/shared/inversify'; +import { MiniBrowserEndpoint } from '../common/mini-browser-endpoint'; + +@injectable() +export class MiniBrowserBackendSecurityWarnings implements BackendApplicationContribution { + + initialize(): void { + this.checkHostPattern(); + } + + protected async checkHostPattern(): Promise { + if (BackendApplicationConfigProvider.get()['warnOnPotentiallyInsecureHostPattern'] === false) { + return; + } + const envHostPattern = process.env[MiniBrowserEndpoint.HOST_PATTERN_ENV]; + if (envHostPattern && envHostPattern !== MiniBrowserEndpoint.HOST_PATTERN_DEFAULT) { + console.warn(`\ +MINI BROWSER SECURITY WARNING + + Changing the @theia/mini-browser host pattern can lead to security vulnerabilities. + Current pattern: "${envHostPattern}" + Please read @theia/mini-browser/README.md for more information. +` + ); + } + } +} diff --git a/packages/plugin-ext/README.md b/packages/plugin-ext/README.md index b6aa1d46e949e..0c7763c7cd40b 100644 --- a/packages/plugin-ext/README.md +++ b/packages/plugin-ext/README.md @@ -14,6 +14,24 @@ The `@theia/plugin-ext` extension contributes functionality for the `plugin` API. +## Implementation + +The implementation is inspired from: https://blog.mattbierner.com/vscode-webview-web-learnings/. + +## Environment Variables + +- `THEIA_WEBVIEW_ENDPOINT_PATTERN` + + A string pattern possibly containing `{{uuid}}` and `{{hostname}}` which will be replaced. This is the host for which the `webviews` will be served on. + It is a good practice to host the `webview` handlers on a sub-domain as it is more secure. + Defaults to `{{uuid}}.webview.{{hostname}}`. + +## Security Warnings + +- Potentially Insecure Host Pattern + + When you change the host pattern via the `THEIA_WEBVIEW_ENDPOINT_PATTERN` environment variable warning will be emitted both from the frontend and from the backend. + You can disable those warnings by setting `warnOnPotentiallyInsecureHostPattern: false` in the appropriate application configurations in your application's `package.json`. ## Additional Information diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 2082841d0c160..b08675647dcbb 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -75,6 +75,7 @@ import { CustomEditorWidgetFactory } from '../browser/custom-editors/custom-edit import { CustomEditorWidget } from './custom-editors/custom-editor-widget'; import { CustomEditorService } from './custom-editors/custom-editor-service'; import { UndoRedoService } from './custom-editors/undo-redo-service'; +import { WebviewFrontendSecurityWarnings } from './webview/webview-frontend-security-warnings'; export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -226,4 +227,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(CommentingRangeDecorator).toSelf().inSingletonScope(); bind(CommentsContribution).toSelf().inSingletonScope(); bind(CommentsContextKeyService).toSelf().inSingletonScope(); + + bind(WebviewFrontendSecurityWarnings).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(WebviewFrontendSecurityWarnings); }); diff --git a/packages/plugin-ext/src/main/browser/webview/webview-environment.ts b/packages/plugin-ext/src/main/browser/webview/webview-environment.ts index e56175c295cdc..3896d92e9d3b3 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview-environment.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview-environment.ts @@ -25,21 +25,18 @@ import { environment } from '@theia/core/shared/@theia/application-package/lib/e @injectable() export class WebviewEnvironment { - @inject(EnvVariablesServer) - protected readonly environments: EnvVariablesServer; + protected _hostPatternPromise: Promise; protected readonly externalEndpointHost = new Deferred(); + @inject(EnvVariablesServer) + protected readonly environments: EnvVariablesServer; + @postConstruct() protected async init(): Promise { + this._hostPatternPromise = this.getHostPattern(); try { - let endpointPattern; - if (environment.electron.is()) { - endpointPattern = WebviewExternalEndpoint.defaultPattern; - } else { - const variable = await this.environments.getValue(WebviewExternalEndpoint.pattern); - endpointPattern = variable && variable.value || WebviewExternalEndpoint.defaultPattern; - } + const endpointPattern = await this.hostPatternPromise; const { host } = new Endpoint(); this.externalEndpointHost.resolve(endpointPattern.replace('{{hostname}}', host)); } catch (e) { @@ -47,6 +44,10 @@ export class WebviewEnvironment { } } + get hostPatternPromise(): Promise { + return this._hostPatternPromise; + } + async externalEndpointUrl(): Promise { const host = await this.externalEndpointHost.promise; return new Endpoint({ @@ -67,4 +68,10 @@ export class WebviewEnvironment { return (await this.externalEndpointUrl()).withPath('').withQuery('').withFragment('').toString(true).replace('{{uuid}}', '*'); } + protected async getHostPattern(): Promise { + return environment.electron.is() + ? WebviewExternalEndpoint.defaultPattern + : this.environments.getValue(WebviewExternalEndpoint.pattern) + .then(variable => variable?.value || WebviewExternalEndpoint.defaultPattern); + } } diff --git a/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts b/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts new file mode 100644 index 0000000000000..df765dac8cc6a --- /dev/null +++ b/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (C) 2021 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { MessageService } from '@theia/core'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { WebviewExternalEndpoint } from '../../common/webview-protocol'; +import { WebviewEnvironment } from './webview-environment'; + +@injectable() +export class WebviewFrontendSecurityWarnings implements FrontendApplicationContribution { + + @inject(WindowService) + protected windowService: WindowService; + + @inject(MessageService) + protected messageService: MessageService; + + @inject(WebviewEnvironment) + protected webviewEnvironment: WebviewEnvironment; + + initialize(): void { + this.checkHostPattern(); + } + + protected async checkHostPattern(): Promise { + if (FrontendApplicationConfigProvider.get()['warnOnPotentiallyInsecureHostPattern'] === false) { + return; + } + const hostPattern = await this.webviewEnvironment.hostPatternPromise; + if (hostPattern !== WebviewExternalEndpoint.defaultPattern) { + this.messageService.warn(`\ +The webview endpoint's host pattern has been changed to \`${hostPattern}\`; changing the pattern can lead to security vulnerabilities. \ +See \`@theia/plugin-ext/README.md\` for more information.`, + /* actions: */ 'Ok', 'Go To README', + ).then(action => { + if (action === 'Go To README') { + this.windowService.openNewWindow('https://www.npmjs.com/package/@theia/plugin-ext', { external: true }); + } + }); + } + } +} diff --git a/packages/plugin-ext/src/main/browser/webview/webview-preferences.ts b/packages/plugin-ext/src/main/browser/webview/webview-preferences.ts index c15689d35da73..16a23d44e7a58 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview-preferences.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview-preferences.ts @@ -15,6 +15,7 @@ ********************************************************************************/ import { interfaces } from '@theia/core/shared/inversify'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; import { createPreferenceProxy, PreferenceProxy, @@ -23,20 +24,32 @@ import { PreferenceSchema } from '@theia/core/lib/browser/preferences'; +const frontendConfig = FrontendApplicationConfigProvider.get(); + export const WebviewConfigSchema: PreferenceSchema = { - 'type': 'object', - 'properties': { + type: 'object', + properties: { 'webview.trace': { - 'type': 'string', - 'enum': ['off', 'on', 'verbose'], - 'description': 'Controls communication tracing with webviews.', - 'default': 'off' + type: 'string', + enum: ['off', 'on', 'verbose'], + description: 'Controls communication tracing with webviews.', + default: 'off' } } }; +if (frontendConfig.securityWarnings) { + WebviewConfigSchema.properties['webview.warnIfUnsecure'] = { + scope: 'application', + type: 'boolean', + description: 'Warns users that webviews are currently deployed unsecurely.', + default: true, + }; +} + export interface WebviewConfiguration { 'webview.trace': 'off' | 'on' | 'verbose' + 'webview.warnIfUnsecure'?: boolean } export const WebviewPreferences = Symbol('WebviewPreferences'); @@ -51,6 +64,5 @@ export function bindWebviewPreferences(bind: interfaces.Bind): void { const preferences = ctx.container.get(PreferenceService); return createWebviewPreferences(preferences); }); - bind(PreferenceContribution).toConstantValue({ schema: WebviewConfigSchema }); } diff --git a/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts b/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts index f490d71e5adfc..a8719ab9016c3 100644 --- a/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts +++ b/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts @@ -38,6 +38,7 @@ import { PluginServerHandler } from './plugin-server-handler'; import { PluginCliContribution } from './plugin-cli-contribution'; import { PluginTheiaEnvironment } from '../common/plugin-theia-environment'; import { PluginTheiaDeployerParticipant } from './plugin-theia-deployer-participant'; +import { WebviewBackendSecurityWarnings } from './webview-backend-security-warnings'; export function bindMainBackend(bind: interfaces.Bind): void { bind(PluginApiContribution).toSelf().inSingletonScope(); @@ -81,4 +82,6 @@ export function bindMainBackend(bind: interfaces.Bind): void { bind(PluginCliContribution).toSelf().inSingletonScope(); bind(CliContribution).toService(PluginCliContribution); + bind(WebviewBackendSecurityWarnings).toSelf().inSingletonScope(); + bind(BackendApplicationContribution).toService(WebviewBackendSecurityWarnings); } diff --git a/packages/plugin-ext/src/main/node/webview-backend-security-warnings.ts b/packages/plugin-ext/src/main/node/webview-backend-security-warnings.ts new file mode 100644 index 0000000000000..e372b61efa5b1 --- /dev/null +++ b/packages/plugin-ext/src/main/node/webview-backend-security-warnings.ts @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (C) 2021 Ericsson and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider'; +import { injectable } from '@theia/core/shared/inversify'; +import { WebviewExternalEndpoint } from '../common/webview-protocol'; + +@injectable() +export class WebviewBackendSecurityWarnings implements BackendApplicationContribution { + + initialize(): void { + this.checkHostPattern(); + } + + protected async checkHostPattern(): Promise { + if (BackendApplicationConfigProvider.get()['warnOnPotentiallyInsecureHostPattern'] === false) { + return; + } + const envHostPattern = process.env[WebviewExternalEndpoint.pattern]; + if (envHostPattern && envHostPattern !== WebviewExternalEndpoint.defaultPattern) { + console.warn(`\ +WEBVIEW SECURITY WARNING + + Changing the @theia/plugin-ext webview host pattern can lead to security vulnerabilities. + Current pattern: "${envHostPattern}" + Please read @theia/plugin-ext/README.md for more information. +` + ); + } + } +}