Merge pull request #319813 from microsoft/dev/mjbvz/zestful-rat

Block programmatic webview click and keypress re-dispatch events in some cases
This commit is contained in:
Matt Bierner
2026-06-03 18:13:38 -07:00
committed by GitHub
7 changed files with 51 additions and 20 deletions
@@ -152,6 +152,7 @@ export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWeb
return {
allowScripts: webviewOptions.enableScripts,
allowForms: webviewOptions.enableForms,
forwardUntrustedKeypressEvents: true, // This is always enabled for extensions
enableCommandUris: webviewOptions.enableCommandUris,
localResourceRoots: Array.isArray(webviewOptions.localResourceRoots) ? webviewOptions.localResourceRoots.map(r => URI.revive(r)) : undefined,
portMapping: webviewOptions.portMapping,
@@ -1205,6 +1205,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
contentOptions: {
allowMultipleAPIAcquire: true,
allowScripts: true,
forwardUntrustedKeypressEvents: false,
localResourceRoots: this.localResourceRootsCache,
},
extension: undefined,
@@ -5,7 +5,7 @@
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'sha256-q+WTr+fBXpLLE3++yWNaxT6BTWQtsKscoeIlynBRk4E=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
content="default-src 'none'; script-src 'sha256-nXjtuhBilO++r8hfxl5VjEScSmdm07wDAk6jw228DgM=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
<!-- Disable pinch zooming -->
<meta name="viewport"
@@ -533,7 +533,7 @@
* @param {MouseEvent} event
*/
const handleInnerClick = (event) => {
if (!event?.view?.document) {
if (!event.isTrusted || !event?.view?.document) {
return;
}
@@ -606,7 +606,8 @@
e.preventDefault();
}
hostMessaging.postMessage('did-keydown', {
/** @type {import('../webviewMessages').FromWebviewMessage['did-keydown']} */
const data = {
key: e.key,
keyCode: e.keyCode,
code: e.code,
@@ -614,14 +615,17 @@
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
repeat: e.repeat
});
repeat: e.repeat,
isTrusted: e.isTrusted
};
hostMessaging.postMessage('did-keydown', data);
};
/**
* @param {KeyboardEvent} e
*/
const handleInnerKeyup = (e) => {
hostMessaging.postMessage('did-keyup', {
/** @type {import('../webviewMessages').FromWebviewMessage['did-keyup']} */
const data = {
key: e.key,
keyCode: e.keyCode,
code: e.code,
@@ -629,8 +633,10 @@
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
repeat: e.repeat
});
repeat: e.repeat,
isTrusted: e.isTrusted
};
hostMessaging.postMessage('did-keyup', data);
};
/**
@@ -123,6 +123,18 @@ export interface WebviewContentOptions {
*/
readonly allowForms?: boolean;
/**
* Should untrusted key events from the webview be forwarded to the workbench? Defaults to false.
*
* This blocks keypress events that are triggered by scripts. Webviews should not allow untrusted scripts
* to be run, so this is more of a defense-in-depth measure.
*
* There are valid reasons to enable this, such as when a webview embeds an iframe. In those cases, keyboard events can
* be eaten by the iframe, so the inner iframe needs to forward keyboard events to the parent webview,
* which then needs to forward them to the workbench.
*/
readonly forwardUntrustedKeypressEvents?: boolean;
/**
* Set of root paths from which the webview can load local resources.
*/
@@ -149,6 +161,7 @@ export function areWebviewContentOptionsEqual(a: WebviewContentOptions, b: Webvi
a.allowMultipleAPIAcquire === b.allowMultipleAPIAcquire
&& a.allowScripts === b.allowScripts
&& a.allowForms === b.allowForms
&& a.forwardUntrustedKeypressEvents === b.forwardUntrustedKeypressEvents
&& equals(a.localResourceRoots, b.localResourceRoots, isEqual)
&& equals(a.portMapping, b.portMapping, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort)
&& areEnableCommandUrisEqual(a, b)
@@ -247,9 +247,6 @@ export class WebviewElement extends Disposable implements IWebviewElement, Webvi
}));
this._register(this.on('did-keydown', (data) => {
// Electron: workaround for https://github.com/electron/electron/issues/14258
// We have to detect keyboard events in the <webview> and dispatch them to our
// keybinding service because these events do not bubble to the parent window anymore.
this.handleKeyEvent('keydown', data);
}));
@@ -713,7 +710,18 @@ export class WebviewElement extends Disposable implements IWebviewElement, Webvi
}
}
private shouldForwardKeyEvent(event: KeyEvent): boolean {
return event.isTrusted || !!this._content.options.forwardUntrustedKeypressEvents;
}
private handleKeyEvent(type: 'keydown' | 'keyup', event: KeyEvent) {
if (!this.shouldForwardKeyEvent(event)) {
return;
}
// Electron: workaround for https://github.com/electron/electron/issues/14258
// We have to detect keyboard events in the <webview> and dispatch them to our
// keybinding service because these events do not bubble to the parent window anymore.
// Create a fake KeyboardEvent from the data provided
const emulatedKeyboardEvent = new KeyboardEvent(type, event);
// Force override the target
@@ -7,18 +7,19 @@ import type { IMouseWheelEvent } from '../../../../base/browser/mouseEvent.js';
import type { WebviewStyles } from './webview.js';
type KeyEvent = {
key: string;
keyCode: number;
code: string;
shiftKey: boolean;
altKey: boolean;
ctrlKey: boolean;
metaKey: boolean;
repeat: boolean;
readonly key: string;
readonly keyCode: number;
readonly code: string;
readonly shiftKey: boolean;
readonly altKey: boolean;
readonly ctrlKey: boolean;
readonly metaKey: boolean;
readonly repeat: boolean;
readonly isTrusted: boolean;
}
type WebViewDragEvent = {
shiftKey: boolean;
readonly shiftKey: boolean;
}
export type FromWebviewMessage = {
@@ -184,6 +184,7 @@ export function restoreWebviewOptions(options: SerializedWebviewOptions): Webvie
export function restoreWebviewContentOptions(options: SerializedWebviewOptions): WebviewContentOptions {
return {
...options,
forwardUntrustedKeypressEvents: options.forwardUntrustedKeypressEvents ?? true, // default to true for backwards compatibility
localResourceRoots: options.localResourceRoots?.map(uri => reviveUri(uri)),
};
}