Restrict vscode-file requests to main window (#139140)

* Restrict vscode-file requests to main window

This change tries to limit use of the vscode-file protocol to the main window.

While investigating this issue, I hit a bug: `CodeWindow` registers a `onBeforeRequestHandler` but can then be disposed of. When this happens, the handler remains active but any references to `this` inside of it now reference the disposed class.

To fix this, I've moved all the request filtering logic into `app.ts` instead and register it on the `defaultSession` instead of on a web contents.

Then to fix the original issue, I added logic that checks the routingId of the request against the routingIds of all known windows. Only windows with known routingIds are allowed to use `vscode-file`.

* Move logic into configure session

* Check processId

* Check BrowserWindow.getAllWindows instead of just main VS Code windows
This commit is contained in:
Matt Bierner
2022-01-18 12:43:47 -08:00
committed by GitHub
parent 5bd31d3385
commit 86e3a3a062
2 changed files with 86 additions and 57 deletions

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, BrowserWindow, contentTracing, dialog, ipcMain, protocol, session, Session, systemPreferences } from 'electron';
import { app, BrowserWindow, contentTracing, dialog, ipcMain, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron';
import { statSync } from 'fs';
import { hostname, release } from 'os';
import { VSBuffer } from 'vs/base/common/buffer';
@@ -154,6 +154,90 @@ export class CodeApplication extends Disposable {
//#endregion
//#region Request filtering
// Block all SVG requests from unsupported origins
const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, 'devtools']);
// But allow them if the are made from inside an webview
const isSafeFrame = (requestFrame: WebFrameMain | undefined): boolean => {
for (let frame: WebFrameMain | null | undefined = requestFrame; frame; frame = frame.parent) {
if (frame.url.startsWith(`${Schemas.vscodeWebview}://`)) {
return true;
}
}
return false;
};
const isSvgRequestFromSafeContext = (details: Electron.OnBeforeRequestListenerDetails | Electron.OnHeadersReceivedListenerDetails): boolean => {
return details.resourceType === 'xhr' || isSafeFrame(details.frame);
};
const isAllowedVsCodeFileRequest = (details: Electron.OnBeforeRequestListenerDetails) => {
const frame = details.frame;
if (!frame || !this.windowsMainService) {
return false;
}
// Check to see if the request comes from one of the main windows (or shared process) and not from embedded content
const windows = BrowserWindow.getAllWindows();
for (const window of windows) {
if (frame.processId === window.webContents.mainFrame.processId) {
return true;
}
}
return false;
};
session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
const uri = URI.parse(details.url);
if (uri.scheme === Schemas.vscodeFileResource) {
if (!isAllowedVsCodeFileRequest(details)) {
this.logService.error('Blocked vscode-file request', details.url);
return callback({ cancel: true });
}
}
// Block most svgs
if (uri.path.endsWith('.svg')) {
const isSafeResourceUrl = supportedSvgSchemes.has(uri.scheme);
if (!isSafeResourceUrl) {
return callback({ cancel: !isSvgRequestFromSafeContext(details) });
}
}
return callback({ cancel: false });
});
// Configure SVG header content type properly
// https://github.com/microsoft/vscode/issues/97564
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
const contentTypes = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
if (contentTypes && Array.isArray(contentTypes)) {
const uri = URI.parse(details.url);
if (uri.path.endsWith('.svg')) {
if (supportedSvgSchemes.has(uri.scheme)) {
responseHeaders['Content-Type'] = ['image/svg+xml'];
return callback({ cancel: false, responseHeaders });
}
}
// remote extension schemes have the following format
// http://127.0.0.1:<port>/vscode-remote-resource?path=
if (!uri.path.includes(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) {
return callback({ cancel: !isSvgRequestFromSafeContext(details) });
}
}
return callback({ cancel: false });
});
//#endregion
//#region Code Cache

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, BrowserWindow, BrowserWindowConstructorOptions, Display, Event, nativeImage, NativeImage, Rectangle, screen, SegmentedControlSegment, systemPreferences, TouchBar, TouchBarSegmentedControl, WebFrameMain } from 'electron';
import { app, BrowserWindow, BrowserWindowConstructorOptions, Display, Event, nativeImage, NativeImage, Rectangle, screen, SegmentedControlSegment, systemPreferences, TouchBar, TouchBarSegmentedControl } from 'electron';
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
@@ -427,61 +427,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.dispose();
});
// Block all SVG requests from unsupported origins
const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, 'devtools']);
// But allow them if the are made from inside an webview
const isSafeFrame = (requestFrame: WebFrameMain | undefined): boolean => {
for (let frame: WebFrameMain | null | undefined = requestFrame; frame; frame = frame.parent) {
if (frame.url.startsWith(`${Schemas.vscodeWebview}://`)) {
return true;
}
}
return false;
};
const isRequestFromSafeContext = (details: Electron.OnBeforeRequestListenerDetails | Electron.OnHeadersReceivedListenerDetails): boolean => {
return details.resourceType === 'xhr' || isSafeFrame(details.frame);
};
this._win.webContents.session.webRequest.onBeforeRequest((details, callback) => {
const uri = URI.parse(details.url);
if (uri.path.endsWith('.svg')) {
const isSafeResourceUrl = supportedSvgSchemes.has(uri.scheme);
if (!isSafeResourceUrl) {
return callback({ cancel: !isRequestFromSafeContext(details) });
}
}
return callback({ cancel: false });
});
// Configure SVG header content type properly
// https://github.com/microsoft/vscode/issues/97564
this._win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
const contentTypes = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
if (contentTypes && Array.isArray(contentTypes)) {
const uri = URI.parse(details.url);
if (uri.path.endsWith('.svg')) {
if (supportedSvgSchemes.has(uri.scheme)) {
responseHeaders['Content-Type'] = ['image/svg+xml'];
return callback({ cancel: false, responseHeaders });
}
}
// remote extension schemes have the following format
// http://127.0.0.1:<port>/vscode-remote-resource?path=
if (!uri.path.includes(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) {
return callback({ cancel: !isRequestFromSafeContext(details) });
}
}
return callback({ cancel: false });
});
// Remember that we loaded
this._win.webContents.on('did-finish-load', () => {