@polkadot/extension-base 0.60.1 → 0.61.1

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.
@@ -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
@@ -437,8 +440,27 @@ export default class State {
437
440
  this.#notification = notification;
438
441
  return true;
439
442
  }
443
+ handleSignRequest(origin) {
444
+ const now = Date.now();
445
+ const lastTime = this.#lastRequestTimestamps.get(origin) || 0;
446
+ if (now - lastTime < this.#rateLimitInterval) {
447
+ throw new Error('Rate limit exceeded. Try again later.');
448
+ }
449
+ // If we're about to exceed max entries, evict the oldest
450
+ if (!this.#lastRequestTimestamps.has(origin) && this.#lastRequestTimestamps.size >= this.#maxEntries) {
451
+ const oldestKey = this.#lastRequestTimestamps.keys().next().value;
452
+ oldestKey && this.#lastRequestTimestamps.delete(oldestKey);
453
+ }
454
+ this.#lastRequestTimestamps.set(origin, now);
455
+ }
440
456
  sign(url, request, account) {
441
457
  const id = getId();
458
+ try {
459
+ this.handleSignRequest(url);
460
+ }
461
+ catch (error) {
462
+ return Promise.reject(error);
463
+ }
442
464
  return new Promise((resolve, reject) => {
443
465
  this.#signRequests[id] = {
444
466
  ...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
- return this.redirectIfPhishing(url);
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);
@@ -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
@@ -440,8 +443,27 @@ class State {
440
443
  this.#notification = notification;
441
444
  return true;
442
445
  }
446
+ handleSignRequest(origin) {
447
+ const now = Date.now();
448
+ const lastTime = this.#lastRequestTimestamps.get(origin) || 0;
449
+ if (now - lastTime < this.#rateLimitInterval) {
450
+ throw new Error('Rate limit exceeded. Try again later.');
451
+ }
452
+ // If we're about to exceed max entries, evict the oldest
453
+ if (!this.#lastRequestTimestamps.has(origin) && this.#lastRequestTimestamps.size >= this.#maxEntries) {
454
+ const oldestKey = this.#lastRequestTimestamps.keys().next().value;
455
+ oldestKey && this.#lastRequestTimestamps.delete(oldestKey);
456
+ }
457
+ this.#lastRequestTimestamps.set(origin, now);
458
+ }
443
459
  sign(url, request, account) {
444
460
  const id = (0, getId_js_1.getId)();
461
+ try {
462
+ this.handleSignRequest(url);
463
+ }
464
+ catch (error) {
465
+ return Promise.reject(error);
466
+ }
445
467
  return new Promise((resolve, reject) => {
446
468
  this.#signRequests[id] = {
447
469
  ...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
- return this.redirectIfPhishing(url);
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);
@@ -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.60.1' };
4
+ exports.packageInfo = { name: '@polkadot/extension-base', path: typeof __dirname === 'string' ? __dirname : 'auto', type: 'cjs', version: '0.61.1' };
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "./cjs/packageDetect.js"
19
19
  ],
20
20
  "type": "module",
21
- "version": "0.60.1",
21
+ "version": "0.61.1",
22
22
  "main": "./cjs/index.js",
23
23
  "module": "./index.js",
24
24
  "types": "./index.d.ts",
@@ -480,9 +480,9 @@
480
480
  },
481
481
  "dependencies": {
482
482
  "@polkadot/api": "^16.2.2",
483
- "@polkadot/extension-chains": "0.60.1",
484
- "@polkadot/extension-dapp": "0.60.1",
485
- "@polkadot/extension-inject": "0.60.1",
483
+ "@polkadot/extension-chains": "0.61.1",
484
+ "@polkadot/extension-dapp": "0.61.1",
485
+ "@polkadot/extension-inject": "0.61.1",
486
486
  "@polkadot/keyring": "^13.5.2",
487
487
  "@polkadot/networks": "^13.5.2",
488
488
  "@polkadot/phishing": "^0.25.13",
@@ -494,6 +494,7 @@
494
494
  "@polkadot/util-crypto": "^13.5.2",
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.60.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.61.1' };