Skip to content

Commit

Permalink
feat: Add anonymized signature metrics (#27298)
Browse files Browse the repository at this point in the history
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Adds logic to send Signature Requested Anon, Signature Approved Anon and
Signature Rejected Anon events when the signature is of type
signTypedData_v4 with additional anonymous properties.
Updates the e2e to check the correct Anon events are sent with the
additional properties
Added additional unit tests to increase sonar coverage
Exclude the test folder from sonar checks

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27162?quickstart=1)

## **Related issues**

Fixes: [3009](MetaMask/MetaMask-planning#3009)

## **Manual testing steps**

1. Go to test dapp
2. Under the Permit section, click on Sign
3. This should trigger the Signature Anon events with:
      ```
eip712_verifyingContract == 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC
      eip712_domain_version == 1
      eip712_domain_name == MyToken
      ```

1. Go to test dapp
2. Under the SignTypedData_v4 section, click on Sign
3. This should trigger the Signature Anon events with:
      ```
eip712_verifyingContract == 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC
      eip712_domain_version == 1
      eip712_domain_name == Ether Mail
      ```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
  • Loading branch information
pnarayanaswamy authored Sep 23, 2024
1 parent c2858b4 commit f17891a
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 114 deletions.
7 changes: 7 additions & 0 deletions app/scripts/controllers/metametrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
import {
METAMETRICS_ANONYMOUS_ID,
METAMETRICS_BACKGROUND_PAGE_OBJECT,
MetaMetricsEventName,
MetaMetricsUserTrait,
} from '../../../shared/constants/metametrics';
import { SECOND } from '../../../shared/constants/time';
Expand All @@ -40,6 +41,12 @@ export const overrideAnonymousEventNames = {
AnonymousTransactionMetaMetricsEvent.rejected,
[TransactionMetaMetricsEvent.submitted]:
AnonymousTransactionMetaMetricsEvent.submitted,
[MetaMetricsEventName.SignatureRequested]:
MetaMetricsEventName.SignatureRequestedAnon,
[MetaMetricsEventName.SignatureApproved]:
MetaMetricsEventName.SignatureApprovedAnon,
[MetaMetricsEventName.SignatureRejected]:
MetaMetricsEventName.SignatureRejectedAnon,
};

const defaultCaptureException = (err) => {
Expand Down
32 changes: 32 additions & 0 deletions app/scripts/controllers/metametrics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,38 @@ describe('MetaMetricsController', function () {
});
});

describe('Change Signature XXX anonymous event names', function () {
it.each([
['Signature Requested', 'Signature Requested Anon'],
['Signature Rejected', 'Signature Rejected Anon'],
['Signature Approved', 'Signature Approved Anon'],
])(
'should change "%s" anonymous event names to "%s"',
(eventType, anonEventType) => {
const metaMetricsController = getMetaMetricsController();
const spy = jest.spyOn(segment, 'track');
metaMetricsController.submitEvent({
event: eventType,
category: 'Unit Test',
properties: { ...DEFAULT_EVENT_PROPERTIES },
sensitiveProperties: { foo: 'bar' },
});

expect(spy).toHaveBeenCalledTimes(2);

expect(spy.mock.calls[0][0]).toMatchObject({
event: anonEventType,
properties: { foo: 'bar', ...DEFAULT_EVENT_PROPERTIES },
});

expect(spy.mock.calls[1][0]).toMatchObject({
event: eventType,
properties: { ...DEFAULT_EVENT_PROPERTIES },
});
},
);
});

describe('Change Transaction XXX anonymous event namnes', function () {
it('should change "Transaction Added" anonymous event names to "Transaction Added Anon"', function () {
const metaMetricsController = getMetaMetricsController();
Expand Down
48 changes: 30 additions & 18 deletions app/scripts/lib/createRPCMethodTrackingMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,11 @@ let globalRateLimitCount = 0;
function createSignatureFragment(metaMetricsController, req, fragmentPayload) {
metaMetricsController.createEventFragment({
category: MetaMetricsEventCategory.InpageProvider,

initialEvent: MetaMetricsEventName.SignatureRequested,
successEvent: MetaMetricsEventName.SignatureApproved,
failureEvent: MetaMetricsEventName.SignatureRejected,

uniqueIdentifier: generateSignatureUniqueId(req.id),
persist: true,
referrer: {
Expand All @@ -164,12 +166,7 @@ function finalizeSignatureFragment(
) {
const signatureUniqueId = generateSignatureUniqueId(req.id);

if (fragmentPayload) {
metaMetricsController.updateEventFragment(
signatureUniqueId,
fragmentPayload,
);
}
metaMetricsController.updateEventFragment(signatureUniqueId, fragmentPayload);

metaMetricsController.finalizeEventFragment(
signatureUniqueId,
Expand Down Expand Up @@ -222,8 +219,7 @@ export default function createRPCMethodTrackingMiddleware({
) {
const { origin, method, params } = req;

const rateLimitType =
RATE_LIMIT_MAP[method] ?? RATE_LIMIT_TYPES.RANDOM_SAMPLE;
const rateLimitType = RATE_LIMIT_MAP[method];

let isRateLimited;
switch (rateLimitType) {
Expand Down Expand Up @@ -258,6 +254,7 @@ export default function createRPCMethodTrackingMiddleware({
const eventType = EVENT_NAME_MAP[method];

const eventProperties = {};
let sensitiveEventProperties;

// Boolean variable that reduces code duplication and increases legibility
const shouldTrackEvent =
Expand All @@ -270,8 +267,6 @@ export default function createRPCMethodTrackingMiddleware({
// Don't track if the user isn't participating in metametrics
userParticipatingInMetaMetrics === true;

let signatureUniqueId;

if (shouldTrackEvent) {
// We track an initial "requested" event as soon as the dapp calls the
// provider method. For the events not special cased this is the only
Expand Down Expand Up @@ -346,14 +341,25 @@ export default function createRPCMethodTrackingMiddleware({
];
}
} else if (method === MESSAGE_TYPE.ETH_SIGN_TYPED_DATA_V4) {
const { primaryType } = parseTypedDataMessage(data);
eventProperties.eip712_primary_type = primaryType;
if (PRIMARY_TYPES_PERMIT.includes(primaryType)) {
const parsedMessageData = parseTypedDataMessage(data);
sensitiveEventProperties = {};

eventProperties.eip712_primary_type = parsedMessageData.primaryType;
sensitiveEventProperties.eip712_verifyingContract =
parsedMessageData.domain.verifyingContract;
sensitiveEventProperties.eip712_domain_version =
parsedMessageData.domain.version;
sensitiveEventProperties.eip712_domain_name =
parsedMessageData.domain.name;

if (PRIMARY_TYPES_PERMIT.includes(parsedMessageData.primaryType)) {
eventProperties.ui_customizations = [
...(eventProperties.ui_customizations || []),
MetaMetricsEventUiCustomization.Permit,
];
} else if (PRIMARY_TYPES_ORDER.includes(primaryType)) {
} else if (
PRIMARY_TYPES_ORDER.includes(parsedMessageData.primaryType)
) {
eventProperties.ui_customizations = [
...(eventProperties.ui_customizations || []),
MetaMetricsEventUiCustomization.Order,
Expand All @@ -373,9 +379,12 @@ export default function createRPCMethodTrackingMiddleware({
}

if (event === MetaMetricsEventName.SignatureRequested) {
createSignatureFragment(metaMetricsController, req, {
const fragmentPayload = {
properties: eventProperties,
});
sensitiveProperties: sensitiveEventProperties,
};

createSignatureFragment(metaMetricsController, req, fragmentPayload);
} else {
metaMetricsController.trackEvent({
event,
Expand Down Expand Up @@ -436,12 +445,16 @@ export default function createRPCMethodTrackingMiddleware({
location,
};

if (signatureUniqueId) {
if (
event === MetaMetricsEventName.SignatureRejected ||
event === MetaMetricsEventName.SignatureApproved
) {
const finalizeOptions = {
abandoned: event === eventType.REJECTED,
};
const fragmentPayload = {
properties,
sensitiveProperties: sensitiveEventProperties,
};

finalizeSignatureFragment(
Expand All @@ -460,7 +473,6 @@ export default function createRPCMethodTrackingMiddleware({
properties,
});
}

return callback();
});
};
Expand Down
Loading

0 comments on commit f17891a

Please sign in to comment.