@polkadot/extension-base 0.60.1 → 0.61.2
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/background/handlers/State.d.ts +2 -1
- package/background/handlers/State.js +25 -1
- package/background/handlers/Tabs.d.ts +1 -0
- package/background/handlers/Tabs.js +16 -1
- package/cjs/background/handlers/State.d.ts +2 -1
- package/cjs/background/handlers/State.js +25 -1
- package/cjs/background/handlers/Tabs.d.ts +1 -0
- package/cjs/background/handlers/Tabs.js +16 -1
- package/cjs/packageInfo.js +1 -1
- package/package.json +15 -14
- package/packageInfo.js +1 -1
|
@@ -44,7 +44,7 @@ export default class State {
|
|
|
44
44
|
readonly signSubject: BehaviorSubject<SigningRequest[]>;
|
|
45
45
|
readonly authUrlSubjects: Record<string, BehaviorSubject<AuthUrlInfo>>;
|
|
46
46
|
defaultAuthAccountSelection: string[];
|
|
47
|
-
constructor(providers?: Providers);
|
|
47
|
+
constructor(providers?: Providers, rateLimitInterval?: number);
|
|
48
48
|
init(): Promise<void>;
|
|
49
49
|
get knownMetadata(): MetadataDef[];
|
|
50
50
|
get numAuthRequests(): number;
|
|
@@ -90,6 +90,7 @@ export default class State {
|
|
|
90
90
|
rpcUnsubscribe(request: RequestRpcUnsubscribe, port: chrome.runtime.Port): Promise<boolean>;
|
|
91
91
|
saveMetadata(meta: MetadataDef): Promise<void>;
|
|
92
92
|
setNotification(notification: string): boolean;
|
|
93
|
+
private handleSignRequest;
|
|
93
94
|
sign(url: string, request: RequestSign, account: AccountJson): Promise<ResponseSigning>;
|
|
94
95
|
}
|
|
95
96
|
export {};
|
|
@@ -64,6 +64,9 @@ async function extractMetadata(store) {
|
|
|
64
64
|
}
|
|
65
65
|
export default class State {
|
|
66
66
|
#authUrls = new Map();
|
|
67
|
+
#lastRequestTimestamps = new Map();
|
|
68
|
+
#maxEntries = 10;
|
|
69
|
+
#rateLimitInterval = 3000; // 3 seconds
|
|
67
70
|
#authRequests = {};
|
|
68
71
|
#metaStore = new MetadataStore();
|
|
69
72
|
// Map of providers currently injected in tabs
|
|
@@ -80,8 +83,10 @@ export default class State {
|
|
|
80
83
|
signSubject = new BehaviorSubject([]);
|
|
81
84
|
authUrlSubjects = {};
|
|
82
85
|
defaultAuthAccountSelection = [];
|
|
83
|
-
constructor(providers = {}) {
|
|
86
|
+
constructor(providers = {}, rateLimitInterval = 3000) {
|
|
87
|
+
assert(rateLimitInterval >= 0, 'Expects non-negative number for rateLimitInterval');
|
|
84
88
|
this.#providers = providers;
|
|
89
|
+
this.#rateLimitInterval = rateLimitInterval;
|
|
85
90
|
}
|
|
86
91
|
async init() {
|
|
87
92
|
await extractMetadata(this.#metaStore);
|
|
@@ -437,8 +442,27 @@ export default class State {
|
|
|
437
442
|
this.#notification = notification;
|
|
438
443
|
return true;
|
|
439
444
|
}
|
|
445
|
+
handleSignRequest(origin) {
|
|
446
|
+
const now = Date.now();
|
|
447
|
+
const lastTime = this.#lastRequestTimestamps.get(origin) || 0;
|
|
448
|
+
if (now - lastTime < this.#rateLimitInterval) {
|
|
449
|
+
throw new Error('Rate limit exceeded. Try again later.');
|
|
450
|
+
}
|
|
451
|
+
// If we're about to exceed max entries, evict the oldest
|
|
452
|
+
if (!this.#lastRequestTimestamps.has(origin) && this.#lastRequestTimestamps.size >= this.#maxEntries) {
|
|
453
|
+
const oldestKey = this.#lastRequestTimestamps.keys().next().value;
|
|
454
|
+
oldestKey && this.#lastRequestTimestamps.delete(oldestKey);
|
|
455
|
+
}
|
|
456
|
+
this.#lastRequestTimestamps.set(origin, now);
|
|
457
|
+
}
|
|
440
458
|
sign(url, request, account) {
|
|
441
459
|
const id = getId();
|
|
460
|
+
try {
|
|
461
|
+
this.handleSignRequest(url);
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
return Promise.reject(error);
|
|
465
|
+
}
|
|
442
466
|
return new Promise((resolve, reject) => {
|
|
443
467
|
this.#signRequests[id] = {
|
|
444
468
|
...this.signComplete(id, resolve, reject),
|
|
@@ -20,6 +20,7 @@ export default class Tabs {
|
|
|
20
20
|
private rpcSubscribeConnected;
|
|
21
21
|
private rpcUnsubscribe;
|
|
22
22
|
private redirectPhishingLanding;
|
|
23
|
+
private parseUrl;
|
|
23
24
|
private redirectIfPhishing;
|
|
24
25
|
handle<TMessageType extends MessageTypes>(id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port?: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]>;
|
|
25
26
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { combineLatest } from 'rxjs';
|
|
2
|
+
import { parse } from 'tldts';
|
|
2
3
|
import { checkIfDenied } from '@polkadot/phishing';
|
|
3
4
|
import { keyring } from '@polkadot/ui-keyring';
|
|
4
5
|
import { accounts as accountsObservable } from '@polkadot/ui-keyring/observable/accounts';
|
|
@@ -142,6 +143,19 @@ export default class Tabs {
|
|
|
142
143
|
.forEach((id) => withErrorLog(() => chrome.tabs.update(id, { url })));
|
|
143
144
|
});
|
|
144
145
|
}
|
|
146
|
+
parseUrl(rawUrl) {
|
|
147
|
+
let from = 'extension';
|
|
148
|
+
if (rawUrl) {
|
|
149
|
+
try {
|
|
150
|
+
const { hostname } = parse(rawUrl);
|
|
151
|
+
from = hostname || '<unknown>'; // Only use the hostname
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
from = '<unknown>';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return from;
|
|
158
|
+
}
|
|
145
159
|
async redirectIfPhishing(url) {
|
|
146
160
|
const isInDenyList = await checkIfDenied(url);
|
|
147
161
|
if (isInDenyList) {
|
|
@@ -152,7 +166,8 @@ export default class Tabs {
|
|
|
152
166
|
}
|
|
153
167
|
async handle(id, type, request, url, port) {
|
|
154
168
|
if (type === 'pub(phishing.redirectIfDenied)') {
|
|
155
|
-
|
|
169
|
+
const parsedUrl = this.parseUrl(url);
|
|
170
|
+
return this.redirectIfPhishing(parsedUrl);
|
|
156
171
|
}
|
|
157
172
|
if (type !== 'pub(authorize.tab)') {
|
|
158
173
|
this.#state.ensureUrlAuthorized(url);
|
|
@@ -44,7 +44,7 @@ export default class State {
|
|
|
44
44
|
readonly signSubject: BehaviorSubject<SigningRequest[]>;
|
|
45
45
|
readonly authUrlSubjects: Record<string, BehaviorSubject<AuthUrlInfo>>;
|
|
46
46
|
defaultAuthAccountSelection: string[];
|
|
47
|
-
constructor(providers?: Providers);
|
|
47
|
+
constructor(providers?: Providers, rateLimitInterval?: number);
|
|
48
48
|
init(): Promise<void>;
|
|
49
49
|
get knownMetadata(): MetadataDef[];
|
|
50
50
|
get numAuthRequests(): number;
|
|
@@ -90,6 +90,7 @@ export default class State {
|
|
|
90
90
|
rpcUnsubscribe(request: RequestRpcUnsubscribe, port: chrome.runtime.Port): Promise<boolean>;
|
|
91
91
|
saveMetadata(meta: MetadataDef): Promise<void>;
|
|
92
92
|
setNotification(notification: string): boolean;
|
|
93
|
+
private handleSignRequest;
|
|
93
94
|
sign(url: string, request: RequestSign, account: AccountJson): Promise<ResponseSigning>;
|
|
94
95
|
}
|
|
95
96
|
export {};
|
|
@@ -67,6 +67,9 @@ async function extractMetadata(store) {
|
|
|
67
67
|
}
|
|
68
68
|
class State {
|
|
69
69
|
#authUrls = new Map();
|
|
70
|
+
#lastRequestTimestamps = new Map();
|
|
71
|
+
#maxEntries = 10;
|
|
72
|
+
#rateLimitInterval = 3000; // 3 seconds
|
|
70
73
|
#authRequests = {};
|
|
71
74
|
#metaStore = new index_js_1.MetadataStore();
|
|
72
75
|
// Map of providers currently injected in tabs
|
|
@@ -83,8 +86,10 @@ class State {
|
|
|
83
86
|
signSubject = new rxjs_1.BehaviorSubject([]);
|
|
84
87
|
authUrlSubjects = {};
|
|
85
88
|
defaultAuthAccountSelection = [];
|
|
86
|
-
constructor(providers = {}) {
|
|
89
|
+
constructor(providers = {}, rateLimitInterval = 3000) {
|
|
90
|
+
(0, util_1.assert)(rateLimitInterval >= 0, 'Expects non-negative number for rateLimitInterval');
|
|
87
91
|
this.#providers = providers;
|
|
92
|
+
this.#rateLimitInterval = rateLimitInterval;
|
|
88
93
|
}
|
|
89
94
|
async init() {
|
|
90
95
|
await extractMetadata(this.#metaStore);
|
|
@@ -440,8 +445,27 @@ class State {
|
|
|
440
445
|
this.#notification = notification;
|
|
441
446
|
return true;
|
|
442
447
|
}
|
|
448
|
+
handleSignRequest(origin) {
|
|
449
|
+
const now = Date.now();
|
|
450
|
+
const lastTime = this.#lastRequestTimestamps.get(origin) || 0;
|
|
451
|
+
if (now - lastTime < this.#rateLimitInterval) {
|
|
452
|
+
throw new Error('Rate limit exceeded. Try again later.');
|
|
453
|
+
}
|
|
454
|
+
// If we're about to exceed max entries, evict the oldest
|
|
455
|
+
if (!this.#lastRequestTimestamps.has(origin) && this.#lastRequestTimestamps.size >= this.#maxEntries) {
|
|
456
|
+
const oldestKey = this.#lastRequestTimestamps.keys().next().value;
|
|
457
|
+
oldestKey && this.#lastRequestTimestamps.delete(oldestKey);
|
|
458
|
+
}
|
|
459
|
+
this.#lastRequestTimestamps.set(origin, now);
|
|
460
|
+
}
|
|
443
461
|
sign(url, request, account) {
|
|
444
462
|
const id = (0, getId_js_1.getId)();
|
|
463
|
+
try {
|
|
464
|
+
this.handleSignRequest(url);
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
return Promise.reject(error);
|
|
468
|
+
}
|
|
445
469
|
return new Promise((resolve, reject) => {
|
|
446
470
|
this.#signRequests[id] = {
|
|
447
471
|
...this.signComplete(id, resolve, reject),
|
|
@@ -20,6 +20,7 @@ export default class Tabs {
|
|
|
20
20
|
private rpcSubscribeConnected;
|
|
21
21
|
private rpcUnsubscribe;
|
|
22
22
|
private redirectPhishingLanding;
|
|
23
|
+
private parseUrl;
|
|
23
24
|
private redirectIfPhishing;
|
|
24
25
|
handle<TMessageType extends MessageTypes>(id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port?: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]>;
|
|
25
26
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
|
+
const tldts_1 = require("tldts");
|
|
5
6
|
const phishing_1 = require("@polkadot/phishing");
|
|
6
7
|
const ui_keyring_1 = require("@polkadot/ui-keyring");
|
|
7
8
|
const accounts_1 = require("@polkadot/ui-keyring/observable/accounts");
|
|
@@ -145,6 +146,19 @@ class Tabs {
|
|
|
145
146
|
.forEach((id) => (0, helpers_js_1.withErrorLog)(() => chrome.tabs.update(id, { url })));
|
|
146
147
|
});
|
|
147
148
|
}
|
|
149
|
+
parseUrl(rawUrl) {
|
|
150
|
+
let from = 'extension';
|
|
151
|
+
if (rawUrl) {
|
|
152
|
+
try {
|
|
153
|
+
const { hostname } = (0, tldts_1.parse)(rawUrl);
|
|
154
|
+
from = hostname || '<unknown>'; // Only use the hostname
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
from = '<unknown>';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return from;
|
|
161
|
+
}
|
|
148
162
|
async redirectIfPhishing(url) {
|
|
149
163
|
const isInDenyList = await (0, phishing_1.checkIfDenied)(url);
|
|
150
164
|
if (isInDenyList) {
|
|
@@ -155,7 +169,8 @@ class Tabs {
|
|
|
155
169
|
}
|
|
156
170
|
async handle(id, type, request, url, port) {
|
|
157
171
|
if (type === 'pub(phishing.redirectIfDenied)') {
|
|
158
|
-
|
|
172
|
+
const parsedUrl = this.parseUrl(url);
|
|
173
|
+
return this.redirectIfPhishing(parsedUrl);
|
|
159
174
|
}
|
|
160
175
|
if (type !== 'pub(authorize.tab)') {
|
|
161
176
|
this.#state.ensureUrlAuthorized(url);
|
package/cjs/packageInfo.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.packageInfo = void 0;
|
|
4
|
-
exports.packageInfo = { name: '@polkadot/extension-base', path: typeof __dirname === 'string' ? __dirname : 'auto', type: 'cjs', version: '0.
|
|
4
|
+
exports.packageInfo = { name: '@polkadot/extension-base', path: typeof __dirname === 'string' ? __dirname : 'auto', type: 'cjs', version: '0.61.2' };
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"./cjs/packageDetect.js"
|
|
19
19
|
],
|
|
20
20
|
"type": "module",
|
|
21
|
-
"version": "0.
|
|
21
|
+
"version": "0.61.2",
|
|
22
22
|
"main": "./cjs/index.js",
|
|
23
23
|
"module": "./index.js",
|
|
24
24
|
"types": "./index.d.ts",
|
|
@@ -479,21 +479,22 @@
|
|
|
479
479
|
}
|
|
480
480
|
},
|
|
481
481
|
"dependencies": {
|
|
482
|
-
"@polkadot/api": "^16.
|
|
483
|
-
"@polkadot/extension-chains": "0.
|
|
484
|
-
"@polkadot/extension-dapp": "0.
|
|
485
|
-
"@polkadot/extension-inject": "0.
|
|
486
|
-
"@polkadot/keyring": "^13.5.
|
|
487
|
-
"@polkadot/networks": "^13.5.
|
|
488
|
-
"@polkadot/phishing": "^0.25.
|
|
489
|
-
"@polkadot/rpc-provider": "^16.
|
|
490
|
-
"@polkadot/types": "^16.
|
|
491
|
-
"@polkadot/ui-keyring": "^3.15.
|
|
492
|
-
"@polkadot/ui-settings": "^3.15.
|
|
493
|
-
"@polkadot/util": "^13.5.
|
|
494
|
-
"@polkadot/util-crypto": "^13.5.
|
|
482
|
+
"@polkadot/api": "^16.3.1",
|
|
483
|
+
"@polkadot/extension-chains": "0.61.2",
|
|
484
|
+
"@polkadot/extension-dapp": "0.61.2",
|
|
485
|
+
"@polkadot/extension-inject": "0.61.2",
|
|
486
|
+
"@polkadot/keyring": "^13.5.3",
|
|
487
|
+
"@polkadot/networks": "^13.5.3",
|
|
488
|
+
"@polkadot/phishing": "^0.25.14",
|
|
489
|
+
"@polkadot/rpc-provider": "^16.3.1",
|
|
490
|
+
"@polkadot/types": "^16.3.1",
|
|
491
|
+
"@polkadot/ui-keyring": "^3.15.2",
|
|
492
|
+
"@polkadot/ui-settings": "^3.15.2",
|
|
493
|
+
"@polkadot/util": "^13.5.3",
|
|
494
|
+
"@polkadot/util-crypto": "^13.5.3",
|
|
495
495
|
"eventemitter3": "^5.0.1",
|
|
496
496
|
"rxjs": "^7.8.1",
|
|
497
|
+
"tldts": "^7.0.8",
|
|
497
498
|
"tslib": "^2.8.1"
|
|
498
499
|
}
|
|
499
500
|
}
|
package/packageInfo.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageInfo = { name: '@polkadot/extension-base', path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto', type: 'esm', version: '0.
|
|
1
|
+
export const packageInfo = { name: '@polkadot/extension-base', path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto', type: 'esm', version: '0.61.2' };
|