mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-21 02:01:43 -05:00
Move connection token validation earlier
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
|
||||
<!-- Workbench Icon/Manifest/CSS -->
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||
</head>
|
||||
|
||||
<body aria-label="">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<!-- Workbench Icon/Manifest/CSS -->
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||
<link data-name="vs/workbench/workbench.web.main" rel="stylesheet" href="./static/out/vs/workbench/workbench.web.main.css">
|
||||
|
||||
</head>
|
||||
|
||||
@@ -30,15 +30,13 @@ import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAg
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ExtensionHostConnection } from 'vs/server/node/extensionHostConnection';
|
||||
import { ManagementConnection } from 'vs/server/node/remoteExtensionManagement';
|
||||
import { parseServerConnectionToken, ServerConnectionToken, ServerConnectionTokenParseError, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken';
|
||||
import { parseServerConnectionToken, requestHasValidConnectionToken as httpRequestHasValidConnectionToken, ServerConnectionToken, ServerConnectionTokenParseError, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken';
|
||||
import { IServerEnvironmentService, ServerParsedArgs } from 'vs/server/node/serverEnvironmentService';
|
||||
import { setupServerServices, SocketServer } from 'vs/server/node/serverServices';
|
||||
import { serveError, serveFile, WebClientServer } from 'vs/server/node/webClientServer';
|
||||
|
||||
const SHUTDOWN_TIMEOUT = 5 * 60 * 1000;
|
||||
|
||||
export type ServerListenOptions = { host?: string; port?: number; socketPath?: string };
|
||||
|
||||
declare module vsda {
|
||||
// the signer is a native module that for historical reasons uses a lower case class name
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
@@ -114,13 +112,14 @@ export class RemoteExtensionHostAgentServer extends Disposable {
|
||||
return res.end('OK');
|
||||
}
|
||||
|
||||
if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) {
|
||||
// invalid connection token
|
||||
return serveError(req, res, 403, `Forbidden.`);
|
||||
}
|
||||
|
||||
if (pathname === '/vscode-remote-resource') {
|
||||
// Handle HTTP requests for resources rendered in the rich client (images, fonts, etc.)
|
||||
// These resources could be files shipped with extensions or even workspace files.
|
||||
if (!this._connectionToken.validate(parsedUrl.query['tkn'])) {
|
||||
return serveError(req, res, 403, `Forbidden.`);
|
||||
}
|
||||
|
||||
const desiredPath = parsedUrl.query['path'];
|
||||
if (typeof desiredPath !== 'string') {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cookie from 'cookie';
|
||||
import * as fs from 'fs';
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ServerParsedArgs } from 'vs/server/node/serverEnvironmentService';
|
||||
|
||||
@@ -106,3 +109,14 @@ export function parseServerConnectionToken(args: ServerParsedArgs): ServerConnec
|
||||
|
||||
return new MandatoryServerConnectionToken(generateUuid());
|
||||
}
|
||||
|
||||
export function requestHasValidConnectionToken(connectionToken: ServerConnectionToken, req: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery) {
|
||||
// First check if there is a valid `tkn` query parameter
|
||||
if (connectionToken.validate(parsedUrl.query['tkn'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, check if there is a valid `vscode-tkn` cookie
|
||||
const cookies = cookie.parse(req.headers.cookie || '');
|
||||
return connectionToken.validate(cookies['vscode-tkn']);
|
||||
}
|
||||
|
||||
@@ -110,11 +110,6 @@ export class WebClientServer {
|
||||
}
|
||||
}
|
||||
|
||||
private _hasCorrectTokenCookie(req: http.IncomingMessage): boolean {
|
||||
const cookies = cookie.parse(req.headers.cookie || '');
|
||||
return this._connectionToken.validate(cookies['vscode-tkn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle HTTP requests for /static/*
|
||||
*/
|
||||
@@ -141,12 +136,18 @@ export class WebClientServer {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
|
||||
const queryTkn = parsedUrl.query['tkn'];
|
||||
if (typeof queryTkn === 'string') {
|
||||
// tkn came in via a query string
|
||||
// => set a cookie and redirect to url without tkn
|
||||
if (typeof parsedUrl.query['tkn'] === 'string') {
|
||||
// We got a connection token as a query parameter.
|
||||
// We want to have a clean URL, so we strip it
|
||||
const responseHeaders: Record<string, string> = Object.create(null);
|
||||
responseHeaders['Set-Cookie'] = cookie.serialize('vscode-tkn', queryTkn, { sameSite: 'strict', maxAge: 60 * 60 * 24 * 7 /* 1 week */ });
|
||||
responseHeaders['Set-Cookie'] = cookie.serialize(
|
||||
'vscode-tkn',
|
||||
parsedUrl.query['tkn'],
|
||||
{
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 24 * 7 /* 1 week */
|
||||
}
|
||||
);
|
||||
|
||||
const newQuery = Object.create(null);
|
||||
for (let key in parsedUrl.query) {
|
||||
@@ -161,10 +162,6 @@ export class WebClientServer {
|
||||
return res.end();
|
||||
}
|
||||
|
||||
if (this._environmentService.isBuilt && !this._hasCorrectTokenCookie(req)) {
|
||||
return serveError(req, res, 403, `Forbidden.`);
|
||||
}
|
||||
|
||||
const remoteAuthority = req.headers.host;
|
||||
|
||||
function escapeAttribute(value: string): string {
|
||||
|
||||
Reference in New Issue
Block a user