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

Add plugin context to get browser storage #101

Merged
merged 18 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
16 changes: 16 additions & 0 deletions src/components/PluginInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@

export default function PluginUploadInfo(): ReactElement {
const [error, showError] = useState('');
const [pluginBuffer, setPluginBuffer] = useState<ArrayBuffer | any>(null);

Check warning on line 31 in src/components/PluginInfo/index.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
const [pluginContent, setPluginContent] = useState<PluginConfig | null>(null);

const onAddPlugin = useCallback(
async (evt: React.MouseEvent<HTMLButtonElement>) => {

Check warning on line 35 in src/components/PluginInfo/index.tsx

View workflow job for this annotation

GitHub Actions / build

'evt' is defined but never used
try {
await addPlugin(Buffer.from(pluginBuffer).toString('hex'));
setPluginContent(null);
} catch (e: any) {

Check warning on line 39 in src/components/PluginInfo/index.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
showError(e?.message || 'Invalid Plugin');
}
},
Expand All @@ -52,7 +52,7 @@
const plugin = await makePlugin(arrayBuffer);
setPluginContent(await getPluginConfig(plugin));
setPluginBuffer(arrayBuffer);
} catch (e: any) {

Check warning on line 55 in src/components/PluginInfo/index.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
showError(e?.message || 'Invalid Plugin');
} finally {
evt.target.value = '';
Expand Down Expand Up @@ -108,11 +108,11 @@
const { pluginContent, onClose, onAddPlugin, children } = props;

const header = Children.toArray(children).filter(
(c: any) => c.type.name === 'PluginInfoModalHeader',

Check warning on line 111 in src/components/PluginInfo/index.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
)[0];

const content = Children.toArray(children).filter(
(c: any) => c.type.name === 'PluginInfoModalContent',

Check warning on line 115 in src/components/PluginInfo/index.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
)[0];

return (
Expand Down Expand Up @@ -193,6 +193,22 @@
</span>
</PermissionDescription>
)}
{pluginContent.localStorage && (
<PermissionDescription fa="fa-solid fa-database">
<span className="cursor-default">
<span className="mr-1">Access local storage storage from</span>
<MultipleParts parts={pluginContent.localStorage} />
</span>
</PermissionDescription>
)}
{pluginContent.sessionStorage && (
<PermissionDescription fa="fa-solid fa-database">
<span className="cursor-default">
<span className="mr-1">Access session storage from</span>
<MultipleParts parts={pluginContent.sessionStorage} />
</span>
</PermissionDescription>
)}
{pluginContent.requests && (
<PermissionDescription fa="fa-solid fa-globe">
<span className="cursor-default">
Expand Down
27 changes: 26 additions & 1 deletion src/entries/Background/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const cookiesDb = db.sublevel<string, boolean>('cookies', {
const headersDb = db.sublevel<string, boolean>('headers', {
valueEncoding: 'json',
});
const storageDb = db.sublevel<string, any>('storage', {
valueEncoding: 'json',
});

const appDb = db.sublevel<string, any>('app', {
valueEncoding: 'json',
});
Expand Down Expand Up @@ -370,7 +374,6 @@ export async function getHeaders(host: string, name: string) {
return null;
}
}

export async function getHeadersByHost(host: string) {
const ret: { [key: string]: string } = {};
for await (const [key, value] of headersDb.sublevel(host).iterator()) {
Expand All @@ -379,6 +382,28 @@ export async function getHeadersByHost(host: string) {
return ret;
}

export async function setStorage(host: string, name: string, value: string) {
return mutex.runExclusive(async () => {
await storageDb.sublevel(host).put(name, value);
return true;
});
}

export async function clearStorage(host: string) {
return mutex.runExclusive(async () => {
await storageDb.sublevel(host).clear();
return true;
});
}

export async function getStorageByHost(host: string) {
const ret: { [key: string]: string } = {};
for await (const [key, value] of storageDb.sublevel(host).iterator()) {
ret[key] = value;
}
return ret;
}

async function getDefaultPluginsInstalled(): Promise<boolean> {
return appDb.get(AppDatabaseKey.DefaultPluginsInstalled).catch(() => false);
}
Expand Down
1 change: 0 additions & 1 deletion src/entries/Background/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import browser from 'webextension-polyfill';
import { addRequest } from '../../reducers/requests';
import { urlify } from '../../utils/misc';
import { setCookies, setHeaders } from './db';

export const onSendHeaders = (
details: browser.WebRequest.OnSendHeadersDetailsType,
) => {
Expand Down
24 changes: 24 additions & 0 deletions src/entries/Background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,32 @@ import { deleteCacheByTabId } from './cache';
import browser from 'webextension-polyfill';
import { getAppState, setDefaultPluginsInstalled } from './db';
import { installPlugin } from './plugins/utils';
import { BackgroundActiontype } from './rpc';
import { setStorage } from './db';

(async () => {
chrome.runtime.onMessage.addListener(async (request, sender) => {
if (
request.type === BackgroundActiontype.get_browser_storage &&
Codetrauma marked this conversation as resolved.
Show resolved Hide resolved
sender.tab?.url
) {
const url = new URL(sender.tab.url);
const hostname = url.hostname;
const localStorage: { [key: string]: string } =
request.storage.localStorage;
const sessionStorage: { [key: string]: string } =
request.storage.sessionStorage;

for (const [key, value] of Object.entries(localStorage || {})) {
await setStorage(hostname, `localStorage:${key}`, value);
}

for (const [key, value] of Object.entries(sessionStorage || {})) {
await setStorage(hostname, `sessionStorage:${key}`, value);
}
}
});

browser.webRequest.onSendHeaders.addListener(
onSendHeaders,
{
Expand Down
1 change: 1 addition & 0 deletions src/entries/Background/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export enum BackgroundActiontype {
get_logging_level = 'get_logging_level',
get_app_state = 'get_app_state',
set_default_plugins_installed = 'set_default_plugins_installed',
get_browser_storage = 'get_browser_storage',
}

export type BackgroundAction = {
Expand Down
12 changes: 11 additions & 1 deletion src/entries/Content/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import browser from 'webextension-polyfill';
import browser, { browserAction } from 'webextension-polyfill';
import { ContentScriptRequest, ContentScriptTypes, RPCServer } from './rpc';
import { BackgroundActiontype, RequestHistory } from '../Background/rpc';
import { urlify } from '../../utils/misc';
Expand All @@ -7,6 +7,16 @@ import { urlify } from '../../utils/misc';
loadScript('content.bundle.js');
const server = new RPCServer();

const storage = {
localStorage: localStorage,
sessionStorage: sessionStorage,
};

chrome.runtime.sendMessage({
type: BackgroundActiontype.get_browser_storage,
storage,
});

server.on(ContentScriptTypes.connect, async () => {
Codetrauma marked this conversation as resolved.
Show resolved Hide resolved
const connected = await browser.runtime.sendMessage({
type: BackgroundActiontype.connect_request,
Expand Down
53 changes: 49 additions & 4 deletions src/utils/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import NodeCache from 'node-cache';
import { getNotaryApi, getProxyApi } from './storage';
import { minimatch } from 'minimatch';
import { getCookiesByHost, getHeadersByHost } from '../entries/Background/db';

import { getStorageByHost } from '../entries/Background/db';
const charwise = require('charwise');

export function urlify(
Expand Down Expand Up @@ -233,7 +233,6 @@ export const makePlugin = async (
return context.store(`${id}`);
},
};

const funcs: {
[key: string]: (callContext: CallContext, ...args: any[]) => any;
} = {};
Expand All @@ -250,6 +249,41 @@ export const makePlugin = async (
}
}

if (config?.localStorage) {
const localStorage: { [hostname: string]: { [key: string]: string } } = {};
for (const host of config.localStorage) {
const cache = await getStorageByHost(host);

const localStorageEntries: { [key: string]: string } = {};
for (const [key, value] of Object.entries(cache)) {
if (key.startsWith('localStorage')) {
localStorageEntries[key.replace('localStorage:', '')] = value;
}
}
localStorage[host] = localStorageEntries;
}
// @ts-ignore
injectedConfig.localStorage = JSON.stringify(localStorage);
}

if (config?.sessionStorage) {
const sessionStorage: { [hostname: string]: { [key: string]: string } } =
{};
for (const host of config.sessionStorage) {
const cache = await getStorageByHost(host);

const sessionStorageEntries: { [key: string]: string } = {};
for (const [key, value] of Object.entries(cache)) {
if (key.startsWith('sessionStorage')) {
sessionStorageEntries[key.replace('sessionStorage:', '')] = value;
}
}
sessionStorage[host] = sessionStorageEntries;
}
// @ts-ignore
injectedConfig.sessionStorage = JSON.stringify(sessionStorage);
}

if (config?.cookies) {
const cookies: { [hostname: string]: { [key: string]: string } } = {};
for (const host of config.cookies) {
Expand Down Expand Up @@ -293,12 +327,14 @@ export type StepConfig = {

export type PluginConfig = {
title: string; // The name of the plugin
description: string; // A description of the plugin's purpose
description: string; // A description of the plugin purpose
icon?: string; // A base64-encoded image string representing the plugin's icon (optional)
steps?: StepConfig[]; // An array describing the UI steps and behavior (see Step UI below) (optional)
hostFunctions?: string[]; // Host functions that the plugin will have access to
cookies?: string[]; // Cookies the plugin will have access to, cached by the extension from specified hosts (optional)
headers?: string[]; // Headers the plugin will have access to, cached by the extension from specified hosts (optional)
localStorage: string[]; // LocalStorage the plugin will have access to, cached by the extension from specified hosts (optional)
sessionStorage: string[]; // SessionStorage the plugin will have access to, cached by the extension from specified hosts (optional)
Codetrauma marked this conversation as resolved.
Show resolved Hide resolved
requests: { method: string; url: string }[]; // List of requests that the plugin is allowed to make
notaryUrls?: string[]; // List of notary services that the plugin is allowed to use (optional)
proxyUrls?: string[]; // List of websocket proxies that the plugin is allowed to use (optional)
Expand Down Expand Up @@ -348,7 +384,16 @@ export const getPluginConfig = async (
assert(typeof name === 'string' && name.length);
}
}

if (config.localStorage) {
for (const name of config.localStorage) {
assert(typeof name === 'string' && name.length);
}
}
if (config.sessionStorage) {
for (const name of config.sessionStorage) {
assert(typeof name === 'string' && name.length);
}
}
if (config.headers) {
for (const name of config.headers) {
assert(typeof name === 'string' && name.length);
Expand Down
Loading