Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#105 Add sandbox execution; move JQ to it #4701

Merged
merged 28 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
36a70a9
Barebones sandbox loading test via browserAction
fregante Nov 25, 2022
ec232c6
Cleanup first POC, then try Messaging
fregante Nov 25, 2022
94d31de
Setup Messenger-like communicator for postMessage
fregante Nov 26, 2022
3025934
Cleanup
fregante Nov 26, 2022
3f7443c
Add `RENDER_NUNJUCKS`
fregante Dec 4, 2022
c7861c7
Add `APPLY_JQ`
fregante Dec 4, 2022
7755586
Basic error handling
fregante Dec 6, 2022
53d0a6c
/2
fregante Dec 6, 2022
85f0bbe
Types
fregante Dec 6, 2022
6a3b3ea
Live-test JQ
fregante Dec 6, 2022
9af5585
Merge remote-tracking branch 'origin/main' into F/mv3/sandbox
fregante Dec 6, 2022
081d569
Ready for production
fregante Dec 6, 2022
b2415c3
Follow review
fregante Dec 10, 2022
8cdfef7
Refactor to facilitate testing + add 1 test
fregante Dec 11, 2022
26f91f5
Use private channel for the response
fregante Dec 11, 2022
c8093f7
Cleanup
fregante Dec 11, 2022
ea1e4ce
Merge remote-tracking branch 'origin/main' into F/mv3/sandbox
fregante Dec 11, 2022
71f6f6d
Split RequestPacket from ResponsePacket
fregante Dec 11, 2022
952a5d3
Test `postMessage`
fregante Dec 11, 2022
a6727aa
Test `addPostMessageListener`
fregante Dec 11, 2022
f908216
Bypass sandbox in other tests
fregante Dec 11, 2022
0e21bd1
Mock @/utils/postMessage (unused)
fregante Dec 11, 2022
5509174
Revert "Mock @/utils/postMessage (unused)"
fregante Dec 11, 2022
b0418c9
Preload sandbox
fregante Dec 11, 2022
8902e38
Extract unrelated changes
fregante Dec 11, 2022
bfa97dc
Lint
fregante Dec 11, 2022
b6d6bd9
Fix useUndo conflict 🤷‍♂️
fregante Dec 12, 2022
8e763be
Types
fregante Dec 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/contentScript/contentScriptCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
} from "@/errors/contextInvalidated";
import { uncaughtErrorHandlers } from "@/telemetry/reportUncaughtErrors";
import { UUID } from "@/core";
import createSandbox from "@/sandbox/messenger/api";

function ignoreContextInvalidatedErrors(
errorEvent: ErrorEvent | PromiseRejectionEvent
Expand All @@ -64,6 +65,7 @@ export async function init(uuid: UUID): Promise<void> {
addListenerForUpdateSelectedElement();
initTelemetry();
initToaster();
createSandbox();

const sender = await whoAmI();

Expand Down
4 changes: 4 additions & 0 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
"run_at": "document_idle"
}
],
"sandbox": {
"pages": ["sandbox.html"]
},
"optional_permissions": ["clipboardWrite", "*://*/*"],
"permissions": [
"activeTab",
Expand All @@ -47,6 +50,7 @@
"web_accessible_resources": [
"css/*",
"bundles/*",
"sandbox.html",
"frame.html",
"frame.css",
"sidebar.html",
Expand Down
51 changes: 51 additions & 0 deletions src/sandbox/messenger/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022 PixieBrix, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/** @file It doesn't actually use the Messenger but this file tries to replicate the pattern */

import postMessage from "@/utils/postMessage";
import { once } from "lodash";

const hiddenIframeStyle = {
position: "absolute",
bottom: "105%",
right: "105%",
visibility: "hidden",
};

const getSandbox = once(() => {
const iframe = document.createElement("iframe");
iframe.src = chrome.runtime.getURL("sandbox.html");
Object.assign(iframe.style, hiddenIframeStyle);
return iframe;
});

export default function createSandbox() {
const sandbox = getSandbox();
document.body.append(sandbox);
setTimeout(async () => {
console.log("SANDBOX: sending PING");
console.log("SANDBOX: received PING response:", await ping());
}, 1000);
fregante marked this conversation as resolved.
Show resolved Hide resolved
}

export async function ping() {
return postMessage({
channel: getSandbox().contentWindow,
id: "SANDBOX_PING",
});
}
twschiller marked this conversation as resolved.
Show resolved Hide resolved
27 changes: 27 additions & 0 deletions src/sandbox/messenger/registration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2022 PixieBrix, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { addPostMessageListener } from "@/utils/postMessage";

/** @file It doesn't actually use the Messenger but this file tries to replicate the pattern */

export default function registerMessenger(): void {
addPostMessageListener("SANDBOX_PING", (payload) => {
console.log("SANDBOX: Received PING payload:", payload);
return "pong";
});
fregante marked this conversation as resolved.
Show resolved Hide resolved
}
19 changes: 19 additions & 0 deletions src/sandbox/sandbox.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--
~ Copyright (C) 2022 PixieBrix, Inc.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<meta charset="utf-8" />
<script src="sandbox.js"></script>
22 changes: 22 additions & 0 deletions src/sandbox/sandbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 PixieBrix, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import registerMessenger from "./messenger/registration";

console.log("SANDBOX: iframe loaded");

registerMessenger();
104 changes: 104 additions & 0 deletions src/utils/postMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2022 PixieBrix, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { UUID } from "@/core";
import { uuidv4 } from "@/types/helpers";
import pDefer from "p-defer";
import pTimeout from "p-timeout";
import { JsonValue, Promisable } from "type-fest";
import validUuidRegex from "@/vendors/validateUuid";

const TIMEOUT_MS = 3000;

type Payload = JsonValue;

interface PixiebrixPacket {
id: string;
payload?: Payload;
pixiebrix: UUID;
}

interface PostMessageInfo {
id: string;
payload?: Payload;
channel: Window;
}

type PostMessageListener = (payload: Payload) => Promisable<Payload>;

/** Use the postMessage API but expect a response from the target */
export default async function postMessage({
id,
payload,
channel,
}: PostMessageInfo): Promise<Payload> {
const packet: PixiebrixPacket = {
id,
payload,
pixiebrix: uuidv4(),
};
const { promise, resolve } = pDefer<Payload>();
const controller = new AbortController();

const listener = ({ origin, data }: MessageEvent<PixiebrixPacket>): void => {
if (origin === "null" && data?.pixiebrix === packet.pixiebrix) {
resolve(data.payload);
}
};

window.addEventListener("message", listener, { signal: controller.signal });

// The origin must be "*" because it's reported as "null" to the outside world
// https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#using_window.postmessage_in_extensions_non-standard
channel.postMessage(packet, "*");
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved

try {
return await pTimeout(promise, {
milliseconds: TIMEOUT_MS,
message: `Message ${id} did not receive a response within ${
TIMEOUT_MS / 1000
} seconds`,
});
} finally {
controller.abort();
}
}

export function addPostMessageListener(
id: string,
listener: PostMessageListener,
{ signal }: { signal?: AbortSignal } = {}
): void {
const rawListener = async ({
data,
source,
origin,
}: MessageEvent<PixiebrixPacket>): Promise<void> => {
if (data?.id === id && validUuidRegex.test(data.pixiebrix)) {
const responsePayload = await listener(data.payload);

const packet: PixiebrixPacket = {
id,
payload: responsePayload,
pixiebrix: data.pixiebrix,
};
source.postMessage(packet, { targetOrigin: origin });
}
};

window.addEventListener("message", rawListener, { signal });
}
7 changes: 7 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ function updateManifestToV3(manifest) {
policy.remove("script-src", "'unsafe-eval'");
manifest.content_security_policy = {
extension_pages: policy.toString(),

// Set the native default CSP
// https://developer.chrome.com/docs/extensions/mv3/manifest/sandbox/
sandbox:
"sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';",
fregante marked this conversation as resolved.
Show resolved Hide resolved
};

// Replace background script
Expand Down Expand Up @@ -259,6 +264,8 @@ module.exports = (env, options) =>
"pageEditor/pageEditor",
"options/options",
"sidebar/sidebar",
"sandbox/sandbox",

"tinyPages/ephemeralForm",
"tinyPages/permissionsPopup",
"tinyPages/browserActionInstantHandler",
Expand Down