diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index 91070ed67..8ec1402c4 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -8,7 +8,68 @@ const METAMASK_ENVIRONMENT = process.env.METAMASK_ENVIRONMENT const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505' const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496' -export default function setupSentry ({ release }) { +// This describes the subset of Redux state attached to errors sent to Sentry +// These properties have some potential to be useful for debugging, and they do +// not contain any identifiable information. +export const SENTRY_STATE = { + gas: true, + history: true, + metamask: { + alertEnabledness: true, + completedOnboarding: true, + connectedStatusPopoverHasBeenShown: true, + conversionDate: true, + conversionRate: true, + currentBlockGasLimit: true, + currentCurrency: true, + currentLocale: true, + customNonceValue: true, + defaultHomeActiveTabName: true, + featureFlags: true, + firstTimeFlowType: true, + forgottenPassword: true, + incomingTxLastFetchedBlocksByNetwork: true, + ipfsGateway: true, + isAccountMenuOpen: true, + isInitialized: true, + isUnlocked: true, + metaMetricsId: true, + metaMetricsSendCount: true, + nativeCurrency: true, + network: true, + nextNonce: true, + participateInMetaMetrics: true, + preferences: true, + provider: { + nickname: true, + ticker: true, + type: true, + }, + seedPhraseBackedUp: true, + settings: { + chainId: true, + ticker: true, + nickname: true, + }, + showRestorePrompt: true, + threeBoxDisabled: true, + threeBoxLastUpdated: true, + threeBoxSynced: true, + threeBoxSyncingAllowed: true, + unapprovedDecryptMsgCount: true, + unapprovedEncryptionPublicKeyMsgCount: true, + unapprovedMsgCount: true, + unapprovedPersonalMsgCount: true, + unapprovedTypedMessagesCount: true, + useBlockie: true, + useNonceField: true, + usePhishDetect: true, + welcomeScreenSeen: true, + }, + unconnectedAccount: true, +} + +export default function setupSentry ({ release, getState }) { let sentryTarget if (METAMASK_DEBUG || process.env.IN_TEST) { @@ -37,6 +98,11 @@ export default function setupSentry ({ release }) { simplifyErrorMessages(report) // modify report urls rewriteReportUrls(report) + // append app state + if (getState) { + const appState = getState() + report.extra.appState = appState + } } catch (err) { console.warn(err) } diff --git a/app/scripts/ui.js b/app/scripts/ui.js index 822ea73a4..3a76a5f4e 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -36,7 +36,10 @@ async function start () { // setup sentry error reporting const release = global.platform.getVersion() - setupSentry({ release }) + setupSentry({ + release, + getState: () => window.getSentryState?.() || {}, + }) // identify window type (popup, notification) const windowType = getEnvironmentType() diff --git a/ui/index.js b/ui/index.js index c5547fbfb..bc61913b2 100644 --- a/ui/index.js +++ b/ui/index.js @@ -9,6 +9,7 @@ import configureStore from './app/store/store' import txHelper from './lib/tx-helper' import { getEnvironmentType } from '../app/scripts/lib/util' import { ALERT_TYPES } from '../app/scripts/controllers/alert' +import { SENTRY_STATE } from '../app/scripts/lib/setupSentry' import { ENVIRONMENT_TYPE_POPUP } from '../app/scripts/lib/enums' import { fetchLocale, loadRelativeTimeFormatLocaleData } from './app/helpers/utils/i18n-helper' import switchDirection from './app/helpers/utils/switch-direction' @@ -138,6 +139,33 @@ async function startApp (metamaskState, backgroundConnection, opts) { return store } +/** + * Return a "masked" copy of the given object. + * + * The returned object includes only the properties present in the mask. The + * mask is an object that mirrors the structure of the given object, except + * the only values are `true` or a sub-mask. `true` implies the property + * should be included, and a sub-mask implies the property should be further + * masked according to that sub-mask. + * + * @param {Object} object - The object to mask + * @param {Object} mask - The mask to apply to the object + */ +function maskObject (object, mask) { + return Object.keys(object) + .reduce( + (state, key) => { + if (mask[key] === true) { + state[key] = object[key] + } else if (mask[key]) { + state[key] = maskObject(object[key], mask[key]) + } + return state + }, + {}, + ) +} + function setupDebuggingHelpers (store) { window.getCleanAppState = function () { const state = clone(store.getState()) @@ -145,6 +173,15 @@ function setupDebuggingHelpers (store) { state.browser = window.navigator.userAgent return state } + window.getSentryState = function () { + const fullState = store.getState() + const debugState = maskObject(fullState, SENTRY_STATE) + return { + browser: window.navigator.userAgent, + store: debugState, + version: global.platform.getVersion(), + } + } } window.logStateString = function (cb) {