mirror of
https://github.com/tlsnotary/tlsn-extension.git
synced 2026-01-09 13:08:04 -05:00
Compare commits
3 Commits
0.1.0.1200
...
add-verifi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
104457d2c6 | ||
|
|
d20441b553 | ||
|
|
f955ab7fd5 |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tlsn-extension-${{ github.ref_name }}.zip
|
||||
path: .
|
||||
path: ./tlsn-extension-${{ github.ref_name }}.zip
|
||||
|
||||
- name: 📦 Add extension zip file to release
|
||||
env:
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# Chrome Extension (MV3) for TLSNotary
|
||||
|
||||
> [!IMPORTANT]
|
||||
> ⚠️ When running the extension against a [notary server](https://github.com/tlsnotary/tlsn/tree/main/crates/notary/server), please ensure that the server's version is the same as the version of this extension
|
||||
> ⚠️ When running the extension against a [notary server](https://github.com/tlsnotary/tlsn/tree/dev/notary-server), please ensure that the server's version is the same as the version of this extension
|
||||
|
||||
## License
|
||||
This repository is licensed under either of
|
||||
|
||||
1902
package-lock.json
generated
1902
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tlsn-extension",
|
||||
"version": "0.1.0.1200",
|
||||
"version": "0.1.0.1000",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -40,7 +40,7 @@
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tlsn-js": "0.1.0-alpha.12.0"
|
||||
"tlsn-js": "0.1.0-alpha.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.12",
|
||||
@@ -94,4 +94,4 @@
|
||||
"webpack-ext-reloader": "^1.1.12",
|
||||
"zip-webpack-plugin": "^4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,7 @@ import React, {
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
fetchPluginHashes,
|
||||
removePlugin,
|
||||
runPlugin,
|
||||
addPlugin,
|
||||
} from '../../utils/rpc';
|
||||
import { fetchPluginHashes, removePlugin, runPlugin } from '../../utils/rpc';
|
||||
import { usePluginHashes } from '../../reducers/plugins';
|
||||
import {
|
||||
getPluginConfig,
|
||||
@@ -36,78 +31,20 @@ export function PluginList({
|
||||
className,
|
||||
unremovable,
|
||||
onClick,
|
||||
showAddButton = false,
|
||||
}: {
|
||||
className?: string;
|
||||
unremovable?: boolean;
|
||||
onClick?: (hash: string) => void;
|
||||
showAddButton?: boolean;
|
||||
}): ReactElement {
|
||||
const hashes = usePluginHashes();
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPluginHashes();
|
||||
}, []);
|
||||
|
||||
const handleFileUpload = useCallback(
|
||||
async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (!file.name.endsWith('.wasm')) {
|
||||
alert('Please select a .wasm file');
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading(true);
|
||||
try {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const hex = Buffer.from(arrayBuffer).toString('hex');
|
||||
const url = `file://${file.name}`;
|
||||
|
||||
await addPlugin(hex, url);
|
||||
await fetchPluginHashes();
|
||||
} catch (error: any) {
|
||||
alert(`Failed to add plugin: ${error.message}`);
|
||||
} finally {
|
||||
setUploading(false);
|
||||
e.target.value = '';
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames('flex flex-col flex-nowrap gap-1', className)}>
|
||||
{showAddButton && (
|
||||
<div className="relative">
|
||||
<input
|
||||
type="file"
|
||||
accept=".wasm"
|
||||
onChange={handleFileUpload}
|
||||
disabled={uploading}
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10"
|
||||
/>
|
||||
<button
|
||||
className="flex flex-row items-center justify-center gap-2 p-3 border-2 border-dashed border-slate-300 rounded-lg text-slate-500 hover:text-slate-700 hover:border-slate-400 transition-colors cursor-pointer w-full"
|
||||
disabled={uploading}
|
||||
>
|
||||
{uploading ? (
|
||||
<>
|
||||
<Icon fa="fa-solid fa-spinner" className="animate-spin" />
|
||||
<span>Adding Plugin...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon fa="fa-solid fa-plus" />
|
||||
<span>Add Plugin (.wasm file)</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!hashes.length && !showAddButton && (
|
||||
{!hashes.length && (
|
||||
<div className="flex flex-col items-center justify-center text-slate-400 cursor-default select-none">
|
||||
<div>No available plugins</div>
|
||||
</div>
|
||||
|
||||
28
src/entries/Background/cache.ts
Normal file
28
src/entries/Background/cache.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import NodeCache from 'node-cache';
|
||||
|
||||
let RequestsLogs: {
|
||||
[tabId: string]: NodeCache;
|
||||
} = {};
|
||||
|
||||
export const deleteCacheByTabId = (tabId: number) => {
|
||||
delete RequestsLogs[tabId];
|
||||
};
|
||||
|
||||
export const getCacheByTabId = (tabId: number): NodeCache => {
|
||||
RequestsLogs[tabId] =
|
||||
RequestsLogs[tabId] ||
|
||||
new NodeCache({
|
||||
stdTTL: 60 * 5, // default 5m TTL
|
||||
maxKeys: 1000000,
|
||||
});
|
||||
|
||||
return RequestsLogs[tabId];
|
||||
};
|
||||
|
||||
export const clearRequestCache = () => {
|
||||
RequestsLogs = {};
|
||||
};
|
||||
|
||||
export const clearCache = () => {
|
||||
clearRequestCache();
|
||||
};
|
||||
@@ -1,12 +1,6 @@
|
||||
import { Level } from 'level';
|
||||
import { AbstractSublevel } from 'abstract-level';
|
||||
import { PluginConfig, PluginMetadata, sha256, urlify } from '../../utils/misc';
|
||||
import {
|
||||
RequestHistory,
|
||||
RequestLog,
|
||||
RequestProgress,
|
||||
UpsertRequestLog,
|
||||
} from './rpc';
|
||||
import { RequestHistory, RequestProgress } from './rpc';
|
||||
import mutex from './mutex';
|
||||
import { minimatch } from 'minimatch';
|
||||
const charwise = require('charwise');
|
||||
@@ -29,6 +23,12 @@ const pluginMetadataDb = db.sublevel<string, PluginMetadata>('pluginMetadata', {
|
||||
const connectionDb = db.sublevel<string, boolean>('connections', {
|
||||
valueEncoding: 'json',
|
||||
});
|
||||
const cookiesDb = db.sublevel<string, boolean>('cookies', {
|
||||
valueEncoding: 'json',
|
||||
});
|
||||
const headersDb = db.sublevel<string, boolean>('headers', {
|
||||
valueEncoding: 'json',
|
||||
});
|
||||
const localStorageDb = db.sublevel<string, any>('sessionStorage', {
|
||||
valueEncoding: 'json',
|
||||
});
|
||||
@@ -38,81 +38,10 @@ const sessionStorageDb = db.sublevel<string, any>('localStorage', {
|
||||
const appDb = db.sublevel<string, any>('app', {
|
||||
valueEncoding: 'json',
|
||||
});
|
||||
const requestDb = db.sublevel<string, any>('requests', {
|
||||
valueEncoding: 'json',
|
||||
});
|
||||
|
||||
enum AppDatabaseKey {
|
||||
DefaultPluginsInstalled = 'DefaultPluginsInstalled',
|
||||
}
|
||||
|
||||
export async function upsertRequestLog(request: UpsertRequestLog) {
|
||||
const existing = await getRequestLog(request.requestId);
|
||||
|
||||
if (existing) {
|
||||
await requestDb.put(request.requestId, {
|
||||
...existing,
|
||||
...request,
|
||||
});
|
||||
} else if (request.url) {
|
||||
const host = urlify(request.url)?.host;
|
||||
if (host) {
|
||||
await requestDb.put(request.requestId, request);
|
||||
await requestDb
|
||||
.sublevel(request.tabId.toString())
|
||||
.put(request.requestId, '');
|
||||
await requestDb.sublevel(host).put(request.requestId, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRequestLog(
|
||||
requestId: string,
|
||||
): Promise<RequestLog | null> {
|
||||
return requestDb.get(requestId).catch(() => null);
|
||||
}
|
||||
|
||||
export async function removeRequestLog(requestId: string) {
|
||||
const existing = await getRequestLog(requestId);
|
||||
if (existing) {
|
||||
await requestDb.del(requestId);
|
||||
await requestDb.sublevel(existing.tabId.toString()).del(requestId);
|
||||
const host = urlify(existing.url)?.host;
|
||||
if (host) {
|
||||
await requestDb.sublevel(host).del(requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeRequestLogsByTabId(tabId: number) {
|
||||
const requests = requestDb.sublevel(tabId.toString());
|
||||
for await (const [requestId] of requests.iterator()) {
|
||||
await removeRequestLog(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRequestLogsByTabId(tabId: number) {
|
||||
const requests = requestDb.sublevel(tabId.toString());
|
||||
const ret: RequestLog[] = [];
|
||||
for await (const [requestId] of requests.iterator()) {
|
||||
ret.push(await requestDb.get(requestId));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export async function getRequestLogsByHost(host: string) {
|
||||
const requests = requestDb.sublevel(host);
|
||||
const ret: RequestLog[] = [];
|
||||
for await (const [requestId] of requests.iterator()) {
|
||||
ret.push(await requestDb.get(requestId));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export async function clearAllRequestLogs() {
|
||||
await requestDb.clear();
|
||||
}
|
||||
|
||||
export async function addNotaryRequest(
|
||||
now = Date.now(),
|
||||
request: Omit<RequestHistory, 'status' | 'id'>,
|
||||
@@ -410,43 +339,48 @@ export async function setConnection(origin: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getCookiesByHost(linkOrHost: string) {
|
||||
export async function setCookies(host: string, name: string, value: string) {
|
||||
return mutex.runExclusive(async () => {
|
||||
await cookiesDb.sublevel(host).put(name, value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export async function clearCookies(host: string) {
|
||||
return mutex.runExclusive(async () => {
|
||||
await cookiesDb.sublevel(host).clear();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getCookies(link: string, name: string) {
|
||||
try {
|
||||
const existing = await cookiesDb.sublevel(link).get(name);
|
||||
return existing;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCookiesByHost(link: string) {
|
||||
const ret: { [key: string]: string } = {};
|
||||
const url = urlify(linkOrHost);
|
||||
const isHost = !url;
|
||||
const host = isHost ? linkOrHost : url.host;
|
||||
const requests = await getRequestLogsByHost(host);
|
||||
const links: { [k: string]: boolean } = {};
|
||||
const url = urlify(link);
|
||||
|
||||
let filteredRequest: RequestLog | null = null;
|
||||
|
||||
for (const request of requests) {
|
||||
if (isHost) {
|
||||
if (!filteredRequest || filteredRequest.updatedAt > request.updatedAt) {
|
||||
filteredRequest = request;
|
||||
}
|
||||
} else {
|
||||
const { origin, pathname } = urlify(request.url) || {};
|
||||
const link = [origin, pathname].join('');
|
||||
if (
|
||||
minimatch(link, linkOrHost) &&
|
||||
(!filteredRequest || filteredRequest.updatedAt > request.updatedAt)
|
||||
) {
|
||||
filteredRequest = request;
|
||||
}
|
||||
}
|
||||
for await (const sublevel of cookiesDb.keys({ keyEncoding: 'utf8' })) {
|
||||
const l = sublevel.split('!')[1];
|
||||
links[l] = true;
|
||||
}
|
||||
|
||||
if (!filteredRequest) return ret;
|
||||
const cookieLink = url
|
||||
? Object.keys(links).filter((l) => minimatch(l, link))[0]
|
||||
: Object.keys(links).filter((l) => urlify(l)?.host === link)[0];
|
||||
|
||||
for (const header of filteredRequest.requestHeaders) {
|
||||
if (header.name.toLowerCase() === 'cookie') {
|
||||
header.value?.split(';').forEach((cookie) => {
|
||||
const [name, value] = cookie.split('=');
|
||||
ret[name.trim()] = value.trim();
|
||||
});
|
||||
}
|
||||
if (!cookieLink) return ret;
|
||||
|
||||
for await (const [key, value] of cookiesDb.sublevel(cookieLink).iterator()) {
|
||||
ret[key] = value;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -466,40 +400,49 @@ export async function getConnection(origin: string) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export async function getHeadersByHost(linkOrHost: string) {
|
||||
|
||||
export async function setHeaders(link: string, name: string, value?: string) {
|
||||
if (!value) return null;
|
||||
return mutex.runExclusive(async () => {
|
||||
await headersDb.sublevel(link).put(name, value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export async function clearHeaders(host: string) {
|
||||
return mutex.runExclusive(async () => {
|
||||
await headersDb.sublevel(host).clear();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getHeaders(host: string, name: string) {
|
||||
try {
|
||||
const existing = await headersDb.sublevel(host).get(name);
|
||||
return existing;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export async function getHeadersByHost(link: string) {
|
||||
const ret: { [key: string]: string } = {};
|
||||
const url = urlify(linkOrHost);
|
||||
const isHost = !url;
|
||||
const host = isHost ? linkOrHost : url.host;
|
||||
const requests = await getRequestLogsByHost(host);
|
||||
const url = urlify(link);
|
||||
|
||||
let filteredRequest: RequestLog | null = null;
|
||||
|
||||
for (const request of requests) {
|
||||
if (isHost) {
|
||||
if (!filteredRequest || filteredRequest.updatedAt > request.updatedAt) {
|
||||
filteredRequest = request;
|
||||
}
|
||||
} else {
|
||||
const { origin, pathname } = urlify(request.url) || {};
|
||||
const link = [origin, pathname].join('');
|
||||
if (
|
||||
minimatch(link, linkOrHost) &&
|
||||
(!filteredRequest || filteredRequest.updatedAt > request.updatedAt)
|
||||
) {
|
||||
filteredRequest = request;
|
||||
}
|
||||
}
|
||||
const links: { [k: string]: boolean } = {};
|
||||
for await (const sublevel of headersDb.keys({ keyEncoding: 'utf8' })) {
|
||||
const l = sublevel.split('!')[1];
|
||||
links[l] = true;
|
||||
}
|
||||
|
||||
if (!filteredRequest) return ret;
|
||||
const headerLink = url
|
||||
? Object.keys(links).filter((l) => minimatch(l, link))[0]
|
||||
: Object.keys(links).filter((l) => urlify(l)?.host === link)[0];
|
||||
|
||||
for (const header of filteredRequest.requestHeaders) {
|
||||
if (header.name.toLowerCase() !== 'cookie') {
|
||||
ret[header.name] = header.value || '';
|
||||
}
|
||||
if (!headerLink) return ret;
|
||||
|
||||
for await (const [key, value] of headersDb.sublevel(headerLink).iterator()) {
|
||||
ret[key] = value;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -572,62 +515,3 @@ export async function getAppState() {
|
||||
defaultPluginsInstalled: await getDefaultPluginsInstalled(),
|
||||
};
|
||||
}
|
||||
|
||||
export async function resetDB() {
|
||||
return mutex.runExclusive(async () => {
|
||||
return Promise.all([
|
||||
localStorageDb.clear(),
|
||||
sessionStorageDb.clear(),
|
||||
requestDb.clear(),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDBSizeByRoot(
|
||||
rootDB: AbstractSublevel<Level, any, any, any>,
|
||||
): Promise<number> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let size = 0;
|
||||
|
||||
for await (const sublevel of rootDB.keys({ keyEncoding: 'utf8' })) {
|
||||
const link = sublevel.split('!')[1];
|
||||
const sub = rootDB.sublevel(link);
|
||||
for await (const [key, value] of sub.iterator()) {
|
||||
size += key.length + value.length;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(size);
|
||||
});
|
||||
}
|
||||
|
||||
export async function getRecursiveDBSize(
|
||||
db: AbstractSublevel<Level, any, any, any>,
|
||||
): Promise<number> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let size = 0;
|
||||
for await (const sublevel of db.keys({ keyEncoding: 'utf8' })) {
|
||||
const parts = sublevel.split('!');
|
||||
if (parts.length === 1) {
|
||||
const value = await db.get(parts[0]);
|
||||
size += parts[0].length + (value ? JSON.stringify(value).length : 0);
|
||||
} else {
|
||||
const sub = db.sublevel(parts[1]);
|
||||
size +=
|
||||
(await getRecursiveDBSize(
|
||||
sub as unknown as AbstractSublevel<Level, any, any, any>,
|
||||
)) + parts[1].length;
|
||||
}
|
||||
}
|
||||
resolve(size);
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDBSize(): Promise<number> {
|
||||
const sizes = await Promise.all([
|
||||
getDBSizeByRoot(localStorageDb),
|
||||
getDBSizeByRoot(sessionStorageDb),
|
||||
getRecursiveDBSize(requestDb),
|
||||
]);
|
||||
return sizes.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BackgroundActiontype } from './rpc';
|
||||
import { getCacheByTabId } from './cache';
|
||||
import { BackgroundActiontype, RequestLog } from './rpc';
|
||||
import mutex from './mutex';
|
||||
import browser from 'webextension-polyfill';
|
||||
import { addRequest } from '../../reducers/requests';
|
||||
import { urlify } from '../../utils/misc';
|
||||
import { getRequestLog, upsertRequestLog } from './db';
|
||||
|
||||
import { getHeadersByHost, setCookies, setHeaders } from './db';
|
||||
export const onSendHeaders = (
|
||||
details: browser.WebRequest.OnSendHeadersDetailsType,
|
||||
) => {
|
||||
@@ -12,22 +12,40 @@ export const onSendHeaders = (
|
||||
const { method, tabId, requestId } = details;
|
||||
|
||||
if (method !== 'OPTIONS') {
|
||||
const cache = getCacheByTabId(tabId);
|
||||
const existing = cache.get<RequestLog>(requestId);
|
||||
const { origin, pathname } = urlify(details.url) || {};
|
||||
|
||||
const link = [origin, pathname].join('');
|
||||
|
||||
if (link && details.requestHeaders) {
|
||||
upsertRequestLog({
|
||||
method: details.method as 'GET' | 'POST',
|
||||
type: details.type,
|
||||
url: details.url,
|
||||
initiator: details.initiator || null,
|
||||
requestHeaders: details.requestHeaders || [],
|
||||
tabId: tabId,
|
||||
requestId: requestId,
|
||||
updatedAt: Date.now(),
|
||||
details.requestHeaders.forEach((header) => {
|
||||
const { name, value } = header;
|
||||
if (/^cookie$/i.test(name) && value) {
|
||||
value.split(';').forEach((cookieStr) => {
|
||||
const index = cookieStr.indexOf('=');
|
||||
if (index !== -1) {
|
||||
const cookieName = cookieStr.slice(0, index).trim();
|
||||
const cookieValue = cookieStr.slice(index + 1);
|
||||
setCookies(link, cookieName, cookieValue);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setHeaders(link, name, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cache.set(requestId, {
|
||||
...existing,
|
||||
method: details.method as 'GET' | 'POST',
|
||||
type: details.type,
|
||||
url: details.url,
|
||||
initiator: details.initiator || null,
|
||||
requestHeaders: details.requestHeaders || [],
|
||||
tabId: tabId,
|
||||
requestId: requestId,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -41,30 +59,24 @@ export const onBeforeRequest = (
|
||||
if (method === 'OPTIONS') return;
|
||||
|
||||
if (requestBody) {
|
||||
const cache = getCacheByTabId(tabId);
|
||||
const existing = cache.get<RequestLog>(requestId);
|
||||
|
||||
if (requestBody.raw && requestBody.raw[0]?.bytes) {
|
||||
try {
|
||||
await upsertRequestLog({
|
||||
cache.set(requestId, {
|
||||
...existing,
|
||||
requestBody: Buffer.from(requestBody.raw[0].bytes).toString(
|
||||
'utf-8',
|
||||
),
|
||||
requestId: requestId,
|
||||
tabId: tabId,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
} else if (requestBody.formData) {
|
||||
await upsertRequestLog({
|
||||
formData: Object.fromEntries(
|
||||
Object.entries(requestBody.formData).map(([key, value]) => [
|
||||
key,
|
||||
Array.isArray(value) ? value : [value],
|
||||
]),
|
||||
),
|
||||
requestId: requestId,
|
||||
tabId: tabId,
|
||||
updatedAt: Date.now(),
|
||||
cache.set(requestId, {
|
||||
...existing,
|
||||
formData: requestBody.formData,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -79,7 +91,12 @@ export const onResponseStarted = (
|
||||
|
||||
if (method === 'OPTIONS') return;
|
||||
|
||||
await upsertRequestLog({
|
||||
const cache = getCacheByTabId(tabId);
|
||||
|
||||
const existing = cache.get<RequestLog>(requestId);
|
||||
const newLog: RequestLog = {
|
||||
requestHeaders: [],
|
||||
...existing,
|
||||
method: details.method,
|
||||
type: details.type,
|
||||
url: details.url,
|
||||
@@ -87,15 +104,9 @@ export const onResponseStarted = (
|
||||
tabId: tabId,
|
||||
requestId: requestId,
|
||||
responseHeaders,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
};
|
||||
|
||||
const newLog = await getRequestLog(requestId);
|
||||
|
||||
if (!newLog) {
|
||||
console.error('Request log not found', requestId);
|
||||
return;
|
||||
}
|
||||
cache.set(requestId, newLog);
|
||||
|
||||
chrome.runtime.sendMessage({
|
||||
type: BackgroundActiontype.push_action,
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { onBeforeRequest, onResponseStarted, onSendHeaders } from './handlers';
|
||||
import { deleteCacheByTabId } from './cache';
|
||||
import browser from 'webextension-polyfill';
|
||||
import {
|
||||
getAppState,
|
||||
removePlugin,
|
||||
removeRequestLogsByTabId,
|
||||
setDefaultPluginsInstalled,
|
||||
} from './db';
|
||||
import { getAppState, removePlugin, setDefaultPluginsInstalled } from './db';
|
||||
import { installPlugin } from './plugins/utils';
|
||||
|
||||
(async () => {
|
||||
@@ -34,7 +30,7 @@ import { installPlugin } from './plugins/utils';
|
||||
);
|
||||
|
||||
browser.tabs.onRemoved.addListener((tabId) => {
|
||||
removeRequestLogsByTabId(tabId);
|
||||
deleteCacheByTabId(tabId);
|
||||
});
|
||||
|
||||
const { defaultPluginsInstalled } = await getAppState();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import browser from 'webextension-polyfill';
|
||||
import { clearCache, getCacheByTabId } from './cache';
|
||||
import { addRequestHistory, setRequests } from '../../reducers/history';
|
||||
import {
|
||||
addNotaryRequest,
|
||||
@@ -23,8 +24,6 @@ import {
|
||||
setLocalStorage,
|
||||
setSessionStorage,
|
||||
setNotaryRequestProgress,
|
||||
getRequestLogsByTabId,
|
||||
clearAllRequestLogs,
|
||||
} from './db';
|
||||
import { addOnePlugin, removeOnePlugin } from '../../reducers/plugins';
|
||||
import {
|
||||
@@ -55,7 +54,6 @@ import {
|
||||
sendMessage,
|
||||
sendPairedMessage,
|
||||
} from './ws';
|
||||
|
||||
import { parseHttpMessage } from '../../utils/parser';
|
||||
import { mapStringToRange, subtractRanges } from 'tlsn-js';
|
||||
import { PresentationJSON } from 'tlsn-js/build/types';
|
||||
@@ -143,23 +141,6 @@ export type RequestLog = {
|
||||
[k: string]: string[];
|
||||
};
|
||||
responseHeaders?: browser.WebRequest.HttpHeaders;
|
||||
updatedAt: number;
|
||||
};
|
||||
|
||||
export type UpsertRequestLog = {
|
||||
requestId: string;
|
||||
tabId: number;
|
||||
method?: string;
|
||||
type?: string;
|
||||
url?: string;
|
||||
initiator?: string | null;
|
||||
requestHeaders?: browser.WebRequest.HttpHeaders;
|
||||
requestBody?: string;
|
||||
formData?: {
|
||||
[k: string]: string[];
|
||||
};
|
||||
responseHeaders?: browser.WebRequest.HttpHeaders;
|
||||
updatedAt: number;
|
||||
};
|
||||
|
||||
export enum RequestProgress {
|
||||
@@ -231,7 +212,7 @@ export const initRPC = () => {
|
||||
case BackgroundActiontype.get_requests:
|
||||
return handleGetRequests(request, sendResponse);
|
||||
case BackgroundActiontype.clear_requests:
|
||||
clearAllRequestLogs().then(() => pushToRedux(setRequests([])));
|
||||
clearCache();
|
||||
return sendResponse();
|
||||
case BackgroundActiontype.get_prove_requests:
|
||||
return handleGetProveRequests(request, sendResponse);
|
||||
@@ -356,7 +337,6 @@ export const initRPC = () => {
|
||||
pluginHash: request.data,
|
||||
}).then(sendResponse);
|
||||
return;
|
||||
|
||||
case BackgroundActiontype.get_p2p_state:
|
||||
getP2PState();
|
||||
return;
|
||||
@@ -371,7 +351,10 @@ function handleGetRequests(
|
||||
request: BackgroundAction,
|
||||
sendResponse: (data?: any) => void,
|
||||
): boolean {
|
||||
getRequestLogsByTabId(request.data).then(sendResponse);
|
||||
const cache = getCacheByTabId(request.data);
|
||||
const keys = cache.keys() || [];
|
||||
const data = keys.map((key) => cache.get(key));
|
||||
sendResponse(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1049,17 +1032,53 @@ async function handleRunPluginByURLRequest(request: BackgroundAction) {
|
||||
});
|
||||
|
||||
const defer = deferredPromise();
|
||||
const { origin, position, url, params } = request.data;
|
||||
const {
|
||||
origin,
|
||||
position,
|
||||
url,
|
||||
params,
|
||||
skipConfirmation,
|
||||
verifierApiUrl,
|
||||
proxyApiUrl,
|
||||
headers,
|
||||
} = request.data;
|
||||
|
||||
if (skipConfirmation) {
|
||||
try {
|
||||
browser.runtime.onMessage.addListener(async (req: any) => {
|
||||
if (req.type !== SidePanelActionTypes.execute_plugin_response) return;
|
||||
if (req.data.url !== url) return;
|
||||
|
||||
if (req.data.error) defer.reject(req.data.error);
|
||||
if (req.data.proof) defer.resolve(req.data.proof);
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const id = charwise.encode(now).toString('hex');
|
||||
|
||||
await browser.runtime.sendMessage({
|
||||
type: OffscreenActionTypes.create_prover_request,
|
||||
data: {
|
||||
id,
|
||||
url,
|
||||
params,
|
||||
headers,
|
||||
verifierApiUrl,
|
||||
proxyApiUrl,
|
||||
maxRecvData: await getMaxRecv(),
|
||||
maxSentData: await getMaxSent(),
|
||||
},
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
} catch (error) {
|
||||
defer.reject(error);
|
||||
return defer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
// const plugin = await getPluginByHash(hash);
|
||||
// const config = await getPluginConfigByHash(hash);
|
||||
let isUserClose = true;
|
||||
|
||||
// if (!plugin || !config) {
|
||||
// defer.reject(new Error('plugin not found.'));
|
||||
// return defer.promise;
|
||||
// }
|
||||
|
||||
const { popup, tab } = await openPopup(
|
||||
`run-plugin-approval?url=${url}&origin=${encodeURIComponent(origin)}&favIconUrl=${encodeURIComponent(currentTab?.favIconUrl || '')}¶ms=${encodeURIComponent(JSON.stringify(params) || '')}`,
|
||||
position.left,
|
||||
|
||||
@@ -69,7 +69,6 @@ export const connectSession = async () => {
|
||||
if (state.socket) return;
|
||||
|
||||
const rendezvousAPI = await getRendezvousApi();
|
||||
|
||||
const socket = new WebSocket(rendezvousAPI);
|
||||
|
||||
socket.onopen = () => {
|
||||
@@ -317,26 +316,9 @@ export const connectSession = async () => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
socket.onerror = (error) => {
|
||||
console.error('Error connecting to websocket:', error);
|
||||
socket.onerror = () => {
|
||||
console.error('Error connecting to websocket');
|
||||
pushToRedux(setConnected(false));
|
||||
pushToRedux(
|
||||
setP2PError(
|
||||
'Failed to connect to rendezvous server. Please check your connection and server URL.',
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
socket.onclose = (event) => {
|
||||
console.log('WebSocket connection closed:', event.code, event.reason);
|
||||
pushToRedux(setConnected(false));
|
||||
if (event.code !== 1000 && event.code !== 1001) {
|
||||
pushToRedux(
|
||||
setP2PError(
|
||||
`WebSocket connection lost: ${event.reason || 'Unknown error'}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -454,8 +436,8 @@ export const endProofRequest = async (data: {
|
||||
proof: VerifierOutput;
|
||||
}) => {
|
||||
const transcript = new Transcript({
|
||||
sent: data.proof.transcript?.sent || [],
|
||||
recv: data.proof.transcript?.recv || [],
|
||||
sent: data.proof.transcript.sent,
|
||||
recv: data.proof.transcript.recv,
|
||||
});
|
||||
|
||||
state.presentation = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ContentScriptTypes, RPCClient } from './rpc';
|
||||
import { PresentationJSON } from 'tlsn-js/build/types';
|
||||
import { PresentationJSON } from '../../utils/types';
|
||||
|
||||
const client = new RPCClient();
|
||||
|
||||
@@ -44,6 +44,27 @@ class TLSN {
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
async runPluginWithVerifier(
|
||||
url: string,
|
||||
verifierOptions: {
|
||||
verifierApiUrl: string;
|
||||
proxyApiUrl: string;
|
||||
headers?: { [key: string]: string };
|
||||
},
|
||||
params?: Record<string, string>,
|
||||
) {
|
||||
const resp = await client.call(ContentScriptTypes.run_plugin_by_url, {
|
||||
url,
|
||||
params,
|
||||
skipConfirmation: true, // Signal to skip confirmation window
|
||||
verifierApiUrl: verifierOptions.verifierApiUrl,
|
||||
proxyApiUrl: verifierOptions.proxyApiUrl,
|
||||
headers: verifierOptions.headers,
|
||||
});
|
||||
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
|
||||
const connect = async () => {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { deferredPromise, PromiseResolvers } from '../../utils/promise';
|
||||
|
||||
export interface RunPluginByUrlData {
|
||||
url: string;
|
||||
params?: Record<string, string>;
|
||||
skipConfirmation?: boolean;
|
||||
verifierApiUrl?: string;
|
||||
proxyApiUrl?: string;
|
||||
headers?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export enum ContentScriptTypes {
|
||||
notarize = 'tlsn/cs/notarize',
|
||||
run_plugin_by_url = 'tlsn/cs/run_plugin_by_url',
|
||||
|
||||
@@ -16,13 +16,15 @@ import {
|
||||
} from 'tlsn-js';
|
||||
import { convertNotaryWsToHttp, devlog, urlify } from '../../utils/misc';
|
||||
import * as Comlink from 'comlink';
|
||||
import { PresentationJSON as PresentationJSONa7 } from 'tlsn-js/build/types';
|
||||
import { OffscreenActionTypes } from './types';
|
||||
import { PresentationJSON } from 'tlsn-js/build/types';
|
||||
import { PresentationJSON } from '../../utils/types';
|
||||
import { waitForEvent } from '../utils';
|
||||
import {
|
||||
setNotaryRequestError,
|
||||
setNotaryRequestStatus,
|
||||
} from '../Background/db';
|
||||
import { getNotaryApi, getProxyApi } from '../../utils/storage';
|
||||
|
||||
const { init, Prover, Presentation, Verifier }: any = Comlink.wrap(
|
||||
new Worker(new URL('./worker.ts', import.meta.url)),
|
||||
@@ -124,7 +126,7 @@ export const onCreatePresentationRequest = async (request: any) => {
|
||||
secretsHex: notarizationOutputs.secrets,
|
||||
notaryUrl: notarizationOutputs.notaryUrl,
|
||||
websocketProxyUrl: notarizationOutputs.websocketProxyUrl,
|
||||
reveal: { ...commit, server_identity: false },
|
||||
reveal: commit,
|
||||
})) as TPresentation;
|
||||
const json = await presentation.json();
|
||||
browser.runtime.sendMessage({
|
||||
@@ -133,6 +135,11 @@ export const onCreatePresentationRequest = async (request: any) => {
|
||||
id,
|
||||
proof: {
|
||||
...json,
|
||||
meta: {
|
||||
...json.meta,
|
||||
notaryUrl,
|
||||
websocketProxyUrl,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -296,7 +303,6 @@ export const startP2PProver = async (request: any) => {
|
||||
},
|
||||
});
|
||||
await proofRequestStart;
|
||||
|
||||
await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
@@ -345,7 +351,7 @@ export const startP2PProver = async (request: any) => {
|
||||
};
|
||||
|
||||
const endRequest = waitForEvent(OffscreenActionTypes.end_p2p_proof_request);
|
||||
await prover.reveal({ ...commit, server_identity: false });
|
||||
await prover.reveal(commit);
|
||||
await endRequest;
|
||||
};
|
||||
|
||||
@@ -363,7 +369,7 @@ async function createProof(options: {
|
||||
id: string;
|
||||
secretHeaders: string[];
|
||||
secretResps: string[];
|
||||
}): Promise<PresentationJSON> {
|
||||
}): Promise<PresentationJSONa7> {
|
||||
const {
|
||||
url,
|
||||
method = 'GET',
|
||||
@@ -395,18 +401,13 @@ async function createProof(options: {
|
||||
updateRequestProgress(id, RequestProgress.SettingUpProver);
|
||||
await prover.setup(sessionUrl);
|
||||
|
||||
await handleProgress(
|
||||
id,
|
||||
RequestProgress.SendingRequest,
|
||||
() =>
|
||||
prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
}),
|
||||
`Error connecting to websocket proxy: ${websocketProxyUrl}. Please check the proxy URL and ensure it's accessible.`,
|
||||
);
|
||||
updateRequestProgress(id, RequestProgress.SendingRequest);
|
||||
await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
|
||||
updateRequestProgress(id, RequestProgress.ReadingTranscript);
|
||||
const transcript = await prover.transcript();
|
||||
@@ -436,19 +437,25 @@ async function createProof(options: {
|
||||
secretsHex: notarizationOutputs.secrets,
|
||||
notaryUrl: notarizationOutputs.notaryUrl,
|
||||
websocketProxyUrl: notarizationOutputs.websocketProxyUrl,
|
||||
reveal: { ...commit, server_identity: false },
|
||||
reveal: commit,
|
||||
})) as TPresentation;
|
||||
|
||||
const json = await presentation.json();
|
||||
return {
|
||||
...json,
|
||||
meta: {
|
||||
...json,
|
||||
notaryUrl: notaryUrl,
|
||||
websocketProxyUrl: websocketProxyUrl,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function createProver(options: {
|
||||
url: string;
|
||||
notaryUrl: string;
|
||||
websocketProxyUrl: string;
|
||||
verifierApiUrl?: string;
|
||||
proxyApiUrl?: string;
|
||||
notaryUrl?: string;
|
||||
method?: Method;
|
||||
headers?: {
|
||||
[name: string]: string;
|
||||
@@ -465,13 +472,13 @@ async function createProver(options: {
|
||||
body,
|
||||
maxSentData,
|
||||
maxRecvData,
|
||||
notaryUrl,
|
||||
websocketProxyUrl,
|
||||
verifierApiUrl,
|
||||
proxyApiUrl,
|
||||
id,
|
||||
} = options;
|
||||
|
||||
const hostname = urlify(url)?.hostname || '';
|
||||
const notary = NotaryServer.from(notaryUrl);
|
||||
|
||||
try {
|
||||
const prover: TProver = await handleProgress(
|
||||
id,
|
||||
@@ -489,8 +496,17 @@ async function createProver(options: {
|
||||
const sessionUrl = await handleProgress(
|
||||
id,
|
||||
RequestProgress.GettingSession,
|
||||
() => notary.sessionUrl(maxSentData, maxRecvData),
|
||||
'Error getting session from Notary',
|
||||
async () => {
|
||||
if (verifierApiUrl) {
|
||||
return verifierApiUrl;
|
||||
} else {
|
||||
const notary = NotaryServer.from(
|
||||
options.notaryUrl || (await getNotaryApi()),
|
||||
);
|
||||
return notary.sessionUrl(maxSentData, maxRecvData);
|
||||
}
|
||||
},
|
||||
'Error getting session from Verifier/Notary',
|
||||
);
|
||||
|
||||
await handleProgress(
|
||||
@@ -500,17 +516,19 @@ async function createProver(options: {
|
||||
'Error setting up prover',
|
||||
);
|
||||
|
||||
const proxyUrl = proxyApiUrl || (await getProxyApi());
|
||||
|
||||
await handleProgress(
|
||||
id,
|
||||
RequestProgress.SendingRequest,
|
||||
() =>
|
||||
prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
prover.sendRequest(proxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
}),
|
||||
`Error connecting to websocket proxy: ${websocketProxyUrl}. Please check the proxy URL and ensure it's accessible.`,
|
||||
'Error sending request',
|
||||
);
|
||||
|
||||
return prover;
|
||||
@@ -533,29 +551,32 @@ async function verifyProof(proof: PresentationJSON): Promise<{
|
||||
};
|
||||
|
||||
switch (proof.version) {
|
||||
case '0.1.0-alpha.12':
|
||||
result = await verify(proof);
|
||||
break;
|
||||
default:
|
||||
case undefined:
|
||||
case '0.1.0-alpha.7':
|
||||
case '0.1.0-alpha.8':
|
||||
case '0.1.0-alpha.9':
|
||||
result = {
|
||||
sent: 'version not supported',
|
||||
recv: 'version not supported',
|
||||
};
|
||||
break;
|
||||
case '0.1.0-alpha.10':
|
||||
result = await verify(proof);
|
||||
break;
|
||||
}
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
async function verify(proof: PresentationJSON) {
|
||||
if (proof.version !== '0.1.0-alpha.12') {
|
||||
if (proof.version !== '0.1.0-alpha.10') {
|
||||
throw new Error('wrong version');
|
||||
}
|
||||
const presentation: TPresentation = await new Presentation(proof.data);
|
||||
const verifierOutput = await presentation.verify();
|
||||
const transcript = new Transcript({
|
||||
sent: verifierOutput.transcript?.sent || [],
|
||||
recv: verifierOutput.transcript?.recv || [],
|
||||
sent: verifierOutput.transcript.sent,
|
||||
recv: verifierOutput.transcript.recv,
|
||||
});
|
||||
const vk = await presentation.verifyingKey();
|
||||
const verifyingKey = Buffer.from(vk.data).toString('hex');
|
||||
@@ -594,75 +615,6 @@ function updateRequestProgress(
|
||||
});
|
||||
}
|
||||
|
||||
function getWebsocketErrorMessage(
|
||||
lowerError: string,
|
||||
fallbackMessage: string,
|
||||
): string {
|
||||
const isWebsocketError =
|
||||
lowerError.includes('websocket') ||
|
||||
lowerError.includes('proxy') ||
|
||||
lowerError.includes('connection') ||
|
||||
lowerError.includes('network') ||
|
||||
lowerError.includes('prover error') ||
|
||||
lowerError.includes('io error') ||
|
||||
lowerError.includes('certificate') ||
|
||||
lowerError.includes('cert') ||
|
||||
lowerError.includes('ssl') ||
|
||||
lowerError.includes('tls');
|
||||
|
||||
if (!isWebsocketError) {
|
||||
return fallbackMessage;
|
||||
}
|
||||
|
||||
const errorPatterns = [
|
||||
{
|
||||
patterns: ['protocol', 'must use ws://', 'must use wss://'],
|
||||
message:
|
||||
'Invalid websocket proxy URL protocol. Please use ws:// or wss:// protocol in your websocket proxy URL settings.',
|
||||
},
|
||||
{
|
||||
patterns: [
|
||||
'not allowed',
|
||||
'not whitelisted',
|
||||
'forbidden',
|
||||
'unauthorized',
|
||||
'permission denied',
|
||||
'access denied',
|
||||
],
|
||||
message:
|
||||
'Target domain not allowed by websocket proxy. Please check if the website domain is supported by your proxy service.',
|
||||
},
|
||||
{
|
||||
patterns: ['dns', 'resolve'],
|
||||
message:
|
||||
'Cannot resolve websocket proxy domain. Please check your websocket proxy URL in settings.',
|
||||
},
|
||||
{
|
||||
patterns: ['timeout'],
|
||||
message:
|
||||
'Websocket proxy connection timeout. Please check your websocket proxy URL in settings and ensure the server is accessible.',
|
||||
},
|
||||
{
|
||||
patterns: ['refused', 'unreachable'],
|
||||
message:
|
||||
'Cannot reach websocket proxy server. Please check your websocket proxy URL in settings and ensure the server is accessible.',
|
||||
},
|
||||
{
|
||||
patterns: ['cert', 'certificate', 'certnotvalidforname'],
|
||||
message:
|
||||
'Cannot connect to websocket proxy server. Please check your websocket proxy URL in settings and ensure it points to a valid websocket proxy service.',
|
||||
},
|
||||
];
|
||||
|
||||
for (const { patterns, message } of errorPatterns) {
|
||||
if (patterns.some((pattern) => lowerError.includes(pattern))) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
return 'Websocket proxy connection failed. Please check your websocket proxy URL in settings and ensure the server is accessible.';
|
||||
}
|
||||
|
||||
async function handleProgress<T>(
|
||||
id: string,
|
||||
progress: RequestProgress,
|
||||
@@ -673,17 +625,12 @@ async function handleProgress<T>(
|
||||
updateRequestProgress(id, progress);
|
||||
return await action();
|
||||
} catch (error: any) {
|
||||
const specificError = error?.message || '';
|
||||
const lowerError = specificError.toLowerCase();
|
||||
|
||||
const finalErrorMessage = getWebsocketErrorMessage(
|
||||
lowerError,
|
||||
errorMessage,
|
||||
);
|
||||
|
||||
updateRequestProgress(id, RequestProgress.Error, finalErrorMessage);
|
||||
updateRequestProgress(id, RequestProgress.Error, errorMessage);
|
||||
await setNotaryRequestStatus(id, 'error');
|
||||
await setNotaryRequestError(id, finalErrorMessage);
|
||||
await setNotaryRequestError(
|
||||
id,
|
||||
errorMessage || error.message || 'Unknown error',
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,8 @@ export default function SidePanel(): ReactElement {
|
||||
|
||||
switch (type) {
|
||||
case SidePanelActionTypes.execute_plugin_request: {
|
||||
const pluginIdentifier = data.pluginUrl || data.pluginHash;
|
||||
setConfig(await getPluginConfigByUrl(pluginIdentifier));
|
||||
setUrl(pluginIdentifier);
|
||||
setConfig(await getPluginConfigByUrl(data.pluginUrl));
|
||||
setUrl(data.pluginUrl);
|
||||
setParams(data.pluginParams);
|
||||
setStarted(true);
|
||||
break;
|
||||
@@ -93,7 +92,6 @@ export default function SidePanel(): ReactElement {
|
||||
</button>
|
||||
</div>
|
||||
{/*{!config && <PluginList />}*/}
|
||||
|
||||
{started && config && (
|
||||
<PluginBody
|
||||
url={url}
|
||||
@@ -154,10 +152,7 @@ function PluginBody({
|
||||
type: SidePanelActionTypes.execute_plugin_response,
|
||||
data: {
|
||||
url,
|
||||
error:
|
||||
notaryRequest.errorMessage ||
|
||||
notaryRequest.error ||
|
||||
'Notarization failed',
|
||||
error: notaryRequest.error,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -253,7 +248,7 @@ function StepContent(
|
||||
? JSON.stringify(lastResponse)
|
||||
: JSON.stringify(parameterValues),
|
||||
);
|
||||
const val = JSON.parse(out!.string());
|
||||
const val = JSON.parse(out.string());
|
||||
if (val && prover) {
|
||||
setNotarizationId(val);
|
||||
} else {
|
||||
@@ -262,7 +257,7 @@ function StepContent(
|
||||
setResponse(val, index);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
setError(e?.message || 'Unknown error');
|
||||
setError(e?.message || 'Unkonwn error');
|
||||
} finally {
|
||||
setPending(false);
|
||||
}
|
||||
@@ -354,18 +349,12 @@ function StepContent(
|
||||
btnContent = (
|
||||
<div className="flex flex-col gap-2">
|
||||
{notaryRequest?.progress === RequestProgress.Error && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex flex-row items-start gap-2 text-red-600">
|
||||
<Icon
|
||||
fa="fa-solid fa-triangle-exclamation"
|
||||
size={1}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<span className="text-sm">
|
||||
{notaryRequest?.errorMessage ||
|
||||
progressText(notaryRequest.progress)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-row items-center gap-2 text-red-600">
|
||||
<Icon fa="fa-solid fa-triangle-exclamation" size={1} />
|
||||
<span className="text-sm">
|
||||
{notaryRequest?.errorMessage ||
|
||||
progressText(notaryRequest.progress)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{notaryRequest?.progress !== RequestProgress.Error && (
|
||||
@@ -408,18 +397,7 @@ function StepContent(
|
||||
{!!description && (
|
||||
<div className="text-slate-500 text-sm">{description}</div>
|
||||
)}
|
||||
{!!error && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex flex-row items-start gap-2 text-red-600">
|
||||
<Icon
|
||||
fa="fa-solid fa-triangle-exclamation"
|
||||
size={1}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<div className="text-red-500 text-sm">{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!error && <div className="text-red-500 text-sm">{error}</div>}
|
||||
{btnContent}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,23 +11,17 @@ import History from '../History';
|
||||
import './index.scss';
|
||||
import Requests from '../Requests';
|
||||
import { fetchPluginHashes } from '../../utils/rpc';
|
||||
import { PluginList } from '../../components/PluginList';
|
||||
import { getDeveloperMode } from '../../utils/storage';
|
||||
|
||||
export default function Home(props: {
|
||||
tab?: 'history' | 'network';
|
||||
}): ReactElement {
|
||||
const [error, showError] = useState('');
|
||||
const [tab, setTab] = useState<'history' | 'network' | 'plugins'>(
|
||||
props.tab || 'history',
|
||||
);
|
||||
const [tab, setTab] = useState<'history' | 'network'>(props.tab || 'history');
|
||||
const scrollableContent = useRef<HTMLDivElement | null>(null);
|
||||
const [shouldFix, setFix] = useState(false);
|
||||
const [developerMode, setDeveloperMode] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPluginHashes();
|
||||
getDeveloperMode().then(setDeveloperMode);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -77,24 +71,10 @@ export default function Home(props: {
|
||||
>
|
||||
History
|
||||
</TabSelector>
|
||||
{developerMode && (
|
||||
<TabSelector
|
||||
onClick={() => setTab('plugins')}
|
||||
selected={tab === 'plugins'}
|
||||
>
|
||||
Plugins
|
||||
</TabSelector>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
{tab === 'history' && <History />}
|
||||
{tab === 'network' && <Requests shouldFix={shouldFix} />}
|
||||
{tab === 'plugins' && (
|
||||
<PluginList
|
||||
className="p-2 overflow-y-auto"
|
||||
showAddButton={developerMode}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -19,8 +19,6 @@ import {
|
||||
LOGGING_FILTER_KEY,
|
||||
getRendezvousApi,
|
||||
RENDEZVOUS_API_LS_KEY,
|
||||
getDeveloperMode,
|
||||
DEVELOPER_MODE_LS_KEY,
|
||||
} from '../../utils/storage';
|
||||
import {
|
||||
EXPLORER_API,
|
||||
@@ -34,7 +32,6 @@ import Modal, { ModalContent } from '../../components/Modal/Modal';
|
||||
import browser from 'webextension-polyfill';
|
||||
import { LoggingLevel } from 'tlsn-js';
|
||||
import { version } from '../../../package.json';
|
||||
import { getDBSize, resetDB } from '../../entries/Background/db';
|
||||
|
||||
export default function Options(): ReactElement {
|
||||
const [notary, setNotary] = useState(NOTARY_API);
|
||||
@@ -43,28 +40,16 @@ export default function Options(): ReactElement {
|
||||
const [maxReceived, setMaxReceived] = useState(MAX_RECV);
|
||||
const [loggingLevel, setLoggingLevel] = useState<LoggingLevel>('Info');
|
||||
const [rendezvous, setRendezvous] = useState(RENDEZVOUS_API);
|
||||
const [developerMode, setDeveloperMode] = useState(false);
|
||||
|
||||
const [dirty, setDirty] = useState(false);
|
||||
const [shouldReload, setShouldReload] = useState(false);
|
||||
const [advanced, setAdvanced] = useState(false);
|
||||
const [showReloadModal, setShowReloadModal] = useState(false);
|
||||
const [dbSize, setDbSize] = useState(0);
|
||||
const [isCalculatingDbSize, setIsCalculatingDbSize] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setIsCalculatingDbSize(true);
|
||||
setDbSize(await getDBSize());
|
||||
setIsCalculatingDbSize(false);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setNotary(await getNotaryApi());
|
||||
setProxy(await getProxyApi());
|
||||
setDeveloperMode(await getDeveloperMode());
|
||||
})();
|
||||
}, []);
|
||||
|
||||
@@ -89,7 +74,6 @@ export default function Options(): ReactElement {
|
||||
await set(MAX_RECEIVED_LS_KEY, maxReceived.toString());
|
||||
await set(LOGGING_FILTER_KEY, loggingLevel);
|
||||
await set(RENDEZVOUS_API_LS_KEY, rendezvous);
|
||||
await set(DEVELOPER_MODE_LS_KEY, developerMode.toString());
|
||||
setDirty(false);
|
||||
},
|
||||
[
|
||||
@@ -99,7 +83,6 @@ export default function Options(): ReactElement {
|
||||
maxReceived,
|
||||
loggingLevel,
|
||||
rendezvous,
|
||||
developerMode,
|
||||
shouldReload,
|
||||
],
|
||||
);
|
||||
@@ -120,13 +103,6 @@ export default function Options(): ReactElement {
|
||||
browser.tabs.create({ url });
|
||||
}, []);
|
||||
|
||||
const onCleanCache = useCallback(async () => {
|
||||
setIsCalculatingDbSize(true);
|
||||
await resetDB();
|
||||
setDbSize(await getDBSize());
|
||||
setIsCalculatingDbSize(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-nowrap flex-grow overflow-y-auto">
|
||||
{showReloadModal && (
|
||||
@@ -163,8 +139,6 @@ export default function Options(): ReactElement {
|
||||
proxy={proxy}
|
||||
setProxy={setProxy}
|
||||
setDirty={setDirty}
|
||||
developerMode={developerMode}
|
||||
setDeveloperMode={setDeveloperMode}
|
||||
/>
|
||||
<div className="justify-left px-2 pt-3 gap-2">
|
||||
<button className="font-bold" onClick={onAdvanced}>
|
||||
@@ -218,15 +192,6 @@ export default function Options(): ReactElement {
|
||||
>
|
||||
Join our Discord
|
||||
</button>
|
||||
<button className="button" onClick={onCleanCache}>
|
||||
<span>Clean Cache (</span>
|
||||
{isCalculatingDbSize ? (
|
||||
<i className="fa-solid fa-spinner fa-spin"></i>
|
||||
) : (
|
||||
<span>{(dbSize / 1024 / 1024).toFixed(2)} MB</span>
|
||||
)}
|
||||
<span>)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -263,18 +228,8 @@ function NormalOptions(props: {
|
||||
proxy: string;
|
||||
setProxy: (value: string) => void;
|
||||
setDirty: (value: boolean) => void;
|
||||
developerMode: boolean;
|
||||
setDeveloperMode: (value: boolean) => void;
|
||||
}) {
|
||||
const {
|
||||
notary,
|
||||
setNotary,
|
||||
proxy,
|
||||
setProxy,
|
||||
setDirty,
|
||||
developerMode,
|
||||
setDeveloperMode,
|
||||
} = props;
|
||||
const { notary, setNotary, proxy, setProxy, setDirty } = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -306,33 +261,6 @@ function NormalOptions(props: {
|
||||
<div className="font-semibold">Explorer URL</div>
|
||||
<div className="input border bg-slate-100">{EXPLORER_API}</div>
|
||||
</div>
|
||||
<div className="flex flex-row items-center py-3 px-2 gap-2">
|
||||
<div className="font-semibold">Developer Mode</div>
|
||||
<div className="relative inline-block w-9 h-5">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="developer-mode"
|
||||
checked={developerMode}
|
||||
onChange={(e) => {
|
||||
setDeveloperMode(e.target.checked);
|
||||
setDirty(true);
|
||||
}}
|
||||
className="sr-only"
|
||||
/>
|
||||
<label
|
||||
htmlFor="developer-mode"
|
||||
className={`block h-5 rounded-full cursor-pointer transition-all duration-300 ease-in-out ${
|
||||
developerMode ? 'bg-blue-500' : 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow-sm transform transition-all duration-300 ease-in-out ${
|
||||
developerMode ? 'translate-x-4' : 'translate-x-0'
|
||||
}`}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const EXPLORER_API = 'https://explorer.tlsnotary.org';
|
||||
export const NOTARY_API = 'https://notary.pse.dev/v0.1.0-alpha.12';
|
||||
export const NOTARY_API = 'https://notary.pse.dev/v0.1.0-alpha.10';
|
||||
export const RENDEZVOUS_API = 'wss://explorer.tlsnotary.org';
|
||||
export const NOTARY_PROXY = 'wss://notary.pse.dev/proxy';
|
||||
export const MAX_RECV = 16384;
|
||||
|
||||
@@ -8,7 +8,6 @@ export const MAX_SENT_LS_KEY = 'max-sent';
|
||||
export const MAX_RECEIVED_LS_KEY = 'max-received';
|
||||
export const LOGGING_FILTER_KEY = 'logging-filter-2';
|
||||
export const RENDEZVOUS_API_LS_KEY = 'rendezvous-api';
|
||||
export const DEVELOPER_MODE_LS_KEY = 'developer-mode';
|
||||
|
||||
export async function set(key: string, value: string) {
|
||||
return chrome.storage.sync.set({ [key]: value });
|
||||
@@ -44,8 +43,3 @@ export async function getLoggingFilter(): Promise<LoggingLevel> {
|
||||
export async function getRendezvousApi(): Promise<string> {
|
||||
return await get(RENDEZVOUS_API_LS_KEY, RENDEZVOUS_API);
|
||||
}
|
||||
|
||||
export async function getDeveloperMode(): Promise<boolean> {
|
||||
const value = await get(DEVELOPER_MODE_LS_KEY, 'false');
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
10
src/utils/types.ts
Normal file
10
src/utils/types.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { PresentationJSON as PresentationJSONa7 } from 'tlsn-js/build/types';
|
||||
|
||||
export type PresentationJSON = PresentationJSONa5 | PresentationJSONa7;
|
||||
|
||||
export type PresentationJSONa5 = {
|
||||
version?: undefined;
|
||||
session: any;
|
||||
substrings: any;
|
||||
notaryUrl: string;
|
||||
};
|
||||
@@ -22,28 +22,6 @@ config.plugins = (config.plugins || []).concat(
|
||||
}),
|
||||
);
|
||||
|
||||
webpack(config, function (err, stats) {
|
||||
if (err) {
|
||||
console.error('Webpack error:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
console.error('Build failed with errors:');
|
||||
const info = stats.toJson();
|
||||
console.error(info.errors.map((e) => e.message).join('\n\n'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (stats.hasWarnings()) {
|
||||
console.warn('Build completed with warnings:');
|
||||
const info = stats.toJson();
|
||||
console.warn(info.warnings.map((w) => w.message).join('\n\n'));
|
||||
}
|
||||
|
||||
console.log('Build completed successfully!');
|
||||
console.log(`Output: ${path.join(__dirname, '../', 'build')}`);
|
||||
console.log(
|
||||
`Zip: ${path.join(__dirname, '../', 'zip', `${packageInfo.name}-${packageInfo.version}.zip`)}`,
|
||||
);
|
||||
webpack(config, function (err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user