Skip to content

Feat/phishing controller hotlist timestamp #5621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

0xOhm
Copy link

@0xOhm 0xOhm commented Apr 9, 2025

Explanation

  • What is the current state of things and why does it need to change? This needed to change in order to make sure we dont drop updates
  • What is the solution your changes offer and how does it work? We introduced a timestamp on the phishing detection endpoint to ensure we have the correct timestamp

References

Are there any issues that this pull request is tied to? Issue
Are there other links that reviewers should consult to understand these changes better? Michael Hahn and Jacob Lebowitz

Changelog

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

0xOhm added 3 commits April 9, 2025 12:49
…w type update for c2Blocklist having lastFetchedAt being a number now rather than string

Signed-off-by: Ohm <[email protected]>
@0xOhm 0xOhm requested review from a team as code owners April 9, 2025 18:29
});

// Mock a successful response with lastFetchedAt at the root level similar to the example
nock(PHISHING_CONFIG_BASE_URL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With nock we also want to ensure that the request was actually done and the mock was hit. You can do that by using the isDone (https://github.com/nock/nock?tab=readme-ov-file#isdone) expecation from nock:

const scope = nock(...);
...
expect(scope.isDone()).toBe(true);


// Verify the timestamp wasn't changed
expect(controller.state.hotlistLastSuccessTimestamp).toBe(1744213500);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it seems that we are actually doing 3 tests:

  • mock successfull response with lastFetchedAt
  • mock successfull response without lastFetchedAt
  • mock failed response

Each of those should be its own test (an it descriptor for each case). You can read more about testing best practices in the contributor docs:

@@ -148,10 +148,16 @@ export type HotlistDiff = {
isRemoval?: boolean;
};

// TODO: Either fix this lint violation or explain why it's necessary to ignore.
// eslint-disable-next-line @typescript-eslint/naming-convention
/**
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is explicitly the new JSDoc tags

@@ -792,7 +812,7 @@ export class PhishingController extends BaseController<
const newPhishingLists = this.state.phishingLists.map((phishingList) => {
const updatedList = applyDiffs(
phishingList,
[],
[], // Proper Hotlist object with empty data array

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this comment

eth_phishing_detect_config: {
allowlist: [allowlistedHostname],
blocklist: [],
fuzzylist: [],
},
// eslint-disable-next-line @typescript-eslint/naming-convention

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why removing these throughout the entire file?

@@ -714,19 +724,20 @@ export class PhishingController extends BaseController<
* this function that prevents redundant configuration updates.
*/
async #updateHotlist() {
if (this.state.hotlistLastSuccessTimestamp === 0) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the updateHotlist() function need to update the stalelist?

My concern is that in the controller's default state, this value is set to 0. AFAIK, the controller should already be fetching the stalelist in that scenario. But then updateHotlist would then be called, potentially causing a duplicate request to update the stalelist. Let me know if I am missing something here

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should run this locally to ensure there are not duplicate stalelist requests being made since that is a heavy request

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree with @mindofmar.. not sure why we are calling updateStalelist in this scenario. If the stale list is out of date, the updateHotlist method will not be called. So not sure why we are doing this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to force a Stalelist refresh on update to this version.

@@ -673,6 +683,7 @@ export class PhishingController extends BaseController<
this.update((draftState) => {
draftState.stalelistLastFetched = timeNow;
draftState.hotlistLastFetched = timeNow;
draftState.hotlistLastSuccessTimestamp = timeNow;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we were relying on the staleList lastUpdated field for querying the hotList and now we are replacing it with the hotlistLastSuccessTimestamp. However as you can see here, the lastUpdated is a property of the staleList response while the hotlistLastSuccessTimestamp is computed in the client. This can cause multiple issues:

  • The staleList might have been computed sometime into the past. We should not assume that the list returned represents the latest available values at the time that the client receives it.
  • Using a computed property in the client, will not allow us to capture any items added to the list in between the request being handled by the API and the client computing the timestamp value.

I might be missing something, but I don't think that this implementation is quite right. I believe it should change to something like:

draftState.hotlistLastSuccessTimestamp = hotlistDiffsResponse?.lastFetchedAt || this.state.hotlistLastSuccessTimestamp

hotlistResponse = await this.#queryConfig<DataResultWrapper<Hotlist>>(
`${METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`,
`${METAMASK_HOTLIST_DIFF_URL}/${this.state.hotlistLastSuccessTimestamp}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the hotlistLastSuccessTimestamp is still set to 0? Shouldn't we use the state.phishingLists[].lastUpdated?

Comment on lines +756 to +761
this.update((draftState) => {
// We've already verified that hotlistResponse is not null above
draftState.hotlistLastSuccessTimestamp =
hotlistResponse?.lastFetchedAt ||
draftState.hotlistLastSuccessTimestamp;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we prevent updating it to the same value as before? (there's no value in that operation). We could update the code here to something like:

if (hotlistResponse?.lastFetchedAt) {
    this.update((draftState) => {
      draftState.hotlistLastSuccessTimestamp = hotlistResponse.lastFetchedAt;
    });
}

Also I don't think that we need the code comments added. You can drop those as the code is quite readable here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants