mirror of
https://github.com/tlsnotary/tlsn-extension.git
synced 2026-01-09 21:18:02 -05:00
Compare commits
10 Commits
0.1.0.1200
...
0.1.0.1202
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
753e0490f1 | ||
|
|
30249bb2d3 | ||
|
|
e497dcae27 | ||
|
|
1cac2f79e9 | ||
|
|
650553e793 | ||
|
|
de676eb498 | ||
|
|
d334286cbd | ||
|
|
538331c847 | ||
|
|
df1af592b6 | ||
|
|
7db5b12629 |
692
package-lock.json
generated
692
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.1202",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -94,4 +94,4 @@
|
||||
"webpack-ext-reloader": "^1.1.12",
|
||||
"zip-webpack-plugin": "^4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -72,16 +72,6 @@ export default function Menu(props: {
|
||||
>
|
||||
Verify
|
||||
</MenuRow>
|
||||
<MenuRow
|
||||
fa="fa-solid fa-network-wired"
|
||||
className="border-b border-slate-300"
|
||||
onClick={() => {
|
||||
props.setOpen(false);
|
||||
navigate('/p2p');
|
||||
}}
|
||||
>
|
||||
P2P
|
||||
</MenuRow>
|
||||
<MenuRow
|
||||
className="lg:hidden"
|
||||
fa="fa-solid fa-up-right-and-down-left-from-center"
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
MAX_RECEIVED_LS_KEY,
|
||||
getMaxRecv,
|
||||
getMaxSent,
|
||||
getDeveloperMode,
|
||||
} from '../../utils/storage';
|
||||
import { MAX_RECV, MAX_SENT } from '../../utils/constants';
|
||||
|
||||
|
||||
@@ -42,10 +42,6 @@ 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);
|
||||
|
||||
@@ -77,10 +73,20 @@ export async function removeRequestLog(requestId: string) {
|
||||
if (existing) {
|
||||
await requestDb.del(requestId);
|
||||
await requestDb.sublevel(existing.tabId.toString()).del(requestId);
|
||||
|
||||
// Removing requestId for asset url
|
||||
const host = urlify(existing.url)?.host;
|
||||
if (host) {
|
||||
await requestDb.sublevel(host).del(requestId);
|
||||
}
|
||||
|
||||
// Removing requestId for initiator url
|
||||
if (existing.initiator) {
|
||||
const host = urlify(existing.initiator)?.host;
|
||||
if (host) {
|
||||
await requestDb.sublevel(host).del(requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +152,17 @@ export async function addNotaryRequestProofs(
|
||||
return newReq;
|
||||
}
|
||||
|
||||
export async function setNotaryRequestSessionId(
|
||||
id: string,
|
||||
sessionId: string,
|
||||
): Promise<RequestHistory | null> {
|
||||
const existing = await historyDb.get(id);
|
||||
if (!existing) return null;
|
||||
const newReq: RequestHistory = { ...existing, sessionId };
|
||||
await historyDb.put(id, newReq);
|
||||
return newReq;
|
||||
}
|
||||
|
||||
export async function setNotaryRequestStatus(
|
||||
id: string,
|
||||
status: '' | 'pending' | 'success' | 'error',
|
||||
@@ -441,8 +458,11 @@ export async function getCookiesByHost(linkOrHost: string) {
|
||||
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();
|
||||
const i = cookie.indexOf('=');
|
||||
if (i !== -1) {
|
||||
const name = cookie.slice(0, i).trim();
|
||||
ret[name] = cookie.slice(i + 1).trim();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -555,24 +575,6 @@ export async function getSessionStorageByHost(host: string) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
async function getDefaultPluginsInstalled(): Promise<string | boolean> {
|
||||
return appDb.get(AppDatabaseKey.DefaultPluginsInstalled).catch(() => false);
|
||||
}
|
||||
|
||||
export async function setDefaultPluginsInstalled(
|
||||
installed: string | boolean = false,
|
||||
) {
|
||||
return mutex.runExclusive(async () => {
|
||||
await appDb.put(AppDatabaseKey.DefaultPluginsInstalled, installed);
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAppState() {
|
||||
return {
|
||||
defaultPluginsInstalled: await getDefaultPluginsInstalled(),
|
||||
};
|
||||
}
|
||||
|
||||
export async function resetDB() {
|
||||
return mutex.runExclusive(async () => {
|
||||
return Promise.all([
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { onBeforeRequest, onResponseStarted, onSendHeaders } from './handlers';
|
||||
import browser from 'webextension-polyfill';
|
||||
import {
|
||||
getAppState,
|
||||
removePlugin,
|
||||
removeRequestLogsByTabId,
|
||||
setDefaultPluginsInstalled,
|
||||
} from './db';
|
||||
import { removePlugin, removeRequestLogsByTabId } from './db';
|
||||
import { installPlugin } from './plugins/utils';
|
||||
|
||||
(async () => {
|
||||
@@ -37,40 +32,6 @@ import { installPlugin } from './plugins/utils';
|
||||
removeRequestLogsByTabId(tabId);
|
||||
});
|
||||
|
||||
const { defaultPluginsInstalled } = await getAppState();
|
||||
|
||||
switch (defaultPluginsInstalled) {
|
||||
case false: {
|
||||
try {
|
||||
const twitterProfileUrl = browser.runtime.getURL(
|
||||
'twitter_profile.wasm',
|
||||
);
|
||||
const discordDmUrl = browser.runtime.getURL('discord_dm.wasm');
|
||||
await installPlugin(twitterProfileUrl);
|
||||
await installPlugin(discordDmUrl);
|
||||
} finally {
|
||||
await setDefaultPluginsInstalled('0.1.0.703');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case true: {
|
||||
try {
|
||||
await removePlugin(
|
||||
'6931d2ad63340d3a1fb1a5c1e3f4454c5a518164d6de5ad272e744832355ee02',
|
||||
);
|
||||
const twitterProfileUrl = browser.runtime.getURL(
|
||||
'twitter_profile.wasm',
|
||||
);
|
||||
await installPlugin(twitterProfileUrl);
|
||||
} finally {
|
||||
await setDefaultPluginsInstalled('0.1.0.703');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '0.1.0.703':
|
||||
break;
|
||||
}
|
||||
|
||||
const { initRPC } = await import('./rpc');
|
||||
await createOffscreenDocument();
|
||||
initRPC();
|
||||
|
||||
@@ -18,13 +18,12 @@ import {
|
||||
removePluginConfig,
|
||||
getCookiesByHost,
|
||||
getHeadersByHost,
|
||||
getAppState,
|
||||
setDefaultPluginsInstalled,
|
||||
setLocalStorage,
|
||||
setSessionStorage,
|
||||
setNotaryRequestProgress,
|
||||
getRequestLogsByTabId,
|
||||
clearAllRequestLogs,
|
||||
setNotaryRequestSessionId,
|
||||
} from './db';
|
||||
import { addOnePlugin, removeOnePlugin } from '../../reducers/plugins';
|
||||
import {
|
||||
@@ -222,6 +221,7 @@ export type RequestHistory = {
|
||||
metadata?: {
|
||||
[k: string]: string;
|
||||
};
|
||||
sessionId?: string;
|
||||
};
|
||||
|
||||
export const initRPC = () => {
|
||||
@@ -279,12 +279,6 @@ export const initRPC = () => {
|
||||
case BackgroundActiontype.get_logging_level:
|
||||
getLoggingFilter().then(sendResponse);
|
||||
return true;
|
||||
case BackgroundActiontype.get_app_state:
|
||||
getAppState().then(sendResponse);
|
||||
return true;
|
||||
case BackgroundActiontype.set_default_plugins_installed:
|
||||
setDefaultPluginsInstalled(request.data).then(sendResponse);
|
||||
return true;
|
||||
case BackgroundActiontype.set_local_storage:
|
||||
return handleSetLocalStorage(request, sender, sendResponse);
|
||||
case BackgroundActiontype.set_session_storage:
|
||||
@@ -397,8 +391,9 @@ async function handleFinishProveRequest(
|
||||
request: BackgroundAction,
|
||||
sendResponse: (data?: any) => void,
|
||||
) {
|
||||
const { id, proof, error, verification } = request.data;
|
||||
const { id, proof, error, verification, sessionId } = request.data;
|
||||
|
||||
console.log('handleFinishProveRequest', request.data);
|
||||
if (proof) {
|
||||
const newReq = await addNotaryRequestProofs(id, proof);
|
||||
if (!newReq) return;
|
||||
@@ -420,6 +415,12 @@ async function handleFinishProveRequest(
|
||||
await pushToRedux(addRequestHistory(await getNotaryRequest(id)));
|
||||
}
|
||||
|
||||
if (sessionId) {
|
||||
const newReq = await setNotaryRequestSessionId(id, sessionId);
|
||||
if (!newReq) return;
|
||||
await pushToRedux(addRequestHistory(await getNotaryRequest(id)));
|
||||
}
|
||||
|
||||
return sendResponse();
|
||||
}
|
||||
|
||||
@@ -528,6 +529,7 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) {
|
||||
websocketProxyUrl: _websocketProxyUrl,
|
||||
maxSentData: _maxSentData,
|
||||
maxRecvData: _maxRecvData,
|
||||
metadata,
|
||||
} = request.data;
|
||||
const notaryUrl = _notaryUrl || (await getNotaryApi());
|
||||
const websocketProxyUrl = _websocketProxyUrl || (await getProxyApi());
|
||||
@@ -547,6 +549,7 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) {
|
||||
maxSentData,
|
||||
secretHeaders,
|
||||
secretResps,
|
||||
metadata,
|
||||
});
|
||||
|
||||
await setNotaryRequestStatus(id, 'pending');
|
||||
@@ -713,24 +716,38 @@ async function runP2PPluginProver(request: BackgroundAction, now = Date.now()) {
|
||||
websocketProxyUrl: _websocketProxyUrl,
|
||||
maxSentData: _maxSentData,
|
||||
maxRecvData: _maxRecvData,
|
||||
clientId,
|
||||
verifierPlugin,
|
||||
notaryUrl,
|
||||
} = request.data;
|
||||
const rendezvousApi = await getRendezvousApi();
|
||||
const proverUrl = `${rendezvousApi}?clientId=${clientId}:proof`;
|
||||
const websocketProxyUrl = _websocketProxyUrl || (await getProxyApi());
|
||||
const maxSentData = _maxSentData || (await getMaxSent());
|
||||
const maxRecvData = _maxRecvData || (await getMaxRecv());
|
||||
|
||||
const { id } = await addNotaryRequest(now, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
notaryUrl,
|
||||
websocketProxyUrl,
|
||||
maxRecvData,
|
||||
maxSentData,
|
||||
secretHeaders,
|
||||
secretResps: [],
|
||||
});
|
||||
|
||||
await browser.runtime.sendMessage({
|
||||
type: OffscreenActionTypes.start_p2p_prover,
|
||||
data: {
|
||||
id,
|
||||
pluginUrl,
|
||||
pluginHex,
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
proverUrl,
|
||||
proverUrl: notaryUrl,
|
||||
verifierPlugin,
|
||||
websocketProxyUrl,
|
||||
maxRecvData,
|
||||
maxSentData,
|
||||
@@ -1068,14 +1085,22 @@ async function handleRunPluginByURLRequest(request: BackgroundAction) {
|
||||
|
||||
const onPluginRequest = async (req: any) => {
|
||||
if (req.type !== SidePanelActionTypes.execute_plugin_response) return;
|
||||
console.log('onPluginRequest', req.data);
|
||||
if (req.data.url !== url) return;
|
||||
|
||||
if (req.data.error) defer.reject(req.data.error);
|
||||
if (req.data.proof) defer.resolve(req.data.proof);
|
||||
if (req.data.sessionId) defer.resolve(req.data.sessionId);
|
||||
|
||||
browser.runtime.onMessage.removeListener(onPluginRequest);
|
||||
};
|
||||
|
||||
const onSidePanelClosing = async (req: any) => {
|
||||
if (req.type === SidePanelActionTypes.panel_closing) {
|
||||
browser.runtime.onMessage.removeListener(onSidePanelClosing);
|
||||
defer.reject(new Error('user rejected.'));
|
||||
}
|
||||
};
|
||||
|
||||
const onMessage = async (req: BackgroundAction) => {
|
||||
if (req.type === BackgroundActiontype.run_plugin_by_url_response) {
|
||||
if (req.data) {
|
||||
@@ -1098,6 +1123,7 @@ async function handleRunPluginByURLRequest(request: BackgroundAction) {
|
||||
};
|
||||
|
||||
browser.runtime.onMessage.addListener(onMessage);
|
||||
browser.runtime.onMessage.addListener(onSidePanelClosing);
|
||||
browser.windows.onRemoved.addListener(onPopUpClose);
|
||||
|
||||
return defer.promise;
|
||||
|
||||
@@ -245,6 +245,7 @@ export const startP2PVerifier = async (request: any) => {
|
||||
|
||||
export const startP2PProver = async (request: any) => {
|
||||
const {
|
||||
id,
|
||||
pluginUrl,
|
||||
pluginHex,
|
||||
url,
|
||||
@@ -257,53 +258,56 @@ export const startP2PProver = async (request: any) => {
|
||||
maxSentData,
|
||||
secretHeaders,
|
||||
getSecretResponse,
|
||||
verifierPlugin,
|
||||
} = request.data;
|
||||
|
||||
const hostname = urlify(url)?.hostname || '';
|
||||
|
||||
updateRequestProgress(id, RequestProgress.CreatingProver);
|
||||
const prover: TProver = await new Prover({
|
||||
id: pluginUrl,
|
||||
id,
|
||||
serverDns: hostname,
|
||||
maxSentData,
|
||||
maxRecvData,
|
||||
serverIdentity: true,
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.prover_instantiated,
|
||||
data: {
|
||||
pluginUrl,
|
||||
updateRequestProgress(id, RequestProgress.GettingSession);
|
||||
const resp = await fetch(`${proverUrl}/session`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
clientType: 'Websocket',
|
||||
maxRecvData,
|
||||
maxSentData,
|
||||
plugin: 'plugin-js',
|
||||
}),
|
||||
});
|
||||
const { sessionId } = await resp.json();
|
||||
const _url = new URL(proverUrl);
|
||||
const protocol = _url.protocol === 'https:' ? 'wss' : 'ws';
|
||||
const pathname = _url.pathname;
|
||||
const sessionUrl = `${protocol}://${_url.host}${pathname === '/' ? '' : pathname}/notarize?sessionId=${sessionId!}`;
|
||||
|
||||
const proofRequestStart = waitForEvent(
|
||||
OffscreenActionTypes.start_p2p_proof_request,
|
||||
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.`,
|
||||
);
|
||||
|
||||
const proverSetup = prover.setup(proverUrl);
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.prover_setup,
|
||||
data: {
|
||||
pluginUrl,
|
||||
},
|
||||
});
|
||||
|
||||
await proverSetup;
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.prover_started,
|
||||
data: {
|
||||
pluginUrl,
|
||||
},
|
||||
});
|
||||
await proofRequestStart;
|
||||
|
||||
await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
|
||||
updateRequestProgress(id, RequestProgress.ReadingTranscript);
|
||||
const transcript = await prover.transcript();
|
||||
|
||||
let secretResps: string[] = [];
|
||||
@@ -344,9 +348,15 @@ export const startP2PProver = async (request: any) => {
|
||||
),
|
||||
};
|
||||
|
||||
const endRequest = waitForEvent(OffscreenActionTypes.end_p2p_proof_request);
|
||||
await prover.reveal({ ...commit, server_identity: false });
|
||||
await endRequest;
|
||||
await prover.reveal({ ...commit, server_identity: true });
|
||||
updateRequestProgress(id, RequestProgress.FinalizingOutputs);
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.finish_prove_request,
|
||||
data: {
|
||||
id,
|
||||
sessionId: sessionId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
async function createProof(options: {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
makePlugin,
|
||||
PluginConfig,
|
||||
StepConfig,
|
||||
InputFieldConfig,
|
||||
} from '../../utils/misc';
|
||||
import DefaultPluginIcon from '../../assets/img/default-plugin-icon.png';
|
||||
import logo from '../../assets/img/icon-128.png';
|
||||
@@ -79,6 +80,13 @@ export default function SidePanel(): ReactElement {
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
browser.runtime.sendMessage({
|
||||
type: SidePanelActionTypes.panel_closing,
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -149,6 +157,14 @@ function PluginBody({
|
||||
proof: notaryRequest.proof,
|
||||
},
|
||||
});
|
||||
} else if (notaryRequest?.sessionId) {
|
||||
browser.runtime.sendMessage({
|
||||
type: SidePanelActionTypes.execute_plugin_response,
|
||||
data: {
|
||||
url,
|
||||
sessionId: notaryRequest.sessionId,
|
||||
},
|
||||
});
|
||||
} else if (notaryRequest?.status === 'error') {
|
||||
browser.runtime.sendMessage({
|
||||
type: SidePanelActionTypes.execute_plugin_response,
|
||||
@@ -161,7 +177,7 @@ function PluginBody({
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [url, notaryRequest?.status]);
|
||||
}, [url, notaryRequest?.status, notaryRequest?.sessionId]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col p-4">
|
||||
@@ -225,13 +241,27 @@ function StepContent(
|
||||
p2p = false,
|
||||
clientId = '',
|
||||
parameterValues,
|
||||
inputs,
|
||||
} = props;
|
||||
const [completed, setCompleted] = useState(false);
|
||||
const [pending, setPending] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [notarizationId, setNotarizationId] = useState('');
|
||||
const [inputValues, setInputValues] = useState<Record<string, string>>({});
|
||||
const notaryRequest = useRequestHistory(notarizationId);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputs) {
|
||||
const initialValues: Record<string, string> = {};
|
||||
inputs.forEach((input) => {
|
||||
if (input.defaultValue) {
|
||||
initialValues[input.name] = input.defaultValue;
|
||||
}
|
||||
});
|
||||
setInputValues(initialValues);
|
||||
}
|
||||
}, [inputs]);
|
||||
|
||||
const getPlugin = useCallback(async () => {
|
||||
const hex = (await getPluginByUrl(url)) || _hex;
|
||||
const arrayBuffer = hexToArrayBuffer(hex!);
|
||||
@@ -243,16 +273,31 @@ function StepContent(
|
||||
if (!plugin) return;
|
||||
if (index > 0 && !lastResponse) return;
|
||||
|
||||
// Validate required input fields
|
||||
if (inputs) {
|
||||
for (const input of inputs) {
|
||||
if (
|
||||
input.required &&
|
||||
(!inputValues[input.name] || inputValues[input.name].trim() === '')
|
||||
) {
|
||||
setError(`${input.label} is required`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setPending(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const out = await plugin.call(
|
||||
action,
|
||||
index > 0
|
||||
? JSON.stringify(lastResponse)
|
||||
: JSON.stringify(parameterValues),
|
||||
);
|
||||
let stepData: any;
|
||||
if (index > 0) {
|
||||
stepData = lastResponse;
|
||||
} else {
|
||||
stepData = { ...parameterValues, ...inputValues };
|
||||
}
|
||||
|
||||
const out = await plugin.call(action, JSON.stringify(stepData));
|
||||
const val = JSON.parse(out!.string());
|
||||
if (val && prover) {
|
||||
setNotarizationId(val);
|
||||
@@ -266,7 +311,16 @@ function StepContent(
|
||||
} finally {
|
||||
setPending(false);
|
||||
}
|
||||
}, [action, index, lastResponse, prover, getPlugin]);
|
||||
}, [
|
||||
action,
|
||||
index,
|
||||
lastResponse,
|
||||
prover,
|
||||
getPlugin,
|
||||
inputs,
|
||||
inputValues,
|
||||
parameterValues,
|
||||
]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (
|
||||
@@ -311,11 +365,16 @@ function StepContent(
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
processStep();
|
||||
}, [processStep]);
|
||||
// only auto-progress if this step does need inputs
|
||||
if (!inputs || inputs.length === 0) {
|
||||
processStep();
|
||||
}
|
||||
}, [processStep, inputs]);
|
||||
|
||||
let btnContent = null;
|
||||
|
||||
console.log('notaryRequest', notaryRequest);
|
||||
console.log('notarizationId', notarizationId);
|
||||
if (prover && p2p) {
|
||||
btnContent = (
|
||||
<button
|
||||
@@ -327,7 +386,7 @@ function StepContent(
|
||||
<span className="text-sm">View in P2P</span>
|
||||
</button>
|
||||
);
|
||||
} else if (completed) {
|
||||
} else if (completed || notaryRequest?.sessionId) {
|
||||
btnContent = (
|
||||
<button
|
||||
className={classNames(
|
||||
@@ -420,8 +479,105 @@ function StepContent(
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{inputs && inputs.length > 0 && !completed && (
|
||||
<div className="flex flex-col gap-3 mt-3">
|
||||
{inputs.map((input) => (
|
||||
<InputField
|
||||
key={input.name}
|
||||
config={input}
|
||||
value={inputValues[input.name] || ''}
|
||||
onChange={(value) =>
|
||||
setInputValues((prev) => ({ ...prev, [input.name]: value }))
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{btnContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface InputFieldProps {
|
||||
config: InputFieldConfig;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function InputField({
|
||||
config,
|
||||
value,
|
||||
onChange,
|
||||
disabled = false,
|
||||
}: InputFieldProps): ReactElement {
|
||||
const { name, label, type, placeholder, required, options } = config;
|
||||
|
||||
const baseClasses =
|
||||
'w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent';
|
||||
|
||||
const renderInput = () => {
|
||||
switch (type) {
|
||||
case 'textarea':
|
||||
return (
|
||||
<textarea
|
||||
id={name}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
className={classNames(baseClasses, 'resize-y min-h-[80px]')}
|
||||
rows={3}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'select':
|
||||
return (
|
||||
<select
|
||||
id={name}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
className={baseClasses}
|
||||
>
|
||||
<option value="">{placeholder || 'Select an option'}</option>
|
||||
{options?.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
id={name}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
className={baseClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor={name} className="text-sm font-medium text-gray-700">
|
||||
{label}
|
||||
{required && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
{renderInput()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export enum SidePanelActionTypes {
|
||||
panel_opened = 'sidePanel/panel_opened',
|
||||
panel_closing = 'sidePanel/panel_closing',
|
||||
execute_plugin_request = 'sidePanel/execute_plugin_request',
|
||||
execute_plugin_response = 'sidePanel/execute_plugin_response',
|
||||
run_p2p_plugin_request = 'sidePanel/run_p2p_plugin_request',
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "content.bundle.js", "discord_dm.wasm", "twitter_profile.wasm"],
|
||||
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "content.bundle.js"],
|
||||
"matches": ["http://*/*", "https://*/*", "<all_urls>"]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -30,6 +30,12 @@ export default function Home(props: {
|
||||
getDeveloperMode().then(setDeveloperMode);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.tab === 'network' && !developerMode) {
|
||||
setTab('history');
|
||||
}
|
||||
}, [props.tab, developerMode]);
|
||||
|
||||
useEffect(() => {
|
||||
const element = scrollableContent.current;
|
||||
if (!element) return;
|
||||
@@ -65,12 +71,14 @@ export default function Home(props: {
|
||||
},
|
||||
)}
|
||||
>
|
||||
<TabSelector
|
||||
onClick={() => setTab('network')}
|
||||
selected={tab === 'network'}
|
||||
>
|
||||
Network
|
||||
</TabSelector>
|
||||
{developerMode && (
|
||||
<TabSelector
|
||||
onClick={() => setTab('network')}
|
||||
selected={tab === 'network'}
|
||||
>
|
||||
Network
|
||||
</TabSelector>
|
||||
)}
|
||||
<TabSelector
|
||||
onClick={() => setTab('history')}
|
||||
selected={tab === 'history'}
|
||||
@@ -88,7 +96,9 @@ export default function Home(props: {
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
{tab === 'history' && <History />}
|
||||
{tab === 'network' && <Requests shouldFix={shouldFix} />}
|
||||
{tab === 'network' && developerMode && (
|
||||
<Requests shouldFix={shouldFix} />
|
||||
)}
|
||||
{tab === 'plugins' && (
|
||||
<PluginList
|
||||
className="p-2 overflow-y-auto"
|
||||
|
||||
@@ -384,15 +384,6 @@ function AdvancedOptions(props: {
|
||||
setDirty(true);
|
||||
}}
|
||||
/>
|
||||
<InputField
|
||||
label="Rendezvous API (for P2P)"
|
||||
value={rendezvous}
|
||||
type="text"
|
||||
onChange={(e) => {
|
||||
setRendezvous(e.target.value);
|
||||
setDirty(true);
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col flex-nowrap py-1 px-2 gap-2">
|
||||
<div className="font-semibold">Logging Level</div>
|
||||
<select
|
||||
|
||||
@@ -151,6 +151,15 @@ export default function ProofViewer(props?: {
|
||||
label="Notary Key"
|
||||
value={props?.notaryKey || request?.verification?.notaryKey}
|
||||
/>
|
||||
|
||||
{request?.metadata &&
|
||||
Object.entries(request.metadata).map(([key, value]) => (
|
||||
<MetadataRow
|
||||
key={`req-${key}`}
|
||||
label={`Custom: ${key}`}
|
||||
value={String(value)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -188,11 +188,13 @@ export const makePlugin = async (
|
||||
} = {
|
||||
redirect: function (context: CallContext, off: bigint) {
|
||||
const r = context.read(off);
|
||||
if (!r) throw new Error('Failed to read context');
|
||||
const url = r.text();
|
||||
browser.tabs.update(tab.id, { url });
|
||||
},
|
||||
notarize: function (context: CallContext, off: bigint) {
|
||||
const r = context.read(off);
|
||||
if (!r) throw new Error('Failed to read context');
|
||||
const params = JSON.parse(r.text());
|
||||
const now = Date.now();
|
||||
const id = charwise.encode(now).toString('hex');
|
||||
@@ -221,11 +223,20 @@ export const makePlugin = async (
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const { getSecretResponse, body: reqBody } = params;
|
||||
const {
|
||||
getSecretResponse,
|
||||
body: reqBody,
|
||||
interactive,
|
||||
verifierPlugin,
|
||||
} = params;
|
||||
|
||||
if (meta?.p2p) {
|
||||
console.log('interactive', interactive);
|
||||
console.log('verifierPlugin', verifierPlugin);
|
||||
console.log('params', params);
|
||||
if (interactive) {
|
||||
const pluginHex = Buffer.from(arrayBuffer).toString('hex');
|
||||
const pluginUrl = await sha256(pluginHex);
|
||||
|
||||
handleExecP2PPluginProver({
|
||||
type: BackgroundActiontype.execute_p2p_plugin_prover,
|
||||
data: {
|
||||
@@ -234,7 +245,7 @@ export const makePlugin = async (
|
||||
pluginHex,
|
||||
body: reqBody,
|
||||
now,
|
||||
clientId: meta.clientId,
|
||||
verifierPlugin,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -338,7 +349,10 @@ export const makePlugin = async (
|
||||
|
||||
const pluginConfig: ExtismPluginOptions = {
|
||||
useWasi: true,
|
||||
config: injectedConfig,
|
||||
config: {
|
||||
...injectedConfig,
|
||||
tabId: tab.id?.toString() || '',
|
||||
},
|
||||
// allowedHosts: approvedRequests.map((r) => urlify(r.url)?.origin),
|
||||
functions: {
|
||||
'extism:host/user': funcs,
|
||||
@@ -349,12 +363,23 @@ export const makePlugin = async (
|
||||
return plugin;
|
||||
};
|
||||
|
||||
export type InputFieldConfig = {
|
||||
name: string; // Unique identifier for the input field
|
||||
label: string; // Display label for the input
|
||||
type: 'text' | 'password' | 'email' | 'number' | 'textarea' | 'select'; // Input field type
|
||||
placeholder?: string; // Optional placeholder text
|
||||
required?: boolean; // Whether the field is required
|
||||
defaultValue?: string; // Default value for the field
|
||||
options?: { value: string; label: string }[]; // Options for select type
|
||||
};
|
||||
|
||||
export type StepConfig = {
|
||||
title: string; // Text for the step's title
|
||||
description?: string; // Text for the step's description (optional)
|
||||
cta: string; // Text for the step's call-to-action button
|
||||
action: string; // The function name that this step will execute
|
||||
prover?: boolean; // Boolean indicating if this step outputs a notarization (optional)
|
||||
inputs?: InputFieldConfig[]; // Input fields for user data collection (optional)
|
||||
};
|
||||
|
||||
export type PluginConfig = {
|
||||
@@ -382,6 +407,7 @@ export const getPluginConfig = async (
|
||||
): Promise<PluginConfig> => {
|
||||
const plugin = data instanceof ArrayBuffer ? await makePlugin(data) : data;
|
||||
const out = await plugin.call('config');
|
||||
if (!out) throw new Error('Plugin config call returned null');
|
||||
const config: PluginConfig = JSON.parse(out.string());
|
||||
|
||||
assert(typeof config.title === 'string' && config.title.length);
|
||||
@@ -439,6 +465,23 @@ export const getPluginConfig = async (
|
||||
assert(typeof step.cta === 'string' && step.cta.length);
|
||||
assert(typeof step.action === 'string' && step.action.length);
|
||||
assert(!step.prover || typeof step.prover === 'boolean');
|
||||
|
||||
if (step.inputs) {
|
||||
for (const input of step.inputs) {
|
||||
assert(typeof input.name === 'string' && input.name.length);
|
||||
assert(typeof input.label === 'string' && input.label.length);
|
||||
assert(!input.placeholder || typeof input.placeholder === 'string');
|
||||
assert(!input.required || typeof input.required === 'boolean');
|
||||
assert(!input.defaultValue || typeof input.defaultValue === 'string');
|
||||
if (input.type === 'select') {
|
||||
assert(Array.isArray(input.options) && input.options.length > 0);
|
||||
for (const option of input.options!) {
|
||||
assert(typeof option.value === 'string');
|
||||
assert(typeof option.label === 'string');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,16 +205,6 @@ var options = {
|
||||
to: path.join(__dirname, "build"),
|
||||
force: true,
|
||||
},
|
||||
{
|
||||
from: "src/assets/plugins/discord_dm.wasm",
|
||||
to: path.join(__dirname, "build"),
|
||||
force: true,
|
||||
},
|
||||
{
|
||||
from: "src/assets/plugins/twitter_profile.wasm",
|
||||
to: path.join(__dirname, "build"),
|
||||
force: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
|
||||
Reference in New Issue
Block a user