code-server
Version:
Run VS Code on a remote server.
166 lines (144 loc) • 5.44 kB
HTML
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="
default-src 'none';
child-src 'self' data: blob:;
script-src 'self' 'unsafe-eval' 'sha256-yhZXuB8LS6t73dvNg6rtLX8y4PHLnqRm5+6DdOGkOcw=' https: http://localhost:* blob:;
connect-src 'self' https: wss: http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*;"/>
</head>
<body>
<script>
(function () {
const searchParams = new URL(document.location.href).searchParams;
const vscodeWebWorkerExtHostId = searchParams.get('vscodeWebWorkerExtHostId') || '';
// DO NOT CHANGE the name of the worker without also updating js-debug, as that
// is used to filter targets to attach to (e.g. #232544)
const name = searchParams.get('debugged') ? 'DebugExtensionHostWorker' : 'ExtensionHostWorker';
const parentOrigin = searchParams.get('parentOrigin') || window.origin;
const salt = searchParams.get('salt');
(async function () {
const hostnameValidationMarker = 'v--';
const hostname = location.hostname;
if (!hostname.startsWith(hostnameValidationMarker)) {
// validation not requested
return start();
}
// It is safe to run if we are on the same host.
const parent = new URL(parentOrigin)
if (parent.hostname === hostname) {
return start()
}
if (!crypto.subtle) {
// cannot validate, not running in a secure context
return sendError(new Error(`Cannot validate in current context!`));
}
// Here the `parentOriginHash()` function from `src/vs/base/browser/iframe.ts` is inlined
// compute a sha-256 composed of `parentOrigin` and `salt` converted to base 32
/** @type {string} */
let parentOriginHash;
try {
const strData = JSON.stringify({ parentOrigin, salt });
const encoder = new TextEncoder();
const arrData = encoder.encode(strData);
const hash = await crypto.subtle.digest('sha-256', arrData);
const hashArray = Array.from(new Uint8Array(hash));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
// sha256 has 256 bits, so we need at most ceil(lg(2^256-1)/lg(32)) = 52 chars to represent it in base 32
parentOriginHash = BigInt(`0x${hashHex}`).toString(32).padStart(52, '0');
} catch (err) {
return sendError(err instanceof Error ? err : new Error(String(err)));
}
const requiredSubdomain = `${hostnameValidationMarker}${parentOriginHash}.`;
if (hostname.substring(0, requiredSubdomain.length) === requiredSubdomain) {
// validation succeeded!
return start();
}
return sendError(new Error(`Expected '${requiredSubdomain}' as subdomain!`));
})();
function sendError(error) {
window.parent.postMessage({
vscodeWebWorkerExtHostId,
error: {
name: error ? error.name : '',
message: error ? error.message : '',
stack: error ? error.stack : []
}
}, '*');
}
function start() {
// Before we can load the worker, we need to get the current set of NLS
// configuration into this iframe. We ask the parent window to send it
// together with the necessary information to load the worker via Blob.
const bootstrapNlsType = 'vscode.bootstrap.nls';
self.onmessage = (event) => {
if (event.origin !== parentOrigin || event.data.type !== bootstrapNlsType) {
return;
}
const { data } = event.data;
createWorker(data.workerUrl, data.fileRoot, data.nls.messages, data.nls.language);
};
window.parent.postMessage({
vscodeWebWorkerExtHostId,
type: bootstrapNlsType
}, '*');
}
function createWorker(workerUrl, fileRoot, nlsMessages, nlsLanguage) {
try {
if (globalThis.crossOriginIsolated) {
workerUrl += '?vscode-coi=2'; // COEP
}
// In below blob code, we are using JSON.stringify to ensure the passed
// in values are not breaking our script. The values may contain string
// terminating characters (such as ' or ").
const blob = new Blob([[
`/*extensionHostWorker*/`,
`globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(nlsMessages)};`,
`globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(nlsLanguage)};`,
`globalThis._VSCODE_FILE_ROOT = ${JSON.stringify(fileRoot)};`,
`await import(${JSON.stringify(workerUrl)});`,
`/*extensionHostWorker*/`
].join('')], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob), { name, type: 'module' });
const nestedWorkers = new Map();
worker.onmessage = (event) => {
const { data } = event;
if (data?.type === '_newWorker') {
const { id, port, url, options } = data;
const newWorker = new Worker(url, options);
newWorker.postMessage(port, [port]);
newWorker.onerror = console.error.bind(console);
nestedWorkers.set(id, newWorker);
} else if (data?.type === '_terminateWorker') {
const { id } = data;
if (nestedWorkers.has(id)) {
nestedWorkers.get(id).terminate();
nestedWorkers.delete(id);
}
} else {
worker.onerror = console.error.bind(console);
window.parent.postMessage({
vscodeWebWorkerExtHostId,
data
}, parentOrigin, [data]);
}
};
worker.onerror = (event) => {
console.error(event.message, event.error);
sendError(event.error);
};
self.onmessage = (event) => {
if (event.origin !== parentOrigin) {
return;
}
worker.postMessage(event.data, event.ports);
};
} catch (err) {
console.error(err);
sendError(err);
}
}
})();
</script>
</body>
</html>