diff --git a/packages/yoroi-extension/app/api/thunk.js b/packages/yoroi-extension/app/api/thunk.js index 8bdd30e91f..aab033a4cf 100644 --- a/packages/yoroi-extension/app/api/thunk.js +++ b/packages/yoroi-extension/app/api/thunk.js @@ -107,9 +107,12 @@ declare var chrome; // UI -> background queries: -export function callBackground(message: T): Promise { +export function callBackground(message: {| type: string, request?: Object |}): Promise { return new Promise((resolve, reject) => { - window.chrome.runtime.sendMessage(message, response => { + const serializedMessage = { type: message.type, request: JSON.stringify(message.request ?? null) }; + window.chrome.runtime.sendMessage(serializedMessage, response => { + // $FlowIgnore + console.debug(`CLIENT [${message.type}] received result: `, JSON.stringify(sanitizeForLog(response))); if (window.chrome.runtime.lastError) { // eslint-disable-next-line prefer-promise-reject-errors reject(`Error ${window.chrome.runtime.lastError} when calling the background with: ${JSON.stringify(sanitizeForLog(message)) ?? 'undefined'}`); @@ -290,7 +293,8 @@ export async function removeAllTransactions( } } -export const popAddress: GetEntryFuncType = async ({ publicDeriverId }) => { +type PopAddressType = ({ publicDeriverId: number, ...}) => ReturnType>; +export const popAddress: PopAddressType = async ({ publicDeriverId }) => { await callBackground({ type: PopAddress.typeTag, request: { publicDeriverId } }); } @@ -306,11 +310,12 @@ function deserializeTx(tx: any): ?WalletTransaction { } export const refreshTransactions: GetEntryFuncType = async (request) => { - const txs = await callBackground({ type: 'refresh-transactions', request }); - if (txs.error) { - console.error('Failed to refresh transactions!', txs.error); + const resp = await callBackground({ type: RefreshTransactions.typeTag, request }); + if (resp.error) { + console.error('Failed to refresh transactions!', resp.error); return []; } + const txs = JSON.parse(resp); return txs.map(tx => { try { return deserializeTx(tx); @@ -398,8 +403,11 @@ export const getConnectedSites: GetEntryFuncType = asy return await callBackground({ type: GetConnectedSites.typeTag }); } -export const getProtocolParameters: GetEntryFuncType = async (request) => { - return await callBackground({ type: GetProtocolParameters.typeTag, request }); +type GetProtocolParametersType = ({ networkId: number, ... }) => ReturnType>; +export const getProtocolParameters: GetProtocolParametersType = async ( + { networkId } +) => { + return await callBackground({ type: GetProtocolParameters.typeTag, request: { networkId } }); } // Background -> UI notifications: @@ -408,8 +416,34 @@ const callbacks = Object.freeze({ serverStatusUpdate: [], coinPriceUpdate: [], }); -chrome.runtime.onMessage.addListener(async (message, _sender, _sendResponse) => { - //fixme: verify sender.id/origin +const APP_ORIGIN = window.location.origin || null; +const EXPECTED_MESSAGE_TYPE = 'yoroi-emit-update'; +chrome.runtime.onMessage.addListener((rawMessage, { origin }, _sendResponse) => { + if (APP_ORIGIN != null && origin !== APP_ORIGIN) { + Logger.debug('[client] ignoring non-origin message (' + origin + '/' + APP_ORIGIN + ')'); + return; + } + if (rawMessage.type !== EXPECTED_MESSAGE_TYPE) { + Logger.debug('[client] ignoring unknown type message (' + rawMessage.type + '/' + EXPECTED_MESSAGE_TYPE + ')'); + return; + } + const serializedMessage = rawMessage.data; + const messageType = typeof serializedMessage; + if (messageType !== 'string') { + Logger.error('[client] unexpected message type (' + messageType + ') a JSON string is expected, but received: ' + JSON.stringify(sanitizeForLog(serializedMessage))); + return; + } + let message; + try { + message = JSON.parse(serializedMessage); + } catch (error) { + Logger.error('unparsable message: ' + serializedMessage + ' | Error: ' + stringifyError(error)); + return; + } + if (typeof message !== 'object') { + Logger.error('unrecognizable message type: ' + (typeof message) + ' (expected object); Original message: ' + serializedMessage); + return; + } Logger.debug('get message from background:', JSON.stringify(sanitizeForLog(message))); if (message.type === 'wallet-state-update') { diff --git a/packages/yoroi-extension/app/coreUtils.js b/packages/yoroi-extension/app/coreUtils.js index 1d60daf755..be0a53c946 100644 --- a/packages/yoroi-extension/app/coreUtils.js +++ b/packages/yoroi-extension/app/coreUtils.js @@ -225,11 +225,13 @@ export function timeCached(fun: () => R, ttl: number): () => R { * @return same value or a copy in case the value is an object */ export function sanitizeForLog(v: any): any { + const fields: Array = ['password']; if (v != null && typeof v === 'object') { let r = Object.keys(v).reduce((o, k) => ({ ...o, [k]: sanitizeForLog(v[k]) }) , {}) - // $FlowIgnore[incompatible-use] - if (r.password != null) { - r = { ...r, password: '[sanitized]' }; + for (const f of fields) { + if (r[f] != null) { + r = { ...r, [f]: '[sanitized]' }; + } } return r; } diff --git a/packages/yoroi-extension/app/domain/CardanoShelleyTransaction.js b/packages/yoroi-extension/app/domain/CardanoShelleyTransaction.js index 4beaca3ff4..f4f3fa1322 100644 --- a/packages/yoroi-extension/app/domain/CardanoShelleyTransaction.js +++ b/packages/yoroi-extension/app/domain/CardanoShelleyTransaction.js @@ -219,7 +219,11 @@ export function deserializeTransactionCtorData( state: serializedData.state, errorMsg: serializedData.errorMsg, certificates: serializedData.certificates, - ttl: serializedData.ttl && new BigNumber(serializedData.ttl), + ttl: serializedData.ttl && new BigNumber( + typeof serializedData.ttl === 'object' + ? { ...serializedData.ttl, _isBigNumber: true } + : serializedData.ttl + ), metadata: serializedData.metadata, withdrawals: serializedData.withdrawals.map(({ address, value }) => ({ address, diff --git a/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/index.js b/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/index.js index f935d3711a..4b88c7b783 100644 --- a/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/index.js +++ b/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/index.js @@ -43,6 +43,7 @@ import { } from './connector'; import { GetProtocolParameters } from './protocolParameters'; import { subscribe } from '../../subscriptionManager'; +import { sanitizeForLog } from '../../../../../app/coreUtils'; const handlerMap = Object.freeze({ [GetHistoricalCoinPrices.typeTag]: GetHistoricalCoinPrices.handle, @@ -107,6 +108,7 @@ export function getHandler(typeTag: string): ?Handler { return async (request, send, sendResponse) => { try { const result = await handler(request.request); + console.debug(`BACKGROUND [${typeTag}] sending result: `, JSON.stringify(sanitizeForLog(result))); sendResponse(result); } catch (error) { sendResponse({ error: error.message }); diff --git a/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/protocolParameters.js b/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/protocolParameters.js index 93e4cdb8c0..109f64594b 100644 --- a/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/protocolParameters.js +++ b/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/protocolParameters.js @@ -206,7 +206,7 @@ class ProcolParameterApi { } export const GetProtocolParameters: HandlerType< - { networkId: number, ... }, + {| networkId: number |}, ProtocolParameters > = Object.freeze({ typeTag: 'get-protocol-parameters', diff --git a/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/wallet.js b/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/wallet.js index 3bdce74838..bd9b23d1e8 100644 --- a/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/wallet.js +++ b/packages/yoroi-extension/chrome/extension/background/handlers/yoroi/wallet.js @@ -250,7 +250,7 @@ export const GetPrivateStakingKey: HandlerType< }); export const RemoveAllTransactions: HandlerType< - { publicDeriverId: number, ... }, + {| publicDeriverId: number |}, void > = Object.freeze({ typeTag: 'remove-all-transactions', @@ -275,7 +275,7 @@ export const RemoveAllTransactions: HandlerType< }); export const PopAddress: HandlerType< - { publicDeriverId: number, ... }, + {| publicDeriverId: number |}, void > = Object.freeze({ typeTag: 'pop-address', @@ -354,6 +354,7 @@ export const RefreshTransactions: HandlerType< // initial transaction list loading txs = await adaApi.refreshTransactions(refreshTxRequest); } - return txs; + // $FlowIgnore + return JSON.stringify(txs); }, }); diff --git a/packages/yoroi-extension/chrome/extension/background/index.js b/packages/yoroi-extension/chrome/extension/background/index.js index 9df389ca06..ab35d1d8d7 100644 --- a/packages/yoroi-extension/chrome/extension/background/index.js +++ b/packages/yoroi-extension/chrome/extension/background/index.js @@ -36,7 +36,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { } const handler = getHandler(message.type); if (handler) { - handler(message, sender, sendResponse); + const deserializedMessage = { + type: message.type, + request: JSON.parse(message.request), + }; + handler(deserializedMessage, sender, sendResponse); // Returning `true` is required by Firefox, see: // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage return true; diff --git a/packages/yoroi-extension/chrome/extension/background/subscriptionManager.js b/packages/yoroi-extension/chrome/extension/background/subscriptionManager.js index a04d09943b..bd69ca4799 100644 --- a/packages/yoroi-extension/chrome/extension/background/subscriptionManager.js +++ b/packages/yoroi-extension/chrome/extension/background/subscriptionManager.js @@ -68,6 +68,6 @@ declare var chrome; */ export function emitUpdateToSubscriptions(data: Object): void { for (const { tabId } of getSubscriptions()) { - chrome.tabs.sendMessage(tabId, data); + chrome.tabs.sendMessage(tabId, { type: 'yoroi-emit-update', data: JSON.stringify(data) }); } }