@keplr-wallet/background 0.10.23 → 0.10.24
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/build/index.d.ts +1 -0
- package/build/index.js +9 -0
- package/build/index.js.map +1 -1
- package/build/phishing-list/constants.d.ts +1 -0
- package/build/phishing-list/constants.js +5 -0
- package/build/phishing-list/constants.js.map +1 -0
- package/build/phishing-list/handler.d.ts +3 -0
- package/build/phishing-list/handler.js +19 -0
- package/build/phishing-list/handler.js.map +1 -0
- package/build/phishing-list/index.d.ts +2 -0
- package/build/phishing-list/index.js +15 -0
- package/build/phishing-list/index.js.map +1 -0
- package/build/phishing-list/init.d.ts +3 -0
- package/build/phishing-list/init.js +12 -0
- package/build/phishing-list/init.js.map +1 -0
- package/build/phishing-list/internal.d.ts +2 -0
- package/build/phishing-list/internal.js +15 -0
- package/build/phishing-list/internal.js.map +1 -0
- package/build/phishing-list/messages.d.ts +9 -0
- package/build/phishing-list/messages.js +30 -0
- package/build/phishing-list/messages.js.map +1 -0
- package/build/phishing-list/messages.spec.d.ts +1 -0
- package/build/phishing-list/messages.spec.js +102 -0
- package/build/phishing-list/messages.spec.js.map +1 -0
- package/build/phishing-list/service.d.ts +22 -0
- package/build/phishing-list/service.js +86 -0
- package/build/phishing-list/service.js.map +1 -0
- package/build/phishing-list/service.spec.d.ts +1 -0
- package/build/phishing-list/service.spec.js +302 -0
- package/build/phishing-list/service.spec.js.map +1 -0
- package/build/phishing-list/utils.d.ts +1 -0
- package/build/phishing-list/utils.js +27 -0
- package/build/phishing-list/utils.js.map +1 -0
- package/build/phishing-list/utils.spec.d.ts +1 -0
- package/build/phishing-list/utils.spec.js +142 -0
- package/build/phishing-list/utils.spec.js.map +1 -0
- package/package.json +11 -11
- package/src/index.ts +11 -0
- package/src/phishing-list/constants.ts +1 -0
- package/src/phishing-list/handler.ts +27 -0
- package/src/phishing-list/index.ts +2 -0
- package/src/phishing-list/init.ts +11 -0
- package/src/phishing-list/internal.ts +2 -0
- package/src/phishing-list/messages.spec.ts +104 -0
- package/src/phishing-list/messages.ts +32 -0
- package/src/phishing-list/service.spec.ts +367 -0
- package/src/phishing-list/service.ts +88 -0
- package/src/phishing-list/utils.spec.ts +144 -0
- package/src/phishing-list/utils.ts +27 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { CheckURLIsPhishingMsg } from "./messages";
|
|
2
|
+
|
|
3
|
+
describe("Test phishing list service messages", () => {
|
|
4
|
+
test("Test CheckURLIsPhishingMsg", () => {
|
|
5
|
+
const tests: {
|
|
6
|
+
url: string;
|
|
7
|
+
invalid?: boolean;
|
|
8
|
+
}[] = [
|
|
9
|
+
{
|
|
10
|
+
url: "",
|
|
11
|
+
invalid: true,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
url: ".",
|
|
15
|
+
invalid: true,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
url: "..",
|
|
19
|
+
invalid: true,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
url: ".test.",
|
|
23
|
+
invalid: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
url: "..test",
|
|
27
|
+
invalid: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
url: "test.com.",
|
|
31
|
+
invalid: true,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
url: "test.com..",
|
|
35
|
+
invalid: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
url: "asd.test.com.",
|
|
39
|
+
invalid: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
url: "asd..test.com.",
|
|
43
|
+
invalid: true,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
url: "http://",
|
|
47
|
+
invalid: true,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
url: "https://.",
|
|
51
|
+
invalid: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
url: "https://..",
|
|
55
|
+
invalid: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
url: "https://.test.",
|
|
59
|
+
invalid: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
url: "https://..test",
|
|
63
|
+
invalid: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
url: "https://test.com.",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
url: "https://test.com..",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
url: "https://.test.com.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
url: "https://..test.com..",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
url: "https://test..com",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
url: "https://..test..com..",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
url: "https://asd.test.com.",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
url: "https://asd..test.com.",
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
for (const test of tests) {
|
|
92
|
+
const msg = new CheckURLIsPhishingMsg();
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
94
|
+
// @ts-ignore
|
|
95
|
+
msg.origin = test.url;
|
|
96
|
+
|
|
97
|
+
if ("invalid" in test) {
|
|
98
|
+
expect(() => msg.validateBasic()).toThrow();
|
|
99
|
+
} else {
|
|
100
|
+
expect(() => msg.validateBasic()).not.toThrow();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Message } from "@keplr-wallet/router";
|
|
2
|
+
import { ROUTE } from "./constants";
|
|
3
|
+
import { parseDomainUntilSecondLevel } from "./utils";
|
|
4
|
+
|
|
5
|
+
export class CheckURLIsPhishingMsg extends Message<boolean> {
|
|
6
|
+
public static type() {
|
|
7
|
+
return "check-url-is-phishing";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
validateBasic(): void {
|
|
15
|
+
const url = new URL(this.origin);
|
|
16
|
+
|
|
17
|
+
// Will throw an error if url has not second level domain.
|
|
18
|
+
parseDomainUntilSecondLevel(url.origin);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
approveExternal(): boolean {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
route(): string {
|
|
26
|
+
return ROUTE;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type(): string {
|
|
30
|
+
return CheckURLIsPhishingMsg.type();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { PhishingListService } from "./service";
|
|
2
|
+
import Http from "http";
|
|
3
|
+
|
|
4
|
+
const phishings = [
|
|
5
|
+
"keplr-vvallet-app.online",
|
|
6
|
+
"xn--kplr-vva.com",
|
|
7
|
+
"keplr-vvallet.tech",
|
|
8
|
+
"app-keplr-vvallet.online",
|
|
9
|
+
"keplr-vvallet-app.space",
|
|
10
|
+
"keplr-vvallet-app.host",
|
|
11
|
+
"app-keplr-vvallet.tech",
|
|
12
|
+
"app-keplr-vvallet.host",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const createMockServer = () => {
|
|
16
|
+
let queryCount = 0;
|
|
17
|
+
|
|
18
|
+
const server = Http.createServer((req, resp) => {
|
|
19
|
+
queryCount++;
|
|
20
|
+
|
|
21
|
+
// Most desired case
|
|
22
|
+
if (req.url === "/list1") {
|
|
23
|
+
resp.writeHead(200);
|
|
24
|
+
resp.end(phishings.join("\n"));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// If some strange cases exist
|
|
29
|
+
if (req.url === "/list2") {
|
|
30
|
+
resp.writeHead(200);
|
|
31
|
+
let str = "";
|
|
32
|
+
for (let i = 0; i < phishings.length; i++) {
|
|
33
|
+
let phishing = phishings[i];
|
|
34
|
+
|
|
35
|
+
switch (i) {
|
|
36
|
+
case 0:
|
|
37
|
+
phishing = phishing + "\n";
|
|
38
|
+
break;
|
|
39
|
+
case 1:
|
|
40
|
+
// Windows line break
|
|
41
|
+
phishing = phishing + "\r\n";
|
|
42
|
+
break;
|
|
43
|
+
case 2:
|
|
44
|
+
phishing = phishing + ";";
|
|
45
|
+
break;
|
|
46
|
+
case 3:
|
|
47
|
+
phishing = phishing + ",";
|
|
48
|
+
break;
|
|
49
|
+
case 4:
|
|
50
|
+
phishing = phishing + " ; ";
|
|
51
|
+
break;
|
|
52
|
+
case 5:
|
|
53
|
+
phishing = phishing + " , ";
|
|
54
|
+
break;
|
|
55
|
+
case 6:
|
|
56
|
+
phishing = "third-domain." + phishing;
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
phishing = ` ${phishing} `;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
str += phishing;
|
|
63
|
+
}
|
|
64
|
+
resp.end(str);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Even if an invalid endpoint exists, only that endpoint should be ignored and proceeded.
|
|
69
|
+
// When second fetch, add other domain to test fetching interval.
|
|
70
|
+
if (req.url === "/list3") {
|
|
71
|
+
resp.writeHead(200);
|
|
72
|
+
let str = "";
|
|
73
|
+
for (let i = 0; i < phishings.length; i++) {
|
|
74
|
+
let phishing = phishings[i];
|
|
75
|
+
|
|
76
|
+
switch (i) {
|
|
77
|
+
case 0:
|
|
78
|
+
phishing = phishing + "\n";
|
|
79
|
+
break;
|
|
80
|
+
case 1:
|
|
81
|
+
phishing = phishing + ".\n";
|
|
82
|
+
break;
|
|
83
|
+
case 2:
|
|
84
|
+
phishing = "." + phishing + "\r\n";
|
|
85
|
+
break;
|
|
86
|
+
case 3:
|
|
87
|
+
phishing = "invalid;.." + phishing + "..;";
|
|
88
|
+
break;
|
|
89
|
+
case 4:
|
|
90
|
+
phishing = phishing + "/,";
|
|
91
|
+
break;
|
|
92
|
+
case 5:
|
|
93
|
+
phishing = phishing + "?test\n";
|
|
94
|
+
break;
|
|
95
|
+
case 6:
|
|
96
|
+
phishing = "third-domain." + phishing + "\n";
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
phishing = ` ${phishing} `;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
str += phishing;
|
|
103
|
+
}
|
|
104
|
+
str += ";invalid";
|
|
105
|
+
|
|
106
|
+
if (queryCount === 2) {
|
|
107
|
+
str += ";added.domain";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
resp.end(str);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (req.url === "/test-retry") {
|
|
115
|
+
if (queryCount % 3 === 0) {
|
|
116
|
+
resp.writeHead(400);
|
|
117
|
+
resp.end();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
resp.writeHead(200);
|
|
122
|
+
resp.end(phishings.slice(0, queryCount).join("\n"));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new Error("invalid url");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
server.listen();
|
|
130
|
+
|
|
131
|
+
const address = server.address();
|
|
132
|
+
if (!address || typeof address === "string") {
|
|
133
|
+
throw new Error("Failed to get address for server");
|
|
134
|
+
}
|
|
135
|
+
const port = address.port;
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
port,
|
|
139
|
+
closeServer: () => {
|
|
140
|
+
server.close();
|
|
141
|
+
},
|
|
142
|
+
getQueryCount: () => {
|
|
143
|
+
return queryCount;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
describe("Test phishing list service", () => {
|
|
149
|
+
let eachService: PhishingListService | undefined;
|
|
150
|
+
let port: number = -1;
|
|
151
|
+
let closeServer: (() => void) | undefined;
|
|
152
|
+
let getQueryCount: () => number;
|
|
153
|
+
|
|
154
|
+
beforeEach(() => {
|
|
155
|
+
const server = createMockServer();
|
|
156
|
+
port = server.port;
|
|
157
|
+
closeServer = server.closeServer;
|
|
158
|
+
getQueryCount = server.getQueryCount;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
afterEach(() => {
|
|
162
|
+
if (eachService) {
|
|
163
|
+
eachService.stop();
|
|
164
|
+
eachService = undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (closeServer) {
|
|
168
|
+
closeServer();
|
|
169
|
+
closeServer = undefined;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const waitServiceInit = (service: PhishingListService) => {
|
|
174
|
+
return new Promise<void>((resolve) => {
|
|
175
|
+
if (service.hasInited) {
|
|
176
|
+
resolve();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const intervalId = setInterval(() => {
|
|
181
|
+
if (service.hasInited) {
|
|
182
|
+
resolve();
|
|
183
|
+
clearInterval(intervalId);
|
|
184
|
+
}
|
|
185
|
+
}, 10);
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const testCheckURLIsPhishing = (service: PhishingListService) => {
|
|
190
|
+
for (const phishing of phishings) {
|
|
191
|
+
expect(service.checkURLIsPhishing(`http://${phishing}`)).toBe(true);
|
|
192
|
+
expect(service.checkURLIsPhishing(`https://${phishing}`)).toBe(true);
|
|
193
|
+
expect(service.checkURLIsPhishing(`https://test.${phishing}`)).toBe(true);
|
|
194
|
+
expect(service.checkURLIsPhishing(`https://test.${phishing}.`)).toBe(
|
|
195
|
+
true
|
|
196
|
+
);
|
|
197
|
+
expect(service.checkURLIsPhishing(`https://test.${phishing}./`)).toBe(
|
|
198
|
+
true
|
|
199
|
+
);
|
|
200
|
+
expect(service.checkURLIsPhishing(`https://test.${phishing}..`)).toBe(
|
|
201
|
+
true
|
|
202
|
+
);
|
|
203
|
+
expect(service.checkURLIsPhishing(`https://test.${phishing}../`)).toBe(
|
|
204
|
+
true
|
|
205
|
+
);
|
|
206
|
+
expect(
|
|
207
|
+
service.checkURLIsPhishing(`https://test.${phishing}..?test`)
|
|
208
|
+
).toBe(true);
|
|
209
|
+
expect(
|
|
210
|
+
service.checkURLIsPhishing(`https://test.${phishing}..?test=1`)
|
|
211
|
+
).toBe(true);
|
|
212
|
+
expect(service.checkURLIsPhishing(`https://.test.test.${phishing}`)).toBe(
|
|
213
|
+
true
|
|
214
|
+
);
|
|
215
|
+
expect(
|
|
216
|
+
service.checkURLIsPhishing(`https://..test.test.${phishing}`)
|
|
217
|
+
).toBe(true);
|
|
218
|
+
expect(
|
|
219
|
+
service.checkURLIsPhishing(`https://..test..test...${phishing}`)
|
|
220
|
+
).toBe(true);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
test("Test basic PhishingListService with valid cases", async () => {
|
|
225
|
+
const service = new PhishingListService({
|
|
226
|
+
blockListUrl: `http://127.0.0.1:${port}/list1`,
|
|
227
|
+
fetchingIntervalMs: 3600,
|
|
228
|
+
retryIntervalMs: 3600,
|
|
229
|
+
});
|
|
230
|
+
eachService = service;
|
|
231
|
+
|
|
232
|
+
service.init();
|
|
233
|
+
|
|
234
|
+
await waitServiceInit(service);
|
|
235
|
+
|
|
236
|
+
testCheckURLIsPhishing(service);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("Test basic PhishingListService with strange separators", async () => {
|
|
240
|
+
const service = new PhishingListService({
|
|
241
|
+
blockListUrl: `http://127.0.0.1:${port}/list2`,
|
|
242
|
+
fetchingIntervalMs: 3600,
|
|
243
|
+
retryIntervalMs: 3600,
|
|
244
|
+
});
|
|
245
|
+
eachService = service;
|
|
246
|
+
|
|
247
|
+
service.init();
|
|
248
|
+
|
|
249
|
+
await waitServiceInit(service);
|
|
250
|
+
|
|
251
|
+
testCheckURLIsPhishing(service);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("Test basic PhishingListService with strange cases", async () => {
|
|
255
|
+
const service = new PhishingListService({
|
|
256
|
+
blockListUrl: `http://127.0.0.1:${port}/list3`,
|
|
257
|
+
fetchingIntervalMs: 3600,
|
|
258
|
+
retryIntervalMs: 3600,
|
|
259
|
+
});
|
|
260
|
+
eachService = service;
|
|
261
|
+
|
|
262
|
+
service.init();
|
|
263
|
+
|
|
264
|
+
await waitServiceInit(service);
|
|
265
|
+
|
|
266
|
+
testCheckURLIsPhishing(service);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("Test basic PhishingListService fetching interval", async () => {
|
|
270
|
+
const service = new PhishingListService({
|
|
271
|
+
blockListUrl: `http://127.0.0.1:${port}/list3`,
|
|
272
|
+
fetchingIntervalMs: 200,
|
|
273
|
+
retryIntervalMs: 3600,
|
|
274
|
+
});
|
|
275
|
+
eachService = service;
|
|
276
|
+
|
|
277
|
+
service.init();
|
|
278
|
+
|
|
279
|
+
await waitServiceInit(service);
|
|
280
|
+
|
|
281
|
+
testCheckURLIsPhishing(service);
|
|
282
|
+
expect(service.checkURLIsPhishing("https://added.domain")).toBe(false);
|
|
283
|
+
|
|
284
|
+
expect(getQueryCount()).toBe(1);
|
|
285
|
+
|
|
286
|
+
// Wait re-fetching
|
|
287
|
+
await new Promise((resolve) => setTimeout(resolve, 210));
|
|
288
|
+
|
|
289
|
+
testCheckURLIsPhishing(service);
|
|
290
|
+
// See the implementation of /list3
|
|
291
|
+
expect(service.checkURLIsPhishing("https://added.domain")).toBe(true);
|
|
292
|
+
|
|
293
|
+
expect(getQueryCount()).toBe(2);
|
|
294
|
+
|
|
295
|
+
// Wait re-fetching
|
|
296
|
+
await new Promise((resolve) => setTimeout(resolve, 210));
|
|
297
|
+
|
|
298
|
+
testCheckURLIsPhishing(service);
|
|
299
|
+
// See the implementation of /list3
|
|
300
|
+
expect(service.checkURLIsPhishing("https://added.domain")).toBe(false);
|
|
301
|
+
|
|
302
|
+
expect(getQueryCount()).toBe(3);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("Test basic PhishingListService fetching interval with retry interval", async () => {
|
|
306
|
+
const service = new PhishingListService({
|
|
307
|
+
blockListUrl: `http://127.0.0.1:${port}/test-retry`,
|
|
308
|
+
fetchingIntervalMs: 200,
|
|
309
|
+
retryIntervalMs: 100,
|
|
310
|
+
});
|
|
311
|
+
eachService = service;
|
|
312
|
+
|
|
313
|
+
service.init();
|
|
314
|
+
|
|
315
|
+
const testPhishingUntil = (count: number) => {
|
|
316
|
+
const tests = phishings.slice(0, count);
|
|
317
|
+
for (const test of tests) {
|
|
318
|
+
expect(service.checkURLIsPhishing("https://" + test)).toBe(true);
|
|
319
|
+
}
|
|
320
|
+
if (phishings.length > count) {
|
|
321
|
+
const test = phishings[count];
|
|
322
|
+
expect(service.checkURLIsPhishing("https://" + test)).toBe(false);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
await waitServiceInit(service);
|
|
327
|
+
|
|
328
|
+
testPhishingUntil(1);
|
|
329
|
+
expect(getQueryCount()).toBe(1);
|
|
330
|
+
|
|
331
|
+
// Wait re-fetching
|
|
332
|
+
await new Promise((resolve) => setTimeout(resolve, 210));
|
|
333
|
+
|
|
334
|
+
// See the implementation of /test-retry
|
|
335
|
+
testPhishingUntil(2);
|
|
336
|
+
expect(getQueryCount()).toBe(2);
|
|
337
|
+
|
|
338
|
+
// Wait re-fetching
|
|
339
|
+
await new Promise((resolve) => setTimeout(resolve, 210));
|
|
340
|
+
|
|
341
|
+
// See the implementation of /test-retry
|
|
342
|
+
// In this case, the fetching should be failed.
|
|
343
|
+
// So, there is no update on phishing list.
|
|
344
|
+
testPhishingUntil(2);
|
|
345
|
+
expect(getQueryCount()).toBe(3);
|
|
346
|
+
|
|
347
|
+
// Wait retry for failed query
|
|
348
|
+
await new Promise((resolve) => setTimeout(resolve, 110));
|
|
349
|
+
|
|
350
|
+
// See the implementation of /test-retry
|
|
351
|
+
testPhishingUntil(4);
|
|
352
|
+
expect(getQueryCount()).toBe(4);
|
|
353
|
+
|
|
354
|
+
// Not yet re-fetching
|
|
355
|
+
await new Promise((resolve) => setTimeout(resolve, 110));
|
|
356
|
+
|
|
357
|
+
// See the implementation of /test-retry
|
|
358
|
+
testPhishingUntil(4);
|
|
359
|
+
expect(getQueryCount()).toBe(4);
|
|
360
|
+
|
|
361
|
+
// Now re-fetching
|
|
362
|
+
await new Promise((resolve) => setTimeout(resolve, 110));
|
|
363
|
+
// See the implementation of /test-retry
|
|
364
|
+
testPhishingUntil(5);
|
|
365
|
+
expect(getQueryCount()).toBe(5);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import Axios from "axios";
|
|
2
|
+
import { parseDomainUntilSecondLevel } from "./utils";
|
|
3
|
+
|
|
4
|
+
export class PhishingListService {
|
|
5
|
+
protected map: Map<string, boolean> = new Map();
|
|
6
|
+
|
|
7
|
+
protected _hasInited: boolean = false;
|
|
8
|
+
protected _hasStopped: boolean = false;
|
|
9
|
+
protected timeoutId?: NodeJS.Timeout;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
public readonly opts: {
|
|
13
|
+
readonly blockListUrl: string;
|
|
14
|
+
readonly fetchingIntervalMs: number;
|
|
15
|
+
readonly retryIntervalMs: number;
|
|
16
|
+
}
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
get hasInited(): boolean {
|
|
20
|
+
return this._hasInited;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
init() {
|
|
24
|
+
this.startFetchPhishingList();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
stop() {
|
|
28
|
+
if (this.timeoutId != null) {
|
|
29
|
+
clearTimeout(this.timeoutId);
|
|
30
|
+
this.timeoutId = undefined;
|
|
31
|
+
}
|
|
32
|
+
this._hasStopped = true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async startFetchPhishingList() {
|
|
36
|
+
if (this.timeoutId != null) {
|
|
37
|
+
clearTimeout(this.timeoutId);
|
|
38
|
+
this.timeoutId = undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this._hasStopped) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let failed = false;
|
|
46
|
+
try {
|
|
47
|
+
const res = await Axios.get<string>(this.opts.blockListUrl);
|
|
48
|
+
|
|
49
|
+
const domains = res.data
|
|
50
|
+
.split(/(\r?\n)|,|;|\s|\t/)
|
|
51
|
+
.filter((str) => str != null)
|
|
52
|
+
.map((str) => {
|
|
53
|
+
return str.trim();
|
|
54
|
+
})
|
|
55
|
+
.filter((str) => str.length > 0);
|
|
56
|
+
|
|
57
|
+
const map = new Map<string, boolean>();
|
|
58
|
+
|
|
59
|
+
for (const domain of domains) {
|
|
60
|
+
try {
|
|
61
|
+
map.set(parseDomainUntilSecondLevel(domain), true);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.log(e);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this._hasInited = true;
|
|
68
|
+
this.map = map;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
failed = true;
|
|
71
|
+
console.log(e);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!this._hasStopped) {
|
|
75
|
+
this.timeoutId = setTimeout(
|
|
76
|
+
() => {
|
|
77
|
+
this.startFetchPhishingList();
|
|
78
|
+
},
|
|
79
|
+
failed ? this.opts.retryIntervalMs : this.opts.fetchingIntervalMs
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
checkURLIsPhishing(url: string): boolean {
|
|
85
|
+
const parsed = new URL(url);
|
|
86
|
+
return this.map.get(parseDomainUntilSecondLevel(parsed.origin)) === true;
|
|
87
|
+
}
|
|
88
|
+
}
|