From 63fc62401b3a3eb319d23afff4c5cd2da47ac0ec Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Tue, 18 Jul 2023 17:06:13 -0400 Subject: [PATCH] refactor: split handleIncomingMessage to its own file, set activePortalMasterKey, call maybeInitDefaultPortals --- src/index.ts | 161 ++++++++----------------------------------------- src/key.ts | 9 ++- src/message.ts | 111 ++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 142 deletions(-) create mode 100644 src/message.ts diff --git a/src/index.ts b/src/index.ts index 458ba27..0702e9b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,152 +1,39 @@ -// This is the business logic for the Skynet kernel, responsible for -// downloading and running modules, managing queries between modules and -// applications, managing user overrides, and other core functionalities. - -// NOTE: Anything and anyone can send messages to the kernel. All data that -// gets received is untrusted and potentially maliciously crafted. Type -// checking is very important. - -import { notableErrors, respondErr } from "./err.js"; import { logLargeObjects } from "./logLargeState.js"; import { log, logErr } from "./log.js"; -import { handleModuleCall, handleQueryUpdate } from "./queries.js"; import { KERNEL_DISTRO, KERNEL_VERSION } from "./version.js"; -import { setActivePortals } from "@lumeweb/libweb"; +import { + maybeInitDefaultPortals, + setActivePortalMasterKey, +} from "@lumeweb/libweb"; import { Client } from "@lumeweb/libportal"; +import { addContextToErr } from "@lumeweb/libkernel"; +import { handleIncomingMessage } from "./message.js"; +import { activeKey } from "./key.js"; -// These three functions are expected to have already been declared by the -// bootloader. They are necessary for getting started and downloading the -// kernel while informing applications about the auth state of the kernel. -// -// The kernel is encouraged to overwrite these functions with new values. -declare let handleIncomingMessage: (event: MessageEvent) => void; -declare let handleSkynetKernelRequestOverride: (event: MessageEvent) => void; -declare let bootloaderPortals: Client[]; - -// IS_EXTENSION is a boolean that indicates whether or not the kernel is -// running in a browser extension. -const IS_EXTENSION = window.origin === "http://kernel.lume"; +declare global { + interface Window { + bootloaderPortals: Client[]; + } +} // Kick off the thread that will periodically log all of the large objects in // the kernel, so that it's easier to check for memory leaks. logLargeObjects(); -// Establish the stateful variable for tracking module overrides. -let moduleOverrideList = {} as any; - -// Set up portals based on the instances created in the bootloader -setActivePortals(bootloaderPortals); - // Write a log that declares the kernel version and distribution. log("init", "Lume Web Kernel v" + KERNEL_VERSION + "-" + KERNEL_DISTRO); -// Overwrite the handleIncomingMessage function that gets called at the end of the -// event handler, allowing us to support custom messages. -handleIncomingMessage = function (event: any) { - // Ignore all messages from ourself. - if (event.source === window) { - return; - } +/* + Try to load either our saved portal(s) or the default portal(s) + */ +setActivePortalMasterKey(activeKey); - // Input validation. - if (!("method" in event.data)) { - logErr("handleIncomingMessage", "kernel request is missing 'method' field"); - return; - } - if (!("nonce" in event.data)) { - logErr( - "handleIncomingMessage", - "message sent to kernel with no nonce field", - event.data, - ); - return; - } +let [, portalLoadErr] = maybeInitDefaultPortals(); +if (portalLoadErr) { + let err = addContextToErr(portalLoadErr, "unable to init portals"); + logErr(err); +} - // Establish a debugging handler that a developer can call to verify - // that round-trip communication has been correctly programmed between - // the kernel and the calling application. - // - // It was easier to inline the message than to abstract it. - if (event.data.method === "version") { - event.source.postMessage( - { - nonce: event.data.nonce, - method: "response", - err: null, - data: { - distribution: KERNEL_DISTRO, - version: KERNEL_VERSION, - }, - }, - event.origin, - ); - return; - } - - // Establish a debugging handler to return any noteworthy errors that the - // kernel has encountered. This is mainly intended to be used by the test - // suite. - if (event.data.method === "checkErrs") { - event.source.postMessage( - { - nonce: event.data.nonce, - method: "response", - err: null, - data: { - errs: notableErrors, - }, - }, - event.origin, - ); - return; - } - - // Establish handlers for the major kernel methods. - if (event.data.method === "moduleCall") { - // Check for a domain. If the message was sent by a browser - // extension, we trust the domain provided by the extension, - // otherwise we use the domain of the parent as the domain. - // This does mean that the kernel is trusting that the user has - // no malicious browser extensions, as we aren't checking for - // **which** extension is sending the message, we are only - // checking that the message is coming from a browser - // extension. - if (event.origin.startsWith("moz") && !("domain" in event.data)) { - logErr( - "moduleCall", - "caller is an extension, but no domain was provided", - ); - respondErr( - event, - event.source, - false, - "caller is an extension, but not domain was provided", - ); - return; - } - let domain; - if (event.origin.startsWith("moz")) { - domain = event.data.domain; - } else { - domain = new URL(event.origin).hostname; - } - handleModuleCall(event, event.source, domain, false); - return; - } - if (event.data.method === "queryUpdate") { - handleQueryUpdate(event); - return; - } - if (event.data.method === "requestOverride") { - handleSkynetKernelRequestOverride(event); - return; - } - - // Unrecognized method, reject the query. - respondErr( - event, - event.source, - false, - "unrecognized method: " + event.data.method, - ); -}; +if (!portalLoadErr) { + window.addEventListener("message", handleIncomingMessage); +} diff --git a/src/key.ts b/src/key.ts index 5ed60ce..ec759cf 100644 --- a/src/key.ts +++ b/src/key.ts @@ -1,6 +1,5 @@ -// This variable is the key that got loaded into memory by the bootloader, and -// is the user key. We keep this key in memory, because if the user ever logs -// out the kernel is expected to refresh, which will clear the key. -declare let userKey: Uint8Array; +import { hexToBytes } from "@lumeweb/libweb"; -export const activeKey = userKey; +export const activeKey = hexToBytes( + window.localStorage.getItem("key") as string, +); diff --git a/src/message.ts b/src/message.ts new file mode 100644 index 0000000..fcaf43c --- /dev/null +++ b/src/message.ts @@ -0,0 +1,111 @@ +// Overwrite the handleIncomingMessage function that gets called at the end of the +// event handler, allowing us to support custom messages. +import { logErr } from "./log.js"; +import { KERNEL_DISTRO, KERNEL_VERSION } from "./version.js"; +import { notableErrors, respondErr } from "./err.js"; +import { handleModuleCall, handleQueryUpdate } from "./queries.js"; + +export const handleIncomingMessage = function (event: any) { + // Ignore all messages from ourself. + if (event.source === window) { + return; + } + + // Input validation. + if (!("method" in event.data)) { + logErr("handleIncomingMessage", "kernel request is missing 'method' field"); + return; + } + if (!("nonce" in event.data)) { + logErr( + "handleIncomingMessage", + "message sent to kernel with no nonce field", + event.data, + ); + return; + } + + // Establish a debugging handler that a developer can call to verify + // that round-trip communication has been correctly programmed between + // the kernel and the calling application. + // + // It was easier to inline the message than to abstract it. + if (event.data.method === "version") { + event.source.postMessage( + { + nonce: event.data.nonce, + method: "response", + err: null, + data: { + distribution: KERNEL_DISTRO, + version: KERNEL_VERSION, + }, + }, + event.origin, + ); + return; + } + + // Establish a debugging handler to return any noteworthy errors that the + // kernel has encountered. This is mainly intended to be used by the test + // suite. + if (event.data.method === "checkErrs") { + event.source.postMessage( + { + nonce: event.data.nonce, + method: "response", + err: null, + data: { + errs: notableErrors, + }, + }, + event.origin, + ); + return; + } + + // Establish handlers for the major kernel methods. + if (event.data.method === "moduleCall") { + // Check for a domain. If the message was sent by a browser + // extension, we trust the domain provided by the extension, + // otherwise we use the domain of the parent as the domain. + // This does mean that the kernel is trusting that the user has + // no malicious browser extensions, as we aren't checking for + // **which** extension is sending the message, we are only + // checking that the message is coming from a browser + // extension. + if (event.origin.startsWith("moz") && !("domain" in event.data)) { + logErr( + "moduleCall", + "caller is an extension, but no domain was provided", + ); + respondErr( + event, + event.source, + false, + "caller is an extension, but not domain was provided", + ); + return; + } + let domain; + if (event.origin.startsWith("moz")) { + domain = event.data.domain; + } else { + domain = new URL(event.origin).hostname; + } + handleModuleCall(event, event.source, domain, false); + return; + } + if (event.data.method === "queryUpdate") { + handleQueryUpdate(event); + return; + } + + // Unrecognized method, reject the query. + respondErr( + event, + event.source, + false, + "unrecognized method: " + event.data.method, + ); +};