Add Scratch.fetch, Scratch.openWindow, and Scratch.redirect stubs in sandboxed extensions

Also includes the appropriate can[Fetch|OpenWindow|Redirect] methods.
Fetching is always allowed. The other methods are always rejected.
The Worker-based sandbox does not have a window.open method and the
iframe-based sandbox has a feature policy that should prevent them from
opening windows already.
This commit is contained in:
GarboMuffin committed 2023-03-18 13:15:05 -05:00
1 parent c0a1c3db83
commit 47adea8107
2 files changed
+66 -1

No files matched your search

+8 -1
View File
@@ -69,7 +69,14 @@ class ExtensionWorker {
}
global.Scratch = global.Scratch || {};
Object.assign(global.Scratch, ScratchCommon);
Object.assign(global.Scratch, ScratchCommon, {
canFetch: () => Promise.resolve(true),
fetch: (url, options) => fetch(url, options),
canOpenWindow: () => Promise.resolve(false),
openWindow: () => Promise.reject(new Error('Scratch.openWindow not supported in sandboxed extensions')),
canRedirect: () => Promise.resolve(false),
redirect: () => Promise.reject(new Error('Scratch.redirect not supported in sandboxed extensions'))
});
/**
* Expose only specific parts of the worker to extensions.
+58
View File
@@ -0,0 +1,58 @@
const {test} = require('tap');
/* globals Request */
global.Request = class {
constructor (url) {
this.url = url;
}
};
global.fetch = (url, options = {}) => (
Promise.resolve(`[Response ${url instanceof Request ? url.url : url} options=${JSON.stringify(options)}]`)
);
// Need to trick the extension API to think it's running in a worker
// It will not actually use this object ever.
global.self = {};
// This will install extension worker APIs onto `global`
require('../../src/extension-support/extension-worker');
test('basic API', t => {
t.type(global.Scratch.extensions.register, 'function');
t.equal(global.Scratch.ArgumentType.BOOLEAN, 'Boolean');
t.equal(global.Scratch.BlockType.REPORTER, 'reporter');
t.end();
});
test('not unsandboxed', t => {
t.not(global.Scratch.extensions.unsandboxed, true);
t.end();
});
test('Cast', t => {
// Cast is thoroughly tested elsewhere
t.equal(global.Scratch.Cast.toString(5), '5');
t.equal(global.Scratch.Cast.toNumber(' 5'), 5);
t.equal(global.Scratch.Cast.toBoolean('true'), true);
t.end();
});
test('fetch', async t => {
t.equal(await global.Scratch.canFetch('https://untrusted.example/'), true);
t.equal(await global.Scratch.fetch('https://untrusted.example/'), '[Response https://untrusted.example/ options={}]');
t.equal(await global.Scratch.fetch('https://untrusted.example/', {
method: 'POST'
}), `[Response https://untrusted.example/ options={"method":"POST"}]`);
t.end();
});
test('openWindow', async t => {
t.equal(await global.Scratch.canOpenWindow('https://example.com/'), false);
await t.rejects(global.Scratch.openWindow('https://example.com/'), /^Scratch\.openWindow not supported in sandboxed extensions$/);
t.end();
});
test('redirect', async t => {
t.equal(await global.Scratch.canRedirect('https://example.com/'), false);
await t.rejects(global.Scratch.redirect('https://example.com/'), /^Scratch\.redirect not supported in sandboxed extensions$/);
t.end();
});