@metamask-previews/phishing-controller 6.0.0-preview.d32a7cc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/LICENSE +20 -0
- package/README.md +15 -0
- package/dist/PhishingController.d.ts +256 -0
- package/dist/PhishingController.d.ts.map +1 -0
- package/dist/PhishingController.js +335 -0
- package/dist/PhishingController.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +71 -0
- package/dist/utils.js.map +1 -0
- package/package.json +56 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [6.0.0]
|
|
10
|
+
### Changed
|
|
11
|
+
- **BREAKING:** Remove fallback phishing configuration ([#1527](https://github.com/MetaMask/core/pull/1527))
|
|
12
|
+
- The default configuration is now blank. A custom initial configuration can still be specified via the constructor to preserve the old behavior.
|
|
13
|
+
|
|
14
|
+
## [5.0.0]
|
|
15
|
+
### Changed
|
|
16
|
+
- **BREAKING:** Bump to Node 16 ([#1262](https://github.com/MetaMask/core/pull/1262))
|
|
17
|
+
|
|
18
|
+
## [4.0.0]
|
|
19
|
+
### Changed
|
|
20
|
+
- **BREAKING:** Switch to new phishing configuration API that returns a diff since the last update ([#1123](https://github.com/MetaMask/core/pull/1123))
|
|
21
|
+
- The "hotlist" has been replaced by a service that returns any configuration changes since the last update. This should reduce network traffic even further.
|
|
22
|
+
- The endpoints used are now `https://phishing-detection.metafi.codefi.network/v1/stalelist` and `https://phishing-detection.metafi.codefi.network/v1/diffsSince/:lastUpdated`
|
|
23
|
+
- **BREAKING:**: The phishing controller state now keeps the MetaMask and PhishFort configuration separate, allowing for proper attribution of each block ([#1123](https://github.com/MetaMask/core/pull/1123))
|
|
24
|
+
- The `listState` state property has been replaced with an array of phishing list state objects (one entry for MetaMask, one for PhishFort).
|
|
25
|
+
- The PhishFort config is deduplicated server-side, so it should have zero overlap with the MetaMask configuration (which helps reduce memory/disk usage)
|
|
26
|
+
|
|
27
|
+
## [3.0.0]
|
|
28
|
+
### Removed
|
|
29
|
+
- **BREAKING:** Remove `isomorphic-fetch` ([#1106](https://github.com/MetaMask/controllers/pull/1106))
|
|
30
|
+
- Consumers must now import `isomorphic-fetch` or another polyfill themselves if they are running in an environment without `fetch`
|
|
31
|
+
|
|
32
|
+
## [2.0.0]
|
|
33
|
+
### Changed
|
|
34
|
+
- **BREAKING:** Refactor to Cost-Optimized Phishing List Data Architecture. ([#1080](https://github.com/MetaMask/core/pull/1080))
|
|
35
|
+
- Rather than periodically downloading two separate configurations (MetaMask and Phishfort), we now download a combined "stalelist" and "hotlist". The stalelist is downloaded every 4 days, and the hotlist is downloaded every 30 minutes. The hotlist only includes data from the last 8 days, which should dramatically reduce the required network traffic for phishing config updates.
|
|
36
|
+
- When a site is blocked, we no longer know which list is responsible due to the combined format. We will need to come up with another way to attribute blocks to a specific list; this controller will no longer be responsible for that.
|
|
37
|
+
- This change includes the removal of the exports:
|
|
38
|
+
- `METAMASK_CONFIG_FILE` and `PHISHFORT_HOTLIST_FILE` (replaced by `METAMASK_STALELIST_FILE` and `METAMASK_HOTLIST_DIFF_FILE`)
|
|
39
|
+
- `METAMASK_CONFIG_URL` and `PHISHFORT_HOTLIST_URL` (replaced by `METAMASK_STALELIST_URL` and `METAMASK_HOTLIST_DIFF_URL`)
|
|
40
|
+
- `EthPhishingResponse` (replaced by `PhishingStalelist` for the API response and `PhishingListState` for the list in controller state, as they're now different)
|
|
41
|
+
- The configuration has changed:
|
|
42
|
+
- Instead of accepting a `refreshInterval`, we now accept a separate interval for the stalelist and hotlist (`stalelistRefreshInterval` and `hotlistRefreshInterval`)
|
|
43
|
+
- The controller state has been updated:
|
|
44
|
+
- The phishing list itself has been renamed from `phishing` to `listState`, and the shape has changed. Removing the old `phishing` state would be advised, as it will get replaced by an updated configuration immediately anyway.
|
|
45
|
+
- `lastFetched` has been replaced by `hotlistLastFetched` and `stalelistLastFetched`. The old `lastFetched` state can be removed as well (it never needed to be persisted anyway).
|
|
46
|
+
- The `setRefreshInterval` method has been replaced by `setStalelistRefreshInterval` and `setHotlistRefreshInterval`
|
|
47
|
+
- The `isOutOfDate` method has been replaced by `isStalelistOutOfDate` and `isHotlistOutOfDate`
|
|
48
|
+
- The `maybeUpdatePhishingLists` method has been replaced by `maybeUpdateState`
|
|
49
|
+
- The `updatePhishingLists` method has been replaced by `updateStalelist` and `updateHotlist`
|
|
50
|
+
|
|
51
|
+
## [1.1.2]
|
|
52
|
+
### Fixed
|
|
53
|
+
- Improve performance of phishing list update ([#1086](https://github.com/MetaMask/core/pull/1086))
|
|
54
|
+
- We now use a `Set` + `has` method instead of the array `includes` method for detecting overlap between phishing lists after an update.
|
|
55
|
+
|
|
56
|
+
## [1.1.1]
|
|
57
|
+
### Changed
|
|
58
|
+
- Rename this repository to `core` ([#1031](https://github.com/MetaMask/controllers/pull/1031))
|
|
59
|
+
- Update `@metamask/controller-utils` package ([#1041](https://github.com/MetaMask/controllers/pull/1041))
|
|
60
|
+
|
|
61
|
+
## [1.1.0]
|
|
62
|
+
### Added
|
|
63
|
+
- Add method to conditionally update the phishing lists ([#986](https://github.com/MetaMask/core/pull/986))
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
- Relax dependencies on `@metamask/base-controller` and `@metamask/controller-utils` (use `^` instead of `~`) ([#998](https://github.com/MetaMask/core/pull/998))
|
|
67
|
+
- Expose `lastFetched` in PhishingController state ([#986](https://github.com/MetaMask/core/pull/986))
|
|
68
|
+
|
|
69
|
+
## [1.0.0]
|
|
70
|
+
### Added
|
|
71
|
+
- Initial release
|
|
72
|
+
- As a result of converting our shared controllers repo into a monorepo ([#831](https://github.com/MetaMask/core/pull/831)), we've created this package from select parts of [`@metamask/controllers` v33.0.0](https://github.com/MetaMask/core/tree/v33.0.0), namely:
|
|
73
|
+
- `src/third-party/PhishingController.ts`
|
|
74
|
+
- `src/third-party/PhishingController.test.ts`
|
|
75
|
+
|
|
76
|
+
All changes listed after this point were applied to this package following the monorepo conversion.
|
|
77
|
+
|
|
78
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@6.0.0...HEAD
|
|
79
|
+
[6.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@5.0.0...@metamask/phishing-controller@6.0.0
|
|
80
|
+
[5.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@4.0.0...@metamask/phishing-controller@5.0.0
|
|
81
|
+
[4.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@3.0.0...@metamask/phishing-controller@4.0.0
|
|
82
|
+
[3.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@2.0.0...@metamask/phishing-controller@3.0.0
|
|
83
|
+
[2.0.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@1.1.2...@metamask/phishing-controller@2.0.0
|
|
84
|
+
[1.1.2]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@1.1.1...@metamask/phishing-controller@1.1.2
|
|
85
|
+
[1.1.1]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@1.1.0...@metamask/phishing-controller@1.1.1
|
|
86
|
+
[1.1.0]: https://github.com/MetaMask/core/compare/@metamask/phishing-controller@1.0.0...@metamask/phishing-controller@1.1.0
|
|
87
|
+
[1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/phishing-controller@1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 MetaMask
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# `@metamask/phishing-controller`
|
|
2
|
+
|
|
3
|
+
Maintains a periodically updated list of approved and unapproved website origins.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
`yarn add @metamask/phishing-controller`
|
|
8
|
+
|
|
9
|
+
or
|
|
10
|
+
|
|
11
|
+
`npm install @metamask/phishing-controller`
|
|
12
|
+
|
|
13
|
+
## Contributing
|
|
14
|
+
|
|
15
|
+
This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type { BaseConfig, BaseState } from '@metamask/base-controller';
|
|
2
|
+
import { BaseController } from '@metamask/base-controller';
|
|
3
|
+
/**
|
|
4
|
+
* @type ListTypes
|
|
5
|
+
*
|
|
6
|
+
* Type outlining the types of lists provided by aggregating different source lists
|
|
7
|
+
*/
|
|
8
|
+
export declare type ListTypes = 'fuzzylist' | 'blocklist' | 'allowlist';
|
|
9
|
+
/**
|
|
10
|
+
* @type EthPhishingResponse
|
|
11
|
+
*
|
|
12
|
+
* Configuration response from the eth-phishing-detect package
|
|
13
|
+
* consisting of approved and unapproved website origins
|
|
14
|
+
* @property blacklist - List of unapproved origins
|
|
15
|
+
* @property fuzzylist - List of fuzzy-matched unapproved origins
|
|
16
|
+
* @property tolerance - Fuzzy match tolerance level
|
|
17
|
+
* @property version - Version number of this configuration
|
|
18
|
+
* @property whitelist - List of approved origins
|
|
19
|
+
*/
|
|
20
|
+
export interface EthPhishingResponse {
|
|
21
|
+
blacklist: string[];
|
|
22
|
+
fuzzylist: string[];
|
|
23
|
+
tolerance: number;
|
|
24
|
+
version: number;
|
|
25
|
+
whitelist: string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @type PhishingStalelist
|
|
29
|
+
*
|
|
30
|
+
* Interface defining expected type of the stalelist.json file.
|
|
31
|
+
* @property eth_phishing_detect_config - Stale list sourced from eth-phishing-detect's config.json.
|
|
32
|
+
* @property phishfort_hotlist - Stale list sourced from phishfort's hotlist.json. Only includes blocklist. Deduplicated entries from eth_phishing_detect_config.
|
|
33
|
+
* @property tolerance - Fuzzy match tolerance level
|
|
34
|
+
* @property lastUpdated - Timestamp of last update.
|
|
35
|
+
* @property version - Stalelist data structure iteration.
|
|
36
|
+
*/
|
|
37
|
+
export interface PhishingStalelist {
|
|
38
|
+
eth_phishing_detect_config: Record<ListTypes, string[]>;
|
|
39
|
+
phishfort_hotlist: Record<ListTypes, string[]>;
|
|
40
|
+
tolerance: number;
|
|
41
|
+
version: number;
|
|
42
|
+
lastUpdated: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @type PhishingListState
|
|
46
|
+
*
|
|
47
|
+
* Interface defining the persisted list state. This is the persisted state that is updated frequently with `this.maybeUpdateState()`.
|
|
48
|
+
* @property allowlist - List of approved origins (legacy naming "whitelist")
|
|
49
|
+
* @property blocklist - List of unapproved origins (legacy naming "blacklist")
|
|
50
|
+
* @property fuzzylist - List of fuzzy-matched unapproved origins
|
|
51
|
+
* @property tolerance - Fuzzy match tolerance level
|
|
52
|
+
* @property lastUpdated - Timestamp of last update.
|
|
53
|
+
* @property version - Version of the phishing list state.
|
|
54
|
+
* @property name - Name of the list. Used for attribution.
|
|
55
|
+
*/
|
|
56
|
+
export interface PhishingListState {
|
|
57
|
+
allowlist: string[];
|
|
58
|
+
blocklist: string[];
|
|
59
|
+
fuzzylist: string[];
|
|
60
|
+
tolerance: number;
|
|
61
|
+
version: number;
|
|
62
|
+
lastUpdated: number;
|
|
63
|
+
name: ListNames;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* @type EthPhishingDetectResult
|
|
67
|
+
*
|
|
68
|
+
* Interface that describes the result of the `test` method.
|
|
69
|
+
* @property name - Name of the config on which a match was found.
|
|
70
|
+
* @property version - Version of the config on which a match was found.
|
|
71
|
+
* @property result - Whether a domain was detected as a phishing domain. True means an unsafe domain.
|
|
72
|
+
* @property match - The matching fuzzylist origin when a fuzzylist match is found. Returned as undefined for non-fuzzy true results.
|
|
73
|
+
* @property type - The field of the config on which a match was found.
|
|
74
|
+
*/
|
|
75
|
+
export interface EthPhishingDetectResult {
|
|
76
|
+
name?: string;
|
|
77
|
+
version?: string;
|
|
78
|
+
result: boolean;
|
|
79
|
+
match?: string;
|
|
80
|
+
type: 'all' | 'fuzzy' | 'blocklist' | 'allowlist';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* @type HotlistDiff
|
|
84
|
+
*
|
|
85
|
+
* Interface defining the expected type of the diffs in hotlist.json file.
|
|
86
|
+
* @property url - Url of the diff entry.
|
|
87
|
+
* @property timestamp - Timestamp at which the diff was identified.
|
|
88
|
+
* @property targetList - The list name where the diff was identified.
|
|
89
|
+
* @property isRemoval - Was the diff identified a removal type.
|
|
90
|
+
*/
|
|
91
|
+
export interface HotlistDiff {
|
|
92
|
+
url: string;
|
|
93
|
+
timestamp: number;
|
|
94
|
+
targetList: `${ListKeys}.${ListTypes}`;
|
|
95
|
+
isRemoval?: boolean;
|
|
96
|
+
}
|
|
97
|
+
export interface DataResultWrapper<T> {
|
|
98
|
+
data: T;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @type Hotlist
|
|
102
|
+
*
|
|
103
|
+
* Type defining expected hotlist.json file.
|
|
104
|
+
* @property url - Url of the diff entry.
|
|
105
|
+
* @property timestamp - Timestamp at which the diff was identified.
|
|
106
|
+
* @property targetList - The list name where the diff was identified.
|
|
107
|
+
* @property isRemoval - Was the diff identified a removal type.
|
|
108
|
+
*/
|
|
109
|
+
export declare type Hotlist = HotlistDiff[];
|
|
110
|
+
/**
|
|
111
|
+
* @type PhishingConfig
|
|
112
|
+
*
|
|
113
|
+
* Phishing controller configuration
|
|
114
|
+
* @property stalelistRefreshInterval - Polling interval used to fetch stale list.
|
|
115
|
+
* @property hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.
|
|
116
|
+
*/
|
|
117
|
+
export interface PhishingConfig extends BaseConfig {
|
|
118
|
+
stalelistRefreshInterval: number;
|
|
119
|
+
hotlistRefreshInterval: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* @type PhishingState
|
|
123
|
+
*
|
|
124
|
+
* Phishing controller state
|
|
125
|
+
* @property phishing - eth-phishing-detect configuration
|
|
126
|
+
* @property whitelist - array of temporarily-approved origins
|
|
127
|
+
*/
|
|
128
|
+
export interface PhishingState extends BaseState {
|
|
129
|
+
phishingLists: PhishingListState[];
|
|
130
|
+
whitelist: string[];
|
|
131
|
+
hotlistLastFetched: number;
|
|
132
|
+
stalelistLastFetched: number;
|
|
133
|
+
}
|
|
134
|
+
export declare const PHISHING_CONFIG_BASE_URL = "https://phishing-detection.metafi.codefi.network";
|
|
135
|
+
export declare const METAMASK_STALELIST_FILE = "/v1/stalelist";
|
|
136
|
+
export declare const METAMASK_HOTLIST_DIFF_FILE = "/v1/diffsSince";
|
|
137
|
+
export declare const HOTLIST_REFRESH_INTERVAL: number;
|
|
138
|
+
export declare const STALELIST_REFRESH_INTERVAL: number;
|
|
139
|
+
export declare const METAMASK_STALELIST_URL: string;
|
|
140
|
+
export declare const METAMASK_HOTLIST_DIFF_URL: string;
|
|
141
|
+
/**
|
|
142
|
+
* Enum containing upstream data provider source list keys.
|
|
143
|
+
* These are the keys denoting lists consumed by the upstream data provider.
|
|
144
|
+
*/
|
|
145
|
+
export declare enum ListKeys {
|
|
146
|
+
PhishfortHotlist = "phishfort_hotlist",
|
|
147
|
+
EthPhishingDetectConfig = "eth_phishing_detect_config"
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Enum containing downstream client attribution names.
|
|
151
|
+
*/
|
|
152
|
+
export declare enum ListNames {
|
|
153
|
+
MetaMask = "MetaMask",
|
|
154
|
+
Phishfort = "Phishfort"
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Maps from list key sourced from upstream data
|
|
158
|
+
* provider to downstream client attribution name.
|
|
159
|
+
*/
|
|
160
|
+
export declare const phishingListKeyNameMap: {
|
|
161
|
+
eth_phishing_detect_config: ListNames;
|
|
162
|
+
phishfort_hotlist: ListNames;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Controller that manages community-maintained lists of approved and unapproved website origins.
|
|
166
|
+
*/
|
|
167
|
+
export declare class PhishingController extends BaseController<PhishingConfig, PhishingState> {
|
|
168
|
+
#private;
|
|
169
|
+
private detector;
|
|
170
|
+
/**
|
|
171
|
+
* Name of this controller used during composition
|
|
172
|
+
*/
|
|
173
|
+
name: string;
|
|
174
|
+
/**
|
|
175
|
+
* Creates a PhishingController instance.
|
|
176
|
+
*
|
|
177
|
+
* @param config - Initial options used to configure this controller.
|
|
178
|
+
* @param state - Initial state to set on this controller.
|
|
179
|
+
*/
|
|
180
|
+
constructor(config?: Partial<PhishingConfig>, state?: Partial<PhishingState>);
|
|
181
|
+
/**
|
|
182
|
+
* Updates this.detector with an instance of PhishingDetector using the current state.
|
|
183
|
+
*/
|
|
184
|
+
updatePhishingDetector(): void;
|
|
185
|
+
/**
|
|
186
|
+
* Set the interval at which the stale phishing list will be refetched.
|
|
187
|
+
* Fetching will only occur on the next call to test/bypass.
|
|
188
|
+
* For immediate update to the phishing list, call {@link updateStalelist} directly.
|
|
189
|
+
*
|
|
190
|
+
* @param interval - the new interval, in ms.
|
|
191
|
+
*/
|
|
192
|
+
setStalelistRefreshInterval(interval: number): void;
|
|
193
|
+
/**
|
|
194
|
+
* Set the interval at which the hot list will be refetched.
|
|
195
|
+
* Fetching will only occur on the next call to test/bypass.
|
|
196
|
+
* For immediate update to the phishing list, call {@link updateHotlist} directly.
|
|
197
|
+
*
|
|
198
|
+
* @param interval - the new interval, in ms.
|
|
199
|
+
*/
|
|
200
|
+
setHotlistRefreshInterval(interval: number): void;
|
|
201
|
+
/**
|
|
202
|
+
* Determine if an update to the stalelist configuration is needed.
|
|
203
|
+
*
|
|
204
|
+
* @returns Whether an update is needed
|
|
205
|
+
*/
|
|
206
|
+
isStalelistOutOfDate(): boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Determine if an update to the hotlist configuration is needed.
|
|
209
|
+
*
|
|
210
|
+
* @returns Whether an update is needed
|
|
211
|
+
*/
|
|
212
|
+
isHotlistOutOfDate(): boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Conditionally update the phishing configuration.
|
|
215
|
+
*
|
|
216
|
+
* If the stalelist configuration is out of date, this function will call `updateStalelist`
|
|
217
|
+
* to update the configuration. This will automatically grab the hotlist,
|
|
218
|
+
* so it isn't necessary to continue on to download the hotlist.
|
|
219
|
+
*
|
|
220
|
+
*/
|
|
221
|
+
maybeUpdateState(): Promise<void>;
|
|
222
|
+
/**
|
|
223
|
+
* Determines if a given origin is unapproved.
|
|
224
|
+
*
|
|
225
|
+
* It is strongly recommended that you call {@link maybeUpdateState} before calling this,
|
|
226
|
+
* to check whether the phishing configuration is up-to-date. It will be updated if necessary
|
|
227
|
+
* by calling {@link updateStalelist} or {@link updateHotlist}.
|
|
228
|
+
*
|
|
229
|
+
* @param origin - Domain origin of a website.
|
|
230
|
+
* @returns Whether the origin is an unapproved origin.
|
|
231
|
+
*/
|
|
232
|
+
test(origin: string): EthPhishingDetectResult;
|
|
233
|
+
/**
|
|
234
|
+
* Temporarily marks a given origin as approved.
|
|
235
|
+
*
|
|
236
|
+
* @param origin - The origin to mark as approved.
|
|
237
|
+
*/
|
|
238
|
+
bypass(origin: string): void;
|
|
239
|
+
/**
|
|
240
|
+
* Update the hotlist.
|
|
241
|
+
*
|
|
242
|
+
* If an update is in progress, no additional update will be made. Instead this will wait until
|
|
243
|
+
* the in-progress update has finished.
|
|
244
|
+
*/
|
|
245
|
+
updateHotlist(): Promise<void>;
|
|
246
|
+
/**
|
|
247
|
+
* Update the stalelist.
|
|
248
|
+
*
|
|
249
|
+
* If an update is in progress, no additional update will be made. Instead this will wait until
|
|
250
|
+
* the in-progress update has finished.
|
|
251
|
+
*/
|
|
252
|
+
updateStalelist(): Promise<void>;
|
|
253
|
+
private queryConfig;
|
|
254
|
+
}
|
|
255
|
+
export default PhishingController;
|
|
256
|
+
//# sourceMappingURL=PhishingController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhishingController.d.ts","sourceRoot":"","sources":["../src/PhishingController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
12
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
13
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
14
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
15
|
+
};
|
|
16
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
17
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
18
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
19
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
20
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
21
|
+
};
|
|
22
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
23
|
+
var t = {};
|
|
24
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
25
|
+
t[p] = s[p];
|
|
26
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
27
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
28
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
29
|
+
t[p[i]] = s[p[i]];
|
|
30
|
+
}
|
|
31
|
+
return t;
|
|
32
|
+
};
|
|
33
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
34
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
35
|
+
};
|
|
36
|
+
var _PhishingController_instances, _PhishingController_inProgressHotlistUpdate, _PhishingController_inProgressStalelistUpdate, _PhishingController_updateStalelist, _PhishingController_updateHotlist;
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.PhishingController = exports.phishingListKeyNameMap = exports.ListNames = exports.ListKeys = exports.METAMASK_HOTLIST_DIFF_URL = exports.METAMASK_STALELIST_URL = exports.STALELIST_REFRESH_INTERVAL = exports.HOTLIST_REFRESH_INTERVAL = exports.METAMASK_HOTLIST_DIFF_FILE = exports.METAMASK_STALELIST_FILE = exports.PHISHING_CONFIG_BASE_URL = void 0;
|
|
39
|
+
const base_controller_1 = require("@metamask/base-controller");
|
|
40
|
+
const controller_utils_1 = require("@metamask/controller-utils");
|
|
41
|
+
const detector_1 = __importDefault(require("eth-phishing-detect/src/detector"));
|
|
42
|
+
const punycode_1 = require("punycode/");
|
|
43
|
+
const utils_1 = require("./utils");
|
|
44
|
+
exports.PHISHING_CONFIG_BASE_URL = 'https://phishing-detection.metafi.codefi.network';
|
|
45
|
+
exports.METAMASK_STALELIST_FILE = '/v1/stalelist';
|
|
46
|
+
exports.METAMASK_HOTLIST_DIFF_FILE = '/v1/diffsSince';
|
|
47
|
+
exports.HOTLIST_REFRESH_INTERVAL = 30 * 60; // 30 mins in seconds
|
|
48
|
+
exports.STALELIST_REFRESH_INTERVAL = 4 * 24 * 60 * 60; // 4 days in seconds
|
|
49
|
+
exports.METAMASK_STALELIST_URL = `${exports.PHISHING_CONFIG_BASE_URL}${exports.METAMASK_STALELIST_FILE}`;
|
|
50
|
+
exports.METAMASK_HOTLIST_DIFF_URL = `${exports.PHISHING_CONFIG_BASE_URL}${exports.METAMASK_HOTLIST_DIFF_FILE}`;
|
|
51
|
+
/**
|
|
52
|
+
* Enum containing upstream data provider source list keys.
|
|
53
|
+
* These are the keys denoting lists consumed by the upstream data provider.
|
|
54
|
+
*/
|
|
55
|
+
var ListKeys;
|
|
56
|
+
(function (ListKeys) {
|
|
57
|
+
ListKeys["PhishfortHotlist"] = "phishfort_hotlist";
|
|
58
|
+
ListKeys["EthPhishingDetectConfig"] = "eth_phishing_detect_config";
|
|
59
|
+
})(ListKeys = exports.ListKeys || (exports.ListKeys = {}));
|
|
60
|
+
/**
|
|
61
|
+
* Enum containing downstream client attribution names.
|
|
62
|
+
*/
|
|
63
|
+
var ListNames;
|
|
64
|
+
(function (ListNames) {
|
|
65
|
+
ListNames["MetaMask"] = "MetaMask";
|
|
66
|
+
ListNames["Phishfort"] = "Phishfort";
|
|
67
|
+
})(ListNames = exports.ListNames || (exports.ListNames = {}));
|
|
68
|
+
/**
|
|
69
|
+
* Maps from downstream client attribution name
|
|
70
|
+
* to list key sourced from upstream data provider.
|
|
71
|
+
*/
|
|
72
|
+
const phishingListNameKeyMap = {
|
|
73
|
+
[ListNames.Phishfort]: ListKeys.PhishfortHotlist,
|
|
74
|
+
[ListNames.MetaMask]: ListKeys.EthPhishingDetectConfig,
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Maps from list key sourced from upstream data
|
|
78
|
+
* provider to downstream client attribution name.
|
|
79
|
+
*/
|
|
80
|
+
exports.phishingListKeyNameMap = {
|
|
81
|
+
[ListKeys.EthPhishingDetectConfig]: ListNames.MetaMask,
|
|
82
|
+
[ListKeys.PhishfortHotlist]: ListNames.Phishfort,
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Controller that manages community-maintained lists of approved and unapproved website origins.
|
|
86
|
+
*/
|
|
87
|
+
class PhishingController extends base_controller_1.BaseController {
|
|
88
|
+
/**
|
|
89
|
+
* Creates a PhishingController instance.
|
|
90
|
+
*
|
|
91
|
+
* @param config - Initial options used to configure this controller.
|
|
92
|
+
* @param state - Initial state to set on this controller.
|
|
93
|
+
*/
|
|
94
|
+
constructor(config, state) {
|
|
95
|
+
super(config, state);
|
|
96
|
+
_PhishingController_instances.add(this);
|
|
97
|
+
_PhishingController_inProgressHotlistUpdate.set(this, void 0);
|
|
98
|
+
_PhishingController_inProgressStalelistUpdate.set(this, void 0);
|
|
99
|
+
/**
|
|
100
|
+
* Name of this controller used during composition
|
|
101
|
+
*/
|
|
102
|
+
this.name = 'PhishingController';
|
|
103
|
+
this.defaultConfig = {
|
|
104
|
+
stalelistRefreshInterval: exports.STALELIST_REFRESH_INTERVAL,
|
|
105
|
+
hotlistRefreshInterval: exports.HOTLIST_REFRESH_INTERVAL,
|
|
106
|
+
};
|
|
107
|
+
this.defaultState = {
|
|
108
|
+
phishingLists: [],
|
|
109
|
+
whitelist: [],
|
|
110
|
+
hotlistLastFetched: 0,
|
|
111
|
+
stalelistLastFetched: 0,
|
|
112
|
+
};
|
|
113
|
+
this.initialize();
|
|
114
|
+
this.updatePhishingDetector();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Updates this.detector with an instance of PhishingDetector using the current state.
|
|
118
|
+
*/
|
|
119
|
+
updatePhishingDetector() {
|
|
120
|
+
this.detector = new detector_1.default(this.state.phishingLists);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Set the interval at which the stale phishing list will be refetched.
|
|
124
|
+
* Fetching will only occur on the next call to test/bypass.
|
|
125
|
+
* For immediate update to the phishing list, call {@link updateStalelist} directly.
|
|
126
|
+
*
|
|
127
|
+
* @param interval - the new interval, in ms.
|
|
128
|
+
*/
|
|
129
|
+
setStalelistRefreshInterval(interval) {
|
|
130
|
+
this.configure({ stalelistRefreshInterval: interval }, false, false);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Set the interval at which the hot list will be refetched.
|
|
134
|
+
* Fetching will only occur on the next call to test/bypass.
|
|
135
|
+
* For immediate update to the phishing list, call {@link updateHotlist} directly.
|
|
136
|
+
*
|
|
137
|
+
* @param interval - the new interval, in ms.
|
|
138
|
+
*/
|
|
139
|
+
setHotlistRefreshInterval(interval) {
|
|
140
|
+
this.configure({ hotlistRefreshInterval: interval }, false, false);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Determine if an update to the stalelist configuration is needed.
|
|
144
|
+
*
|
|
145
|
+
* @returns Whether an update is needed
|
|
146
|
+
*/
|
|
147
|
+
isStalelistOutOfDate() {
|
|
148
|
+
return ((0, utils_1.fetchTimeNow)() - this.state.stalelistLastFetched >=
|
|
149
|
+
this.config.stalelistRefreshInterval);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Determine if an update to the hotlist configuration is needed.
|
|
153
|
+
*
|
|
154
|
+
* @returns Whether an update is needed
|
|
155
|
+
*/
|
|
156
|
+
isHotlistOutOfDate() {
|
|
157
|
+
return ((0, utils_1.fetchTimeNow)() - this.state.hotlistLastFetched >=
|
|
158
|
+
this.config.hotlistRefreshInterval);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Conditionally update the phishing configuration.
|
|
162
|
+
*
|
|
163
|
+
* If the stalelist configuration is out of date, this function will call `updateStalelist`
|
|
164
|
+
* to update the configuration. This will automatically grab the hotlist,
|
|
165
|
+
* so it isn't necessary to continue on to download the hotlist.
|
|
166
|
+
*
|
|
167
|
+
*/
|
|
168
|
+
maybeUpdateState() {
|
|
169
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
const staleListOutOfDate = this.isStalelistOutOfDate();
|
|
171
|
+
if (staleListOutOfDate) {
|
|
172
|
+
yield this.updateStalelist();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const hotlistOutOfDate = this.isHotlistOutOfDate();
|
|
176
|
+
if (hotlistOutOfDate) {
|
|
177
|
+
yield this.updateHotlist();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Determines if a given origin is unapproved.
|
|
183
|
+
*
|
|
184
|
+
* It is strongly recommended that you call {@link maybeUpdateState} before calling this,
|
|
185
|
+
* to check whether the phishing configuration is up-to-date. It will be updated if necessary
|
|
186
|
+
* by calling {@link updateStalelist} or {@link updateHotlist}.
|
|
187
|
+
*
|
|
188
|
+
* @param origin - Domain origin of a website.
|
|
189
|
+
* @returns Whether the origin is an unapproved origin.
|
|
190
|
+
*/
|
|
191
|
+
test(origin) {
|
|
192
|
+
const punycodeOrigin = (0, punycode_1.toASCII)(origin);
|
|
193
|
+
if (this.state.whitelist.includes(punycodeOrigin)) {
|
|
194
|
+
return { result: false, type: 'all' }; // Same as whitelisted match returned by detector.check(...).
|
|
195
|
+
}
|
|
196
|
+
return this.detector.check(punycodeOrigin);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Temporarily marks a given origin as approved.
|
|
200
|
+
*
|
|
201
|
+
* @param origin - The origin to mark as approved.
|
|
202
|
+
*/
|
|
203
|
+
bypass(origin) {
|
|
204
|
+
const punycodeOrigin = (0, punycode_1.toASCII)(origin);
|
|
205
|
+
const { whitelist } = this.state;
|
|
206
|
+
if (whitelist.includes(punycodeOrigin)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.update({ whitelist: [...whitelist, punycodeOrigin] });
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Update the hotlist.
|
|
213
|
+
*
|
|
214
|
+
* If an update is in progress, no additional update will be made. Instead this will wait until
|
|
215
|
+
* the in-progress update has finished.
|
|
216
|
+
*/
|
|
217
|
+
updateHotlist() {
|
|
218
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
219
|
+
if (__classPrivateFieldGet(this, _PhishingController_inProgressHotlistUpdate, "f")) {
|
|
220
|
+
yield __classPrivateFieldGet(this, _PhishingController_inProgressHotlistUpdate, "f");
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
__classPrivateFieldSet(this, _PhishingController_inProgressHotlistUpdate, __classPrivateFieldGet(this, _PhishingController_instances, "m", _PhishingController_updateHotlist).call(this), "f");
|
|
225
|
+
yield __classPrivateFieldGet(this, _PhishingController_inProgressHotlistUpdate, "f");
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
__classPrivateFieldSet(this, _PhishingController_inProgressHotlistUpdate, undefined, "f");
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Update the stalelist.
|
|
234
|
+
*
|
|
235
|
+
* If an update is in progress, no additional update will be made. Instead this will wait until
|
|
236
|
+
* the in-progress update has finished.
|
|
237
|
+
*/
|
|
238
|
+
updateStalelist() {
|
|
239
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
240
|
+
if (__classPrivateFieldGet(this, _PhishingController_inProgressStalelistUpdate, "f")) {
|
|
241
|
+
yield __classPrivateFieldGet(this, _PhishingController_inProgressStalelistUpdate, "f");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
__classPrivateFieldSet(this, _PhishingController_inProgressStalelistUpdate, __classPrivateFieldGet(this, _PhishingController_instances, "m", _PhishingController_updateStalelist).call(this), "f");
|
|
246
|
+
yield __classPrivateFieldGet(this, _PhishingController_inProgressStalelistUpdate, "f");
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
__classPrivateFieldSet(this, _PhishingController_inProgressStalelistUpdate, undefined, "f");
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
queryConfig(input) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const response = yield (0, controller_utils_1.safelyExecute)(() => fetch(input, { cache: 'no-cache' }), true);
|
|
256
|
+
switch (response === null || response === void 0 ? void 0 : response.status) {
|
|
257
|
+
case 200: {
|
|
258
|
+
return yield response.json();
|
|
259
|
+
}
|
|
260
|
+
default: {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.PhishingController = PhishingController;
|
|
268
|
+
_PhishingController_inProgressHotlistUpdate = new WeakMap(), _PhishingController_inProgressStalelistUpdate = new WeakMap(), _PhishingController_instances = new WeakSet(), _PhishingController_updateStalelist = function _PhishingController_updateStalelist() {
|
|
269
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
270
|
+
if (this.disabled) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
let stalelistResponse;
|
|
274
|
+
let hotlistDiffsResponse;
|
|
275
|
+
try {
|
|
276
|
+
stalelistResponse = yield this.queryConfig(exports.METAMASK_STALELIST_URL).then((d) => d);
|
|
277
|
+
// Fetching hotlist diffs relies on having a lastUpdated timestamp to do `GET /v1/diffsSince/:timestamp`,
|
|
278
|
+
// so it doesn't make sense to call if there is not a timestamp to begin with.
|
|
279
|
+
if ((stalelistResponse === null || stalelistResponse === void 0 ? void 0 : stalelistResponse.data) && stalelistResponse.data.lastUpdated > 0) {
|
|
280
|
+
hotlistDiffsResponse = yield this.queryConfig(`${exports.METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
// Set `stalelistLastFetched` and `hotlistLastFetched` even for failed requests to prevent server
|
|
285
|
+
// from being overwhelmed with traffic after a network disruption.
|
|
286
|
+
const timeNow = (0, utils_1.fetchTimeNow)();
|
|
287
|
+
this.update({
|
|
288
|
+
stalelistLastFetched: timeNow,
|
|
289
|
+
hotlistLastFetched: timeNow,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (!stalelistResponse || !hotlistDiffsResponse) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const _a = stalelistResponse.data, { phishfort_hotlist, eth_phishing_detect_config } = _a, partialState = __rest(_a, ["phishfort_hotlist", "eth_phishing_detect_config"]);
|
|
296
|
+
const phishfortListState = Object.assign(Object.assign(Object.assign({}, phishfort_hotlist), partialState), { fuzzylist: [], allowlist: [], name: exports.phishingListKeyNameMap.phishfort_hotlist });
|
|
297
|
+
const metamaskListState = Object.assign(Object.assign(Object.assign({}, eth_phishing_detect_config), partialState), { name: exports.phishingListKeyNameMap.eth_phishing_detect_config });
|
|
298
|
+
// Correctly shaping eth-phishing-detect state by applying hotlist diffs to the stalelist.
|
|
299
|
+
const newPhishfortListState = (0, utils_1.applyDiffs)(phishfortListState, hotlistDiffsResponse.data, ListKeys.PhishfortHotlist);
|
|
300
|
+
const newMetaMaskListState = (0, utils_1.applyDiffs)(metamaskListState, hotlistDiffsResponse.data, ListKeys.EthPhishingDetectConfig);
|
|
301
|
+
this.update({
|
|
302
|
+
phishingLists: [newMetaMaskListState, newPhishfortListState],
|
|
303
|
+
});
|
|
304
|
+
this.updatePhishingDetector();
|
|
305
|
+
});
|
|
306
|
+
}, _PhishingController_updateHotlist = function _PhishingController_updateHotlist() {
|
|
307
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
308
|
+
if (this.disabled) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const lastDiffTimestamp = Math.max(...this.state.phishingLists.map(({ lastUpdated }) => lastUpdated));
|
|
312
|
+
let hotlistResponse;
|
|
313
|
+
try {
|
|
314
|
+
hotlistResponse = yield this.queryConfig(`${exports.METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`);
|
|
315
|
+
}
|
|
316
|
+
finally {
|
|
317
|
+
// Set `hotlistLastFetched` even for failed requests to prevent server from being overwhelmed with
|
|
318
|
+
// traffic after a network disruption.
|
|
319
|
+
this.update({
|
|
320
|
+
hotlistLastFetched: (0, utils_1.fetchTimeNow)(),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (!(hotlistResponse === null || hotlistResponse === void 0 ? void 0 : hotlistResponse.data)) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const hotlist = hotlistResponse.data;
|
|
327
|
+
const newPhishingLists = this.state.phishingLists.map((phishingList) => (0, utils_1.applyDiffs)(phishingList, hotlist, phishingListNameKeyMap[phishingList.name]));
|
|
328
|
+
this.update({
|
|
329
|
+
phishingLists: newPhishingLists,
|
|
330
|
+
});
|
|
331
|
+
this.updatePhishingDetector();
|
|
332
|
+
});
|
|
333
|
+
};
|
|
334
|
+
exports.default = PhishingController;
|
|
335
|
+
//# sourceMappingURL=PhishingController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhishingController.js","sourceRoot":"","sources":["../src/PhishingController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport PhishingDetector from 'eth-phishing-detect/src/detector';\nimport { toASCII } from 'punycode/';\n\nimport { applyDiffs, fetchTimeNow } from './utils';\n\n/**\n * @type ListTypes\n *\n * Type outlining the types of lists provided by aggregating different source lists\n */\nexport type ListTypes = 'fuzzylist' | 'blocklist' | 'allowlist';\n\n/**\n * @type EthPhishingResponse\n *\n * Configuration response from the eth-phishing-detect package\n * consisting of approved and unapproved website origins\n * @property blacklist - List of unapproved origins\n * @property fuzzylist - List of fuzzy-matched unapproved origins\n * @property tolerance - Fuzzy match tolerance level\n * @property version - Version number of this configuration\n * @property whitelist - List of approved origins\n */\nexport interface EthPhishingResponse {\n blacklist: string[];\n fuzzylist: string[];\n tolerance: number;\n version: number;\n whitelist: string[];\n}\n\n/**\n * @type PhishingStalelist\n *\n * Interface defining expected type of the stalelist.json file.\n * @property eth_phishing_detect_config - Stale list sourced from eth-phishing-detect's config.json.\n * @property phishfort_hotlist - Stale list sourced from phishfort's hotlist.json. Only includes blocklist. Deduplicated entries from eth_phishing_detect_config.\n * @property tolerance - Fuzzy match tolerance level\n * @property lastUpdated - Timestamp of last update.\n * @property version - Stalelist data structure iteration.\n */\nexport interface PhishingStalelist {\n eth_phishing_detect_config: Record<ListTypes, string[]>;\n phishfort_hotlist: Record<ListTypes, string[]>;\n tolerance: number;\n version: number;\n lastUpdated: number;\n}\n\n/**\n * @type PhishingListState\n *\n * Interface defining the persisted list state. This is the persisted state that is updated frequently with `this.maybeUpdateState()`.\n * @property allowlist - List of approved origins (legacy naming \"whitelist\")\n * @property blocklist - List of unapproved origins (legacy naming \"blacklist\")\n * @property fuzzylist - List of fuzzy-matched unapproved origins\n * @property tolerance - Fuzzy match tolerance level\n * @property lastUpdated - Timestamp of last update.\n * @property version - Version of the phishing list state.\n * @property name - Name of the list. Used for attribution.\n */\nexport interface PhishingListState {\n allowlist: string[];\n blocklist: string[];\n fuzzylist: string[];\n tolerance: number;\n version: number;\n lastUpdated: number;\n name: ListNames;\n}\n\n/**\n * @type EthPhishingDetectResult\n *\n * Interface that describes the result of the `test` method.\n * @property name - Name of the config on which a match was found.\n * @property version - Version of the config on which a match was found.\n * @property result - Whether a domain was detected as a phishing domain. True means an unsafe domain.\n * @property match - The matching fuzzylist origin when a fuzzylist match is found. Returned as undefined for non-fuzzy true results.\n * @property type - The field of the config on which a match was found.\n */\nexport interface EthPhishingDetectResult {\n name?: string;\n version?: string;\n result: boolean;\n match?: string; // Returned as undefined for non-fuzzy true results.\n type: 'all' | 'fuzzy' | 'blocklist' | 'allowlist';\n}\n\n/**\n * @type HotlistDiff\n *\n * Interface defining the expected type of the diffs in hotlist.json file.\n * @property url - Url of the diff entry.\n * @property timestamp - Timestamp at which the diff was identified.\n * @property targetList - The list name where the diff was identified.\n * @property isRemoval - Was the diff identified a removal type.\n */\nexport interface HotlistDiff {\n url: string;\n timestamp: number;\n targetList: `${ListKeys}.${ListTypes}`;\n isRemoval?: boolean;\n}\n\nexport interface DataResultWrapper<T> {\n data: T;\n}\n\n/**\n * @type Hotlist\n *\n * Type defining expected hotlist.json file.\n * @property url - Url of the diff entry.\n * @property timestamp - Timestamp at which the diff was identified.\n * @property targetList - The list name where the diff was identified.\n * @property isRemoval - Was the diff identified a removal type.\n */\nexport type Hotlist = HotlistDiff[];\n\n/**\n * @type PhishingConfig\n *\n * Phishing controller configuration\n * @property stalelistRefreshInterval - Polling interval used to fetch stale list.\n * @property hotlistRefreshInterval - Polling interval used to fetch hotlist diff list.\n */\nexport interface PhishingConfig extends BaseConfig {\n stalelistRefreshInterval: number;\n hotlistRefreshInterval: number;\n}\n\n/**\n * @type PhishingState\n *\n * Phishing controller state\n * @property phishing - eth-phishing-detect configuration\n * @property whitelist - array of temporarily-approved origins\n */\nexport interface PhishingState extends BaseState {\n phishingLists: PhishingListState[];\n whitelist: string[];\n hotlistLastFetched: number;\n stalelistLastFetched: number;\n}\n\nexport const PHISHING_CONFIG_BASE_URL =\n 'https://phishing-detection.metafi.codefi.network';\n\nexport const METAMASK_STALELIST_FILE = '/v1/stalelist';\n\nexport const METAMASK_HOTLIST_DIFF_FILE = '/v1/diffsSince';\n\nexport const HOTLIST_REFRESH_INTERVAL = 30 * 60; // 30 mins in seconds\nexport const STALELIST_REFRESH_INTERVAL = 4 * 24 * 60 * 60; // 4 days in seconds\n\nexport const METAMASK_STALELIST_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_STALELIST_FILE}`;\nexport const METAMASK_HOTLIST_DIFF_URL = `${PHISHING_CONFIG_BASE_URL}${METAMASK_HOTLIST_DIFF_FILE}`;\n\n/**\n * Enum containing upstream data provider source list keys.\n * These are the keys denoting lists consumed by the upstream data provider.\n */\nexport enum ListKeys {\n PhishfortHotlist = 'phishfort_hotlist',\n EthPhishingDetectConfig = 'eth_phishing_detect_config',\n}\n\n/**\n * Enum containing downstream client attribution names.\n */\nexport enum ListNames {\n MetaMask = 'MetaMask',\n Phishfort = 'Phishfort',\n}\n\n/**\n * Maps from downstream client attribution name\n * to list key sourced from upstream data provider.\n */\nconst phishingListNameKeyMap = {\n [ListNames.Phishfort]: ListKeys.PhishfortHotlist,\n [ListNames.MetaMask]: ListKeys.EthPhishingDetectConfig,\n};\n\n/**\n * Maps from list key sourced from upstream data\n * provider to downstream client attribution name.\n */\nexport const phishingListKeyNameMap = {\n [ListKeys.EthPhishingDetectConfig]: ListNames.MetaMask,\n [ListKeys.PhishfortHotlist]: ListNames.Phishfort,\n};\n\n/**\n * Controller that manages community-maintained lists of approved and unapproved website origins.\n */\nexport class PhishingController extends BaseController<\n PhishingConfig,\n PhishingState\n> {\n private detector: any;\n\n #inProgressHotlistUpdate: Promise<void> | undefined;\n\n #inProgressStalelistUpdate: Promise<void> | undefined;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'PhishingController';\n\n /**\n * Creates a PhishingController instance.\n *\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n config?: Partial<PhishingConfig>,\n state?: Partial<PhishingState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n stalelistRefreshInterval: STALELIST_REFRESH_INTERVAL,\n hotlistRefreshInterval: HOTLIST_REFRESH_INTERVAL,\n };\n\n this.defaultState = {\n phishingLists: [],\n whitelist: [],\n hotlistLastFetched: 0,\n stalelistLastFetched: 0,\n };\n\n this.initialize();\n this.updatePhishingDetector();\n }\n\n /**\n * Updates this.detector with an instance of PhishingDetector using the current state.\n */\n updatePhishingDetector() {\n this.detector = new PhishingDetector(this.state.phishingLists);\n }\n\n /**\n * Set the interval at which the stale phishing list will be refetched.\n * Fetching will only occur on the next call to test/bypass.\n * For immediate update to the phishing list, call {@link updateStalelist} directly.\n *\n * @param interval - the new interval, in ms.\n */\n setStalelistRefreshInterval(interval: number) {\n this.configure({ stalelistRefreshInterval: interval }, false, false);\n }\n\n /**\n * Set the interval at which the hot list will be refetched.\n * Fetching will only occur on the next call to test/bypass.\n * For immediate update to the phishing list, call {@link updateHotlist} directly.\n *\n * @param interval - the new interval, in ms.\n */\n setHotlistRefreshInterval(interval: number) {\n this.configure({ hotlistRefreshInterval: interval }, false, false);\n }\n\n /**\n * Determine if an update to the stalelist configuration is needed.\n *\n * @returns Whether an update is needed\n */\n isStalelistOutOfDate() {\n return (\n fetchTimeNow() - this.state.stalelistLastFetched >=\n this.config.stalelistRefreshInterval\n );\n }\n\n /**\n * Determine if an update to the hotlist configuration is needed.\n *\n * @returns Whether an update is needed\n */\n isHotlistOutOfDate() {\n return (\n fetchTimeNow() - this.state.hotlistLastFetched >=\n this.config.hotlistRefreshInterval\n );\n }\n\n /**\n * Conditionally update the phishing configuration.\n *\n * If the stalelist configuration is out of date, this function will call `updateStalelist`\n * to update the configuration. This will automatically grab the hotlist,\n * so it isn't necessary to continue on to download the hotlist.\n *\n */\n async maybeUpdateState() {\n const staleListOutOfDate = this.isStalelistOutOfDate();\n if (staleListOutOfDate) {\n await this.updateStalelist();\n return;\n }\n const hotlistOutOfDate = this.isHotlistOutOfDate();\n if (hotlistOutOfDate) {\n await this.updateHotlist();\n }\n }\n\n /**\n * Determines if a given origin is unapproved.\n *\n * It is strongly recommended that you call {@link maybeUpdateState} before calling this,\n * to check whether the phishing configuration is up-to-date. It will be updated if necessary\n * by calling {@link updateStalelist} or {@link updateHotlist}.\n *\n * @param origin - Domain origin of a website.\n * @returns Whether the origin is an unapproved origin.\n */\n test(origin: string): EthPhishingDetectResult {\n const punycodeOrigin = toASCII(origin);\n if (this.state.whitelist.includes(punycodeOrigin)) {\n return { result: false, type: 'all' }; // Same as whitelisted match returned by detector.check(...).\n }\n return this.detector.check(punycodeOrigin);\n }\n\n /**\n * Temporarily marks a given origin as approved.\n *\n * @param origin - The origin to mark as approved.\n */\n bypass(origin: string) {\n const punycodeOrigin = toASCII(origin);\n const { whitelist } = this.state;\n if (whitelist.includes(punycodeOrigin)) {\n return;\n }\n this.update({ whitelist: [...whitelist, punycodeOrigin] });\n }\n\n /**\n * Update the hotlist.\n *\n * If an update is in progress, no additional update will be made. Instead this will wait until\n * the in-progress update has finished.\n */\n async updateHotlist() {\n if (this.#inProgressHotlistUpdate) {\n await this.#inProgressHotlistUpdate;\n return;\n }\n\n try {\n this.#inProgressHotlistUpdate = this.#updateHotlist();\n await this.#inProgressHotlistUpdate;\n } finally {\n this.#inProgressHotlistUpdate = undefined;\n }\n }\n\n /**\n * Update the stalelist.\n *\n * If an update is in progress, no additional update will be made. Instead this will wait until\n * the in-progress update has finished.\n */\n async updateStalelist() {\n if (this.#inProgressStalelistUpdate) {\n await this.#inProgressStalelistUpdate;\n return;\n }\n\n try {\n this.#inProgressStalelistUpdate = this.#updateStalelist();\n await this.#inProgressStalelistUpdate;\n } finally {\n this.#inProgressStalelistUpdate = undefined;\n }\n }\n\n /**\n * Update the stalelist configuration.\n *\n * This should only be called from the `updateStalelist` function, which is a wrapper around\n * this function that prevents redundant configuration updates.\n */\n async #updateStalelist() {\n if (this.disabled) {\n return;\n }\n\n let stalelistResponse;\n let hotlistDiffsResponse;\n try {\n stalelistResponse = await this.queryConfig<\n DataResultWrapper<PhishingStalelist>\n >(METAMASK_STALELIST_URL).then((d) => d);\n\n // Fetching hotlist diffs relies on having a lastUpdated timestamp to do `GET /v1/diffsSince/:timestamp`,\n // so it doesn't make sense to call if there is not a timestamp to begin with.\n if (stalelistResponse?.data && stalelistResponse.data.lastUpdated > 0) {\n hotlistDiffsResponse = await this.queryConfig<\n DataResultWrapper<Hotlist>\n >(`${METAMASK_HOTLIST_DIFF_URL}/${stalelistResponse.data.lastUpdated}`);\n }\n } finally {\n // Set `stalelistLastFetched` and `hotlistLastFetched` even for failed requests to prevent server\n // from being overwhelmed with traffic after a network disruption.\n const timeNow = fetchTimeNow();\n this.update({\n stalelistLastFetched: timeNow,\n hotlistLastFetched: timeNow,\n });\n }\n\n if (!stalelistResponse || !hotlistDiffsResponse) {\n return;\n }\n\n const { phishfort_hotlist, eth_phishing_detect_config, ...partialState } =\n stalelistResponse.data;\n\n const phishfortListState: PhishingListState = {\n ...phishfort_hotlist,\n ...partialState,\n fuzzylist: [], // Phishfort hotlist doesn't contain a fuzzylist\n allowlist: [], // Phishfort hotlist doesn't contain an allowlist\n name: phishingListKeyNameMap.phishfort_hotlist,\n };\n const metamaskListState: PhishingListState = {\n ...eth_phishing_detect_config,\n ...partialState,\n name: phishingListKeyNameMap.eth_phishing_detect_config,\n };\n // Correctly shaping eth-phishing-detect state by applying hotlist diffs to the stalelist.\n const newPhishfortListState: PhishingListState = applyDiffs(\n phishfortListState,\n hotlistDiffsResponse.data,\n ListKeys.PhishfortHotlist,\n );\n const newMetaMaskListState: PhishingListState = applyDiffs(\n metamaskListState,\n hotlistDiffsResponse.data,\n ListKeys.EthPhishingDetectConfig,\n );\n\n this.update({\n phishingLists: [newMetaMaskListState, newPhishfortListState],\n });\n this.updatePhishingDetector();\n }\n\n /**\n * Update the stalelist configuration.\n *\n * This should only be called from the `updateStalelist` function, which is a wrapper around\n * this function that prevents redundant configuration updates.\n */\n async #updateHotlist() {\n if (this.disabled) {\n return;\n }\n const lastDiffTimestamp = Math.max(\n ...this.state.phishingLists.map(({ lastUpdated }) => lastUpdated),\n );\n let hotlistResponse: DataResultWrapper<Hotlist> | null;\n\n try {\n hotlistResponse = await this.queryConfig<DataResultWrapper<Hotlist>>(\n `${METAMASK_HOTLIST_DIFF_URL}/${lastDiffTimestamp}`,\n );\n } finally {\n // Set `hotlistLastFetched` even for failed requests to prevent server from being overwhelmed with\n // traffic after a network disruption.\n this.update({\n hotlistLastFetched: fetchTimeNow(),\n });\n }\n\n if (!hotlistResponse?.data) {\n return;\n }\n const hotlist = hotlistResponse.data;\n const newPhishingLists = this.state.phishingLists.map((phishingList) =>\n applyDiffs(\n phishingList,\n hotlist,\n phishingListNameKeyMap[phishingList.name],\n ),\n );\n\n this.update({\n phishingLists: newPhishingLists,\n });\n this.updatePhishingDetector();\n }\n\n private async queryConfig<ResponseType>(\n input: RequestInfo,\n ): Promise<ResponseType | null> {\n const response = await safelyExecute(\n () => fetch(input, { cache: 'no-cache' }),\n true,\n );\n\n switch (response?.status) {\n case 200: {\n return await response.json();\n }\n\n default: {\n return null;\n }\n }\n }\n}\n\nexport default PhishingController;\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./PhishingController"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uDAAqC","sourcesContent":["export * from './PhishingController';\n"]}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Hotlist, ListKeys, PhishingListState } from './PhishingController';
|
|
2
|
+
/**
|
|
3
|
+
* Fetches current epoch time in seconds.
|
|
4
|
+
*
|
|
5
|
+
* @returns the Date.now() time in seconds instead of miliseconds. backend files rely on timestamps in seconds since epoch.
|
|
6
|
+
*/
|
|
7
|
+
export declare const fetchTimeNow: () => number;
|
|
8
|
+
/**
|
|
9
|
+
* Determines which diffs are applicable to the listState, then applies those diffs.
|
|
10
|
+
*
|
|
11
|
+
* @param listState - the stalelist or the existing liststate that diffs will be applied to.
|
|
12
|
+
* @param hotlistDiffs - the diffs to apply to the listState if valid.
|
|
13
|
+
* @param listKey - the key associated with the input/output phishing list state.
|
|
14
|
+
* @returns the new list state
|
|
15
|
+
*/
|
|
16
|
+
export declare const applyDiffs: (listState: PhishingListState, hotlistDiffs: Hotlist, listKey: ListKeys) => PhishingListState;
|
|
17
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EACR,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAO,MAAuC,CAAC;AAkBxE;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,cACV,iBAAiB,kCAEnB,QAAQ,KAChB,iBAyCF,CAAC"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyDiffs = exports.fetchTimeNow = void 0;
|
|
4
|
+
const PhishingController_1 = require("./PhishingController");
|
|
5
|
+
/**
|
|
6
|
+
* Fetches current epoch time in seconds.
|
|
7
|
+
*
|
|
8
|
+
* @returns the Date.now() time in seconds instead of miliseconds. backend files rely on timestamps in seconds since epoch.
|
|
9
|
+
*/
|
|
10
|
+
const fetchTimeNow = () => Math.round(Date.now() / 1000);
|
|
11
|
+
exports.fetchTimeNow = fetchTimeNow;
|
|
12
|
+
/**
|
|
13
|
+
* Split a string into two pieces, using the first period as the delimiter.
|
|
14
|
+
*
|
|
15
|
+
* @param stringToSplit - The string to split.
|
|
16
|
+
* @returns An array of length two containing the beginning and end of the string.
|
|
17
|
+
*/
|
|
18
|
+
const splitStringByPeriod = (stringToSplit) => {
|
|
19
|
+
const periodIndex = stringToSplit.indexOf('.');
|
|
20
|
+
return [
|
|
21
|
+
stringToSplit.slice(0, periodIndex),
|
|
22
|
+
stringToSplit.slice(periodIndex + 1),
|
|
23
|
+
];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Determines which diffs are applicable to the listState, then applies those diffs.
|
|
27
|
+
*
|
|
28
|
+
* @param listState - the stalelist or the existing liststate that diffs will be applied to.
|
|
29
|
+
* @param hotlistDiffs - the diffs to apply to the listState if valid.
|
|
30
|
+
* @param listKey - the key associated with the input/output phishing list state.
|
|
31
|
+
* @returns the new list state
|
|
32
|
+
*/
|
|
33
|
+
const applyDiffs = (listState, hotlistDiffs, listKey) => {
|
|
34
|
+
// filter to remove diffs that were added before the lastUpdate time.
|
|
35
|
+
// filter to remove diffs that aren't applicable to the specified list (by listKey).
|
|
36
|
+
const diffsToApply = hotlistDiffs.filter(({ timestamp, targetList }) => timestamp > listState.lastUpdated &&
|
|
37
|
+
splitStringByPeriod(targetList)[0] === listKey);
|
|
38
|
+
// the reason behind using latestDiffTimestamp as the lastUpdated time
|
|
39
|
+
// is so that we can benefit server-side from memoization due to end client's
|
|
40
|
+
// `GET /v1/diffSince/:timestamp` requests lining up with
|
|
41
|
+
// our periodic updates (which create diffs at specific timestamps).
|
|
42
|
+
let latestDiffTimestamp = listState.lastUpdated;
|
|
43
|
+
const listSets = {
|
|
44
|
+
allowlist: new Set(listState.allowlist),
|
|
45
|
+
blocklist: new Set(listState.blocklist),
|
|
46
|
+
fuzzylist: new Set(listState.fuzzylist),
|
|
47
|
+
};
|
|
48
|
+
for (const { isRemoval, targetList, url, timestamp } of diffsToApply) {
|
|
49
|
+
const targetListType = splitStringByPeriod(targetList)[1];
|
|
50
|
+
if (timestamp > latestDiffTimestamp) {
|
|
51
|
+
latestDiffTimestamp = timestamp;
|
|
52
|
+
}
|
|
53
|
+
if (isRemoval) {
|
|
54
|
+
listSets[targetListType].delete(url);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
listSets[targetListType].add(url);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
allowlist: Array.from(listSets.allowlist),
|
|
62
|
+
blocklist: Array.from(listSets.blocklist),
|
|
63
|
+
fuzzylist: Array.from(listSets.fuzzylist),
|
|
64
|
+
version: listState.version,
|
|
65
|
+
name: PhishingController_1.phishingListKeyNameMap[listKey],
|
|
66
|
+
tolerance: listState.tolerance,
|
|
67
|
+
lastUpdated: latestDiffTimestamp,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
exports.applyDiffs = applyDiffs;
|
|
71
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAKA,6DAA8D;AAC9D;;;;GAIG;AACI,MAAM,YAAY,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AAA3D,QAAA,YAAY,gBAA+C;AAExE;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,CAC1B,aAAgC,EAClB,EAAE;IAChB,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAO;QACL,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAU;QAC5C,aAAa,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAQ;KAC5C,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;GAOG;AACI,MAAM,UAAU,GAAG,CACxB,SAA4B,EAC5B,YAAqB,EACrB,OAAiB,EACE,EAAE;IACrB,qEAAqE;IACrE,oFAAoF;IACpF,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CACtC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,CAC5B,SAAS,GAAG,SAAS,CAAC,WAAW;QACjC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CACjD,CAAC;IAEF,sEAAsE;IACtE,6EAA6E;IAC7E,yDAAyD;IACzD,oEAAoE;IACpE,IAAI,mBAAmB,GAAG,SAAS,CAAC,WAAW,CAAC;IAEhD,MAAM,QAAQ,GAAG;QACf,SAAS,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC;QACvC,SAAS,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC;QACvC,SAAS,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC;KACxC,CAAC;IACF,KAAK,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,YAAY,EAAE;QACpE,MAAM,cAAc,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,IAAI,SAAS,GAAG,mBAAmB,EAAE;YACnC,mBAAmB,GAAG,SAAS,CAAC;SACjC;QACD,IAAI,SAAS,EAAE;YACb,QAAQ,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;SACtC;aAAM;YACL,QAAQ,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACnC;KACF;IAED,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QACzC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QACzC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QACzC,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,IAAI,EAAE,2CAAsB,CAAC,OAAO,CAAC;QACrC,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,WAAW,EAAE,mBAAmB;KACjC,CAAC;AACJ,CAAC,CAAC;AA7CW,QAAA,UAAU,cA6CrB","sourcesContent":["import type {\n Hotlist,\n ListKeys,\n PhishingListState,\n} from './PhishingController';\nimport { phishingListKeyNameMap } from './PhishingController';\n/**\n * Fetches current epoch time in seconds.\n *\n * @returns the Date.now() time in seconds instead of miliseconds. backend files rely on timestamps in seconds since epoch.\n */\nexport const fetchTimeNow = (): number => Math.round(Date.now() / 1000);\n\n/**\n * Split a string into two pieces, using the first period as the delimiter.\n *\n * @param stringToSplit - The string to split.\n * @returns An array of length two containing the beginning and end of the string.\n */\nconst splitStringByPeriod = <Start extends string, End extends string>(\n stringToSplit: `${Start}.${End}`,\n): [Start, End] => {\n const periodIndex = stringToSplit.indexOf('.');\n return [\n stringToSplit.slice(0, periodIndex) as Start,\n stringToSplit.slice(periodIndex + 1) as End,\n ];\n};\n\n/**\n * Determines which diffs are applicable to the listState, then applies those diffs.\n *\n * @param listState - the stalelist or the existing liststate that diffs will be applied to.\n * @param hotlistDiffs - the diffs to apply to the listState if valid.\n * @param listKey - the key associated with the input/output phishing list state.\n * @returns the new list state\n */\nexport const applyDiffs = (\n listState: PhishingListState,\n hotlistDiffs: Hotlist,\n listKey: ListKeys,\n): PhishingListState => {\n // filter to remove diffs that were added before the lastUpdate time.\n // filter to remove diffs that aren't applicable to the specified list (by listKey).\n const diffsToApply = hotlistDiffs.filter(\n ({ timestamp, targetList }) =>\n timestamp > listState.lastUpdated &&\n splitStringByPeriod(targetList)[0] === listKey,\n );\n\n // the reason behind using latestDiffTimestamp as the lastUpdated time\n // is so that we can benefit server-side from memoization due to end client's\n // `GET /v1/diffSince/:timestamp` requests lining up with\n // our periodic updates (which create diffs at specific timestamps).\n let latestDiffTimestamp = listState.lastUpdated;\n\n const listSets = {\n allowlist: new Set(listState.allowlist),\n blocklist: new Set(listState.blocklist),\n fuzzylist: new Set(listState.fuzzylist),\n };\n for (const { isRemoval, targetList, url, timestamp } of diffsToApply) {\n const targetListType = splitStringByPeriod(targetList)[1];\n if (timestamp > latestDiffTimestamp) {\n latestDiffTimestamp = timestamp;\n }\n if (isRemoval) {\n listSets[targetListType].delete(url);\n } else {\n listSets[targetListType].add(url);\n }\n }\n\n return {\n allowlist: Array.from(listSets.allowlist),\n blocklist: Array.from(listSets.blocklist),\n fuzzylist: Array.from(listSets.fuzzylist),\n version: listState.version,\n name: phishingListKeyNameMap[listKey],\n tolerance: listState.tolerance,\n lastUpdated: latestDiffTimestamp,\n };\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamask-previews/phishing-controller",
|
|
3
|
+
"version": "6.0.0-preview.d32a7cc",
|
|
4
|
+
"description": "Maintains a periodically updated list of approved and unapproved website origins",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"MetaMask",
|
|
7
|
+
"Ethereum"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/MetaMask/core/tree/main/packages/phishing-controller#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/MetaMask/core/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/MetaMask/core.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build:docs": "typedoc",
|
|
25
|
+
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/phishing-controller",
|
|
26
|
+
"publish:preview": "yarn npm publish --tag preview",
|
|
27
|
+
"test": "jest",
|
|
28
|
+
"test:watch": "jest --watch"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@metamask-previews/base-controller": "3.2.0-preview.d32a7cc",
|
|
32
|
+
"@metamask-previews/controller-utils": "4.3.1-preview.d32a7cc",
|
|
33
|
+
"@types/punycode": "^2.1.0",
|
|
34
|
+
"eth-phishing-detect": "^1.2.0",
|
|
35
|
+
"punycode": "^2.1.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@metamask/auto-changelog": "^3.1.0",
|
|
39
|
+
"@types/jest": "^27.4.1",
|
|
40
|
+
"deepmerge": "^4.2.2",
|
|
41
|
+
"jest": "^27.5.1",
|
|
42
|
+
"nock": "^13.3.1",
|
|
43
|
+
"sinon": "^9.2.4",
|
|
44
|
+
"ts-jest": "^27.1.4",
|
|
45
|
+
"typedoc": "^0.22.15",
|
|
46
|
+
"typedoc-plugin-missing-exports": "^0.22.6",
|
|
47
|
+
"typescript": "~4.6.3"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=16.0.0"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public",
|
|
54
|
+
"registry": "https://registry.npmjs.org/"
|
|
55
|
+
}
|
|
56
|
+
}
|