@pezkuwi/extension-base 0.62.13 → 0.62.18
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/package.json +21 -475
- package/src/background/RequestBytesSign.ts +28 -0
- package/src/background/RequestExtrinsicSign.ts +22 -0
- package/src/background/handlers/Extension.spec.ts +478 -0
- package/src/background/handlers/Extension.ts +690 -0
- package/src/background/handlers/State.ts +664 -0
- package/src/background/handlers/Tabs.ts +289 -0
- package/src/background/handlers/helpers.ts +14 -0
- package/src/background/handlers/index.ts +60 -0
- package/src/background/handlers/subscriptions.ts +32 -0
- package/src/background/index.ts +4 -0
- package/src/background/types.ts +432 -0
- package/src/bundle.ts +4 -0
- package/{defaults.js → src/defaults.ts} +15 -2
- package/src/index.ts +7 -0
- package/{packageDetect.js → src/packageDetect.ts} +8 -0
- package/src/packageInfo.ts +6 -0
- package/src/page/Accounts.ts +33 -0
- package/src/page/Injected.ts +33 -0
- package/src/page/Metadata.ts +22 -0
- package/src/page/PostMessageProvider.ts +182 -0
- package/src/page/Signer.ts +45 -0
- package/src/page/index.ts +89 -0
- package/src/page/types.ts +10 -0
- package/src/stores/Accounts.ts +28 -0
- package/src/stores/Base.ts +93 -0
- package/src/stores/Metadata.ts +17 -0
- package/{stores/index.js → src/stores/index.ts} +3 -0
- package/src/types.ts +12 -0
- package/src/utils/canDerive.ts +8 -0
- package/src/utils/getId.ts +10 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/portUtils.ts +65 -0
- package/tsconfig.build.json +16 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.spec.json +18 -0
- package/tsconfig.spec.tsbuildinfo +1 -0
- package/LICENSE +0 -201
- package/background/RequestBytesSign.d.ts +0 -12
- package/background/RequestBytesSign.js +0 -12
- package/background/RequestExtrinsicSign.d.ts +0 -12
- package/background/RequestExtrinsicSign.js +0 -11
- package/background/handlers/Extension.d.ts +0 -49
- package/background/handlers/Extension.js +0 -489
- package/background/handlers/State.js +0 -478
- package/background/handlers/Tabs.d.ts +0 -25
- package/background/handlers/Tabs.js +0 -195
- package/background/handlers/helpers.js +0 -11
- package/background/handlers/index.d.ts +0 -3
- package/background/handlers/index.js +0 -40
- package/background/handlers/subscriptions.d.ts +0 -3
- package/background/handlers/subscriptions.js +0 -18
- package/background/index.d.ts +0 -1
- package/background/index.js +0 -1
- package/background/types.js +0 -1
- package/bundle.d.ts +0 -1
- package/bundle.js +0 -1
- package/cjs/background/RequestBytesSign.d.ts +0 -12
- package/cjs/background/RequestBytesSign.js +0 -15
- package/cjs/background/RequestExtrinsicSign.d.ts +0 -12
- package/cjs/background/RequestExtrinsicSign.js +0 -14
- package/cjs/background/handlers/Extension.d.ts +0 -49
- package/cjs/background/handlers/Extension.js +0 -492
- package/cjs/background/handlers/State.d.ts +0 -96
- package/cjs/background/handlers/State.js +0 -482
- package/cjs/background/handlers/Tabs.d.ts +0 -25
- package/cjs/background/handlers/Tabs.js +0 -199
- package/cjs/background/handlers/helpers.d.ts +0 -1
- package/cjs/background/handlers/helpers.js +0 -14
- package/cjs/background/handlers/index.d.ts +0 -3
- package/cjs/background/handlers/index.js +0 -46
- package/cjs/background/handlers/subscriptions.d.ts +0 -3
- package/cjs/background/handlers/subscriptions.js +0 -22
- package/cjs/background/index.d.ts +0 -1
- package/cjs/background/index.js +0 -7
- package/cjs/background/types.d.ts +0 -343
- package/cjs/background/types.js +0 -2
- package/cjs/bundle.d.ts +0 -1
- package/cjs/bundle.js +0 -5
- package/cjs/defaults.js +0 -16
- package/cjs/index.d.ts +0 -1
- package/cjs/index.js +0 -4
- package/cjs/package.json +0 -3
- package/cjs/packageDetect.d.ts +0 -1
- package/cjs/packageDetect.js +0 -8
- package/cjs/packageInfo.d.ts +0 -6
- package/cjs/packageInfo.js +0 -4
- package/cjs/page/Accounts.d.ts +0 -7
- package/cjs/page/Accounts.js +0 -24
- package/cjs/page/Injected.d.ts +0 -13
- package/cjs/page/Injected.js +0 -25
- package/cjs/page/Metadata.d.ts +0 -7
- package/cjs/page/Metadata.js +0 -15
- package/cjs/page/PostMessageProvider.d.ts +0 -63
- package/cjs/page/PostMessageProvider.js +0 -135
- package/cjs/page/Signer.d.ts +0 -8
- package/cjs/page/Signer.js +0 -29
- package/cjs/page/index.d.ts +0 -16
- package/cjs/page/index.js +0 -52
- package/cjs/page/types.d.ts +0 -6
- package/cjs/page/types.js +0 -2
- package/cjs/stores/Accounts.js +0 -21
- package/cjs/stores/Base.js +0 -70
- package/cjs/stores/Metadata.js +0 -13
- package/cjs/stores/index.js +0 -8
- package/cjs/types.js +0 -2
- package/cjs/utils/canDerive.d.ts +0 -2
- package/cjs/utils/canDerive.js +0 -6
- package/cjs/utils/getId.js +0 -8
- package/cjs/utils/index.d.ts +0 -1
- package/cjs/utils/index.js +0 -5
- package/cjs/utils/portUtils.d.ts +0 -13
- package/cjs/utils/portUtils.js +0 -49
- package/defaults.d.ts +0 -9
- package/index.d.ts +0 -1
- package/index.js +0 -1
- package/packageDetect.d.ts +0 -1
- package/packageInfo.d.ts +0 -6
- package/packageInfo.js +0 -1
- package/page/Accounts.d.ts +0 -7
- package/page/Accounts.js +0 -21
- package/page/Injected.d.ts +0 -13
- package/page/Injected.js +0 -21
- package/page/Metadata.d.ts +0 -7
- package/page/Metadata.js +0 -12
- package/page/PostMessageProvider.d.ts +0 -63
- package/page/PostMessageProvider.js +0 -132
- package/page/Signer.d.ts +0 -8
- package/page/Signer.js +0 -26
- package/page/index.d.ts +0 -16
- package/page/index.js +0 -45
- package/page/types.d.ts +0 -6
- package/page/types.js +0 -1
- package/stores/Accounts.d.ts +0 -6
- package/stores/Accounts.js +0 -17
- package/stores/Base.d.ts +0 -9
- package/stores/Base.js +0 -67
- package/stores/Metadata.d.ts +0 -5
- package/stores/Metadata.js +0 -9
- package/stores/index.d.ts +0 -2
- package/types.d.ts +0 -9
- package/types.js +0 -1
- package/utils/canDerive.d.ts +0 -2
- package/utils/canDerive.js +0 -3
- package/utils/getId.d.ts +0 -1
- package/utils/getId.js +0 -5
- package/utils/index.d.ts +0 -1
- package/utils/index.js +0 -1
- package/utils/portUtils.d.ts +0 -13
- package/utils/portUtils.js +0 -43
- /package/{background → src/background}/handlers/State.d.ts +0 -0
- /package/{background → src/background}/handlers/helpers.d.ts +0 -0
- /package/{background → src/background}/types.d.ts +0 -0
- /package/{cjs → src}/defaults.d.ts +0 -0
- /package/{cjs → src}/stores/Accounts.d.ts +0 -0
- /package/{cjs → src}/stores/Base.d.ts +0 -0
- /package/{cjs → src}/stores/Metadata.d.ts +0 -0
- /package/{cjs → src}/stores/index.d.ts +0 -0
- /package/{cjs → src}/types.d.ts +0 -0
- /package/{cjs → src}/utils/getId.d.ts +0 -0
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
import { BehaviorSubject } from 'rxjs';
|
|
2
|
-
import { addMetadata, knownMetadata } from '@pezkuwi/extension-chains';
|
|
3
|
-
import { knownGenesis } from '@pezkuwi/networks/defaults';
|
|
4
|
-
import { settings } from '@pezkuwi/ui-settings';
|
|
5
|
-
import { assert } from '@pezkuwi/util';
|
|
6
|
-
import { MetadataStore } from '../../stores/index.js';
|
|
7
|
-
import { getId } from '../../utils/getId.js';
|
|
8
|
-
import { withErrorLog } from './helpers.js';
|
|
9
|
-
const NOTIFICATION_URL = chrome.runtime.getURL('notification.html');
|
|
10
|
-
const POPUP_WINDOW_OPTS = {
|
|
11
|
-
focused: true,
|
|
12
|
-
height: 621,
|
|
13
|
-
left: 150,
|
|
14
|
-
top: 150,
|
|
15
|
-
type: 'popup',
|
|
16
|
-
url: NOTIFICATION_URL,
|
|
17
|
-
width: 560
|
|
18
|
-
};
|
|
19
|
-
const NORMAL_WINDOW_OPTS = {
|
|
20
|
-
focused: true,
|
|
21
|
-
type: 'normal',
|
|
22
|
-
url: NOTIFICATION_URL
|
|
23
|
-
};
|
|
24
|
-
export var NotificationOptions;
|
|
25
|
-
(function (NotificationOptions) {
|
|
26
|
-
NotificationOptions[NotificationOptions["None"] = 0] = "None";
|
|
27
|
-
NotificationOptions[NotificationOptions["Normal"] = 1] = "Normal";
|
|
28
|
-
NotificationOptions[NotificationOptions["PopUp"] = 2] = "PopUp";
|
|
29
|
-
})(NotificationOptions || (NotificationOptions = {}));
|
|
30
|
-
const AUTH_URLS_KEY = 'authUrls';
|
|
31
|
-
const DEFAULT_AUTH_ACCOUNTS = 'defaultAuthAccounts';
|
|
32
|
-
async function extractMetadata(store) {
|
|
33
|
-
await store.allMap(async (map) => {
|
|
34
|
-
const knownEntries = Object.entries(knownGenesis);
|
|
35
|
-
const defs = {};
|
|
36
|
-
const removals = [];
|
|
37
|
-
Object
|
|
38
|
-
.entries(map)
|
|
39
|
-
.forEach(([key, def]) => {
|
|
40
|
-
const entry = knownEntries.find(([, hashes]) => hashes.includes(def.genesisHash));
|
|
41
|
-
if (entry) {
|
|
42
|
-
const [name, hashes] = entry;
|
|
43
|
-
const index = hashes.indexOf(def.genesisHash);
|
|
44
|
-
// flatten the known metadata based on the genesis index
|
|
45
|
-
// (lower is better/newer)
|
|
46
|
-
if (!defs[name] || (defs[name].index > index)) {
|
|
47
|
-
if (defs[name]) {
|
|
48
|
-
// remove the old version of the metadata
|
|
49
|
-
removals.push(defs[name].key);
|
|
50
|
-
}
|
|
51
|
-
defs[name] = { def, index, key };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
// this is not a known entry, so we will just apply it
|
|
56
|
-
defs[key] = { def, index: 0, key };
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
for (const key of removals) {
|
|
60
|
-
await store.remove(key);
|
|
61
|
-
}
|
|
62
|
-
Object.values(defs).forEach(({ def }) => addMetadata(def));
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
export default class State {
|
|
66
|
-
#authUrls = new Map();
|
|
67
|
-
#lastRequestTimestamps = new Map();
|
|
68
|
-
#maxEntries = 10;
|
|
69
|
-
#rateLimitInterval = 3000; // 3 seconds
|
|
70
|
-
#authRequests = {};
|
|
71
|
-
#metaStore = new MetadataStore();
|
|
72
|
-
// Map of providers currently injected in tabs
|
|
73
|
-
#injectedProviders = new Map();
|
|
74
|
-
#metaRequests = {};
|
|
75
|
-
#notification = settings.notification;
|
|
76
|
-
// Map of all providers exposed by the extension, they are retrievable by key
|
|
77
|
-
#providers;
|
|
78
|
-
#signRequests = {};
|
|
79
|
-
#windows = [];
|
|
80
|
-
#connectedTabsUrl = [];
|
|
81
|
-
authSubject = new BehaviorSubject([]);
|
|
82
|
-
metaSubject = new BehaviorSubject([]);
|
|
83
|
-
signSubject = new BehaviorSubject([]);
|
|
84
|
-
authUrlSubjects = {};
|
|
85
|
-
defaultAuthAccountSelection = [];
|
|
86
|
-
constructor(providers = {}, rateLimitInterval = 3000) {
|
|
87
|
-
assert(rateLimitInterval >= 0, 'Expects non-negative number for rateLimitInterval');
|
|
88
|
-
this.#providers = providers;
|
|
89
|
-
this.#rateLimitInterval = rateLimitInterval;
|
|
90
|
-
}
|
|
91
|
-
async init() {
|
|
92
|
-
await extractMetadata(this.#metaStore);
|
|
93
|
-
// retrieve previously set authorizations
|
|
94
|
-
const storageAuthUrls = await chrome.storage.local.get(AUTH_URLS_KEY);
|
|
95
|
-
const authString = storageAuthUrls?.[AUTH_URLS_KEY] || '{}';
|
|
96
|
-
const previousAuth = JSON.parse(authString);
|
|
97
|
-
this.#authUrls = new Map(Object.entries(previousAuth));
|
|
98
|
-
// Initialize authUrlSubjects for each URL
|
|
99
|
-
this.#authUrls.forEach((authInfo, url) => {
|
|
100
|
-
this.authUrlSubjects[url] = new BehaviorSubject(authInfo);
|
|
101
|
-
});
|
|
102
|
-
// retrieve previously set default auth accounts
|
|
103
|
-
const storageDefaultAuthAccounts = await chrome.storage.local.get(DEFAULT_AUTH_ACCOUNTS);
|
|
104
|
-
const defaultAuthString = storageDefaultAuthAccounts?.[DEFAULT_AUTH_ACCOUNTS] || '[]';
|
|
105
|
-
const previousDefaultAuth = JSON.parse(defaultAuthString);
|
|
106
|
-
this.defaultAuthAccountSelection = previousDefaultAuth;
|
|
107
|
-
}
|
|
108
|
-
get knownMetadata() {
|
|
109
|
-
return knownMetadata();
|
|
110
|
-
}
|
|
111
|
-
get numAuthRequests() {
|
|
112
|
-
return Object.keys(this.#authRequests).length;
|
|
113
|
-
}
|
|
114
|
-
get numMetaRequests() {
|
|
115
|
-
return Object.keys(this.#metaRequests).length;
|
|
116
|
-
}
|
|
117
|
-
get numSignRequests() {
|
|
118
|
-
return Object.keys(this.#signRequests).length;
|
|
119
|
-
}
|
|
120
|
-
get allAuthRequests() {
|
|
121
|
-
return Object
|
|
122
|
-
.values(this.#authRequests)
|
|
123
|
-
.map(({ id, request, url }) => ({ id, request, url }));
|
|
124
|
-
}
|
|
125
|
-
get allMetaRequests() {
|
|
126
|
-
return Object
|
|
127
|
-
.values(this.#metaRequests)
|
|
128
|
-
.map(({ id, request, url }) => ({ id, request, url }));
|
|
129
|
-
}
|
|
130
|
-
get allSignRequests() {
|
|
131
|
-
return Object
|
|
132
|
-
.values(this.#signRequests)
|
|
133
|
-
.map(({ account, id, request, url }) => ({ account, id, request, url }));
|
|
134
|
-
}
|
|
135
|
-
get authUrls() {
|
|
136
|
-
return Object.fromEntries(this.#authUrls);
|
|
137
|
-
}
|
|
138
|
-
popupClose() {
|
|
139
|
-
this.#windows.forEach((id) => withErrorLog(() => chrome.windows.remove(id)));
|
|
140
|
-
this.#windows = [];
|
|
141
|
-
}
|
|
142
|
-
popupOpen() {
|
|
143
|
-
this.#notification !== 'extension' &&
|
|
144
|
-
chrome.windows.create(this.#notification === 'window'
|
|
145
|
-
? NORMAL_WINDOW_OPTS
|
|
146
|
-
: POPUP_WINDOW_OPTS, (window) => {
|
|
147
|
-
if (window) {
|
|
148
|
-
this.#windows.push(window.id || 0);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
authComplete = (id, resolve, reject) => {
|
|
153
|
-
const complete = async (authorizedAccounts = []) => {
|
|
154
|
-
const { idStr, request: { origin }, url } = this.#authRequests[id];
|
|
155
|
-
const strippedUrl = this.stripUrl(url);
|
|
156
|
-
const authInfo = {
|
|
157
|
-
authorizedAccounts,
|
|
158
|
-
count: 0,
|
|
159
|
-
id: idStr,
|
|
160
|
-
origin,
|
|
161
|
-
url
|
|
162
|
-
};
|
|
163
|
-
this.#authUrls.set(strippedUrl, authInfo);
|
|
164
|
-
if (!this.authUrlSubjects[strippedUrl]) {
|
|
165
|
-
this.authUrlSubjects[strippedUrl] = new BehaviorSubject(authInfo);
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
this.authUrlSubjects[strippedUrl].next(authInfo);
|
|
169
|
-
}
|
|
170
|
-
await this.saveCurrentAuthList();
|
|
171
|
-
await this.updateDefaultAuthAccounts(authorizedAccounts);
|
|
172
|
-
delete this.#authRequests[id];
|
|
173
|
-
this.updateIconAuth(true);
|
|
174
|
-
};
|
|
175
|
-
return {
|
|
176
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
177
|
-
reject: async (error) => {
|
|
178
|
-
if (error.message === 'Cancelled') {
|
|
179
|
-
delete this.#authRequests[id];
|
|
180
|
-
this.updateIconAuth(true);
|
|
181
|
-
reject(new Error('Connection request was cancelled by the user.'));
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
await complete();
|
|
185
|
-
reject(new Error('Connection request was rejected by the user.'));
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
189
|
-
resolve: async ({ authorizedAccounts, result }) => {
|
|
190
|
-
await complete(authorizedAccounts);
|
|
191
|
-
resolve({ authorizedAccounts, result });
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
};
|
|
195
|
-
/**
|
|
196
|
-
* @deprecated This method is deprecated in favor of {@link updateCurrentTabs} and will be removed in a future release.
|
|
197
|
-
*/
|
|
198
|
-
udateCurrentTabsUrl(urls) {
|
|
199
|
-
this.updateCurrentTabsUrl(urls);
|
|
200
|
-
}
|
|
201
|
-
updateCurrentTabsUrl(urls) {
|
|
202
|
-
const connectedTabs = urls.map((url) => {
|
|
203
|
-
let strippedUrl = '';
|
|
204
|
-
// the assert in stripUrl may throw for new tabs with "chrome://newtab/"
|
|
205
|
-
try {
|
|
206
|
-
strippedUrl = this.stripUrl(url);
|
|
207
|
-
}
|
|
208
|
-
catch (e) {
|
|
209
|
-
console.error(e);
|
|
210
|
-
}
|
|
211
|
-
// return the stripped url only if this website is known
|
|
212
|
-
return !!strippedUrl && this.authUrls[strippedUrl]
|
|
213
|
-
? strippedUrl
|
|
214
|
-
: undefined;
|
|
215
|
-
})
|
|
216
|
-
.filter((value) => !!value);
|
|
217
|
-
this.#connectedTabsUrl = connectedTabs;
|
|
218
|
-
}
|
|
219
|
-
getConnectedTabsUrl() {
|
|
220
|
-
return this.#connectedTabsUrl;
|
|
221
|
-
}
|
|
222
|
-
deleteAuthRequest(requestId) {
|
|
223
|
-
delete this.#authRequests[requestId];
|
|
224
|
-
this.updateIconAuth(true);
|
|
225
|
-
}
|
|
226
|
-
async saveCurrentAuthList() {
|
|
227
|
-
await chrome.storage.local.set({ [AUTH_URLS_KEY]: JSON.stringify(Object.fromEntries(this.#authUrls)) });
|
|
228
|
-
}
|
|
229
|
-
async saveDefaultAuthAccounts() {
|
|
230
|
-
await chrome.storage.local.set({ [DEFAULT_AUTH_ACCOUNTS]: JSON.stringify(this.defaultAuthAccountSelection) });
|
|
231
|
-
}
|
|
232
|
-
async updateDefaultAuthAccounts(newList) {
|
|
233
|
-
this.defaultAuthAccountSelection = newList;
|
|
234
|
-
await this.saveDefaultAuthAccounts();
|
|
235
|
-
}
|
|
236
|
-
metaComplete = (id, resolve, reject) => {
|
|
237
|
-
const complete = () => {
|
|
238
|
-
delete this.#metaRequests[id];
|
|
239
|
-
this.updateIconMeta(true);
|
|
240
|
-
};
|
|
241
|
-
return {
|
|
242
|
-
reject: (error) => {
|
|
243
|
-
complete();
|
|
244
|
-
reject(error);
|
|
245
|
-
},
|
|
246
|
-
resolve: (result) => {
|
|
247
|
-
complete();
|
|
248
|
-
resolve(result);
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
};
|
|
252
|
-
signComplete = (id, resolve, reject) => {
|
|
253
|
-
const complete = () => {
|
|
254
|
-
delete this.#signRequests[id];
|
|
255
|
-
this.updateIconSign(true);
|
|
256
|
-
};
|
|
257
|
-
return {
|
|
258
|
-
reject: (error) => {
|
|
259
|
-
complete();
|
|
260
|
-
reject(error);
|
|
261
|
-
},
|
|
262
|
-
resolve: (result) => {
|
|
263
|
-
complete();
|
|
264
|
-
resolve(result);
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
};
|
|
268
|
-
stripUrl(url) {
|
|
269
|
-
try {
|
|
270
|
-
const parsedUrl = new URL(url);
|
|
271
|
-
if (!['http:', 'https:', 'ipfs:', 'ipns:'].includes(parsedUrl.protocol)) {
|
|
272
|
-
throw new Error(`Invalid protocol ${parsedUrl.protocol}`);
|
|
273
|
-
}
|
|
274
|
-
// For ipfs/ipns which don't have a standard origin, we handle it differently.
|
|
275
|
-
if (parsedUrl.protocol === 'ipfs:' || parsedUrl.protocol === 'ipns:') {
|
|
276
|
-
// ipfs://<hash> | ipns://<hash>
|
|
277
|
-
return `${parsedUrl.protocol}//${parsedUrl.hostname}`;
|
|
278
|
-
}
|
|
279
|
-
return parsedUrl.origin;
|
|
280
|
-
}
|
|
281
|
-
catch (e) {
|
|
282
|
-
console.error(e);
|
|
283
|
-
throw new Error('Invalid URL');
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
updateIcon(shouldClose) {
|
|
287
|
-
const authCount = this.numAuthRequests;
|
|
288
|
-
const metaCount = this.numMetaRequests;
|
|
289
|
-
const signCount = this.numSignRequests;
|
|
290
|
-
const text = (authCount
|
|
291
|
-
? 'Auth'
|
|
292
|
-
: metaCount
|
|
293
|
-
? 'Meta'
|
|
294
|
-
: (signCount ? `${signCount}` : ''));
|
|
295
|
-
withErrorLog(() => chrome.action.setBadgeText({ text }));
|
|
296
|
-
if (shouldClose && text === '') {
|
|
297
|
-
this.popupClose();
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
async removeAuthorization(url) {
|
|
301
|
-
const entry = this.#authUrls.get(url);
|
|
302
|
-
assert(entry, `The source ${url} is not known`);
|
|
303
|
-
this.#authUrls.delete(url);
|
|
304
|
-
await this.saveCurrentAuthList();
|
|
305
|
-
if (this.authUrlSubjects[url]) {
|
|
306
|
-
entry.authorizedAccounts = [];
|
|
307
|
-
this.authUrlSubjects[url].next(entry);
|
|
308
|
-
}
|
|
309
|
-
return this.authUrls;
|
|
310
|
-
}
|
|
311
|
-
updateIconAuth(shouldClose) {
|
|
312
|
-
this.authSubject.next(this.allAuthRequests);
|
|
313
|
-
this.updateIcon(shouldClose);
|
|
314
|
-
}
|
|
315
|
-
updateIconMeta(shouldClose) {
|
|
316
|
-
this.metaSubject.next(this.allMetaRequests);
|
|
317
|
-
this.updateIcon(shouldClose);
|
|
318
|
-
}
|
|
319
|
-
updateIconSign(shouldClose) {
|
|
320
|
-
this.signSubject.next(this.allSignRequests);
|
|
321
|
-
this.updateIcon(shouldClose);
|
|
322
|
-
}
|
|
323
|
-
async updateAuthorizedAccounts(authorizedAccountsDiff) {
|
|
324
|
-
authorizedAccountsDiff.forEach(([url, authorizedAccountDiff]) => {
|
|
325
|
-
const authInfo = this.#authUrls.get(url);
|
|
326
|
-
if (authInfo) {
|
|
327
|
-
authInfo.authorizedAccounts = authorizedAccountDiff;
|
|
328
|
-
this.#authUrls.set(url, authInfo);
|
|
329
|
-
this.authUrlSubjects[url].next(authInfo);
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
await this.saveCurrentAuthList();
|
|
333
|
-
}
|
|
334
|
-
async authorizeUrl(url, request) {
|
|
335
|
-
const idStr = this.stripUrl(url);
|
|
336
|
-
// Do not enqueue duplicate authorization requests.
|
|
337
|
-
const isDuplicate = Object
|
|
338
|
-
.values(this.#authRequests)
|
|
339
|
-
.some((request) => request.idStr === idStr);
|
|
340
|
-
assert(!isDuplicate, `The source ${url} has a pending authorization request`);
|
|
341
|
-
if (this.#authUrls.has(idStr)) {
|
|
342
|
-
// this url was seen in the past
|
|
343
|
-
const authInfo = this.#authUrls.get(idStr);
|
|
344
|
-
assert(authInfo?.authorizedAccounts || authInfo?.isAllowed, `The source ${url} is not allowed to interact with this extension`);
|
|
345
|
-
return {
|
|
346
|
-
authorizedAccounts: [],
|
|
347
|
-
result: false
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
return new Promise((resolve, reject) => {
|
|
351
|
-
const id = getId();
|
|
352
|
-
this.#authRequests[id] = {
|
|
353
|
-
...this.authComplete(id, resolve, reject),
|
|
354
|
-
id,
|
|
355
|
-
idStr,
|
|
356
|
-
request,
|
|
357
|
-
url
|
|
358
|
-
};
|
|
359
|
-
this.updateIconAuth();
|
|
360
|
-
this.popupOpen();
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
ensureUrlAuthorized(url) {
|
|
364
|
-
const entry = this.#authUrls.get(this.stripUrl(url));
|
|
365
|
-
assert(entry, `The source ${url} has not been enabled yet`);
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
injectMetadata(url, request) {
|
|
369
|
-
return new Promise((resolve, reject) => {
|
|
370
|
-
const id = getId();
|
|
371
|
-
this.#metaRequests[id] = {
|
|
372
|
-
...this.metaComplete(id, resolve, reject),
|
|
373
|
-
id,
|
|
374
|
-
request,
|
|
375
|
-
url
|
|
376
|
-
};
|
|
377
|
-
this.updateIconMeta();
|
|
378
|
-
this.popupOpen();
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
getAuthRequest(id) {
|
|
382
|
-
return this.#authRequests[id];
|
|
383
|
-
}
|
|
384
|
-
getMetaRequest(id) {
|
|
385
|
-
return this.#metaRequests[id];
|
|
386
|
-
}
|
|
387
|
-
getSignRequest(id) {
|
|
388
|
-
return this.#signRequests[id];
|
|
389
|
-
}
|
|
390
|
-
// List all providers the extension is exposing
|
|
391
|
-
rpcListProviders() {
|
|
392
|
-
return Promise.resolve(Object.keys(this.#providers).reduce((acc, key) => {
|
|
393
|
-
acc[key] = this.#providers[key].meta;
|
|
394
|
-
return acc;
|
|
395
|
-
}, {}));
|
|
396
|
-
}
|
|
397
|
-
rpcSend(request, port) {
|
|
398
|
-
const provider = this.#injectedProviders.get(port);
|
|
399
|
-
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
|
|
400
|
-
return provider.send(request.method, request.params);
|
|
401
|
-
}
|
|
402
|
-
// Start a provider, return its meta
|
|
403
|
-
rpcStartProvider(key, port) {
|
|
404
|
-
assert(Object.keys(this.#providers).includes(key), `Provider ${key} is not exposed by extension`);
|
|
405
|
-
if (this.#injectedProviders.get(port)) {
|
|
406
|
-
return Promise.resolve(this.#providers[key].meta);
|
|
407
|
-
}
|
|
408
|
-
// Instantiate the provider
|
|
409
|
-
this.#injectedProviders.set(port, this.#providers[key].start());
|
|
410
|
-
// Close provider connection when page is closed
|
|
411
|
-
port.onDisconnect.addListener(() => {
|
|
412
|
-
const provider = this.#injectedProviders.get(port);
|
|
413
|
-
if (provider) {
|
|
414
|
-
withErrorLog(() => provider.disconnect());
|
|
415
|
-
}
|
|
416
|
-
this.#injectedProviders.delete(port);
|
|
417
|
-
});
|
|
418
|
-
return Promise.resolve(this.#providers[key].meta);
|
|
419
|
-
}
|
|
420
|
-
rpcSubscribe({ method, params, type }, cb, port) {
|
|
421
|
-
const provider = this.#injectedProviders.get(port);
|
|
422
|
-
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
|
|
423
|
-
return provider.subscribe(type, method, params, cb);
|
|
424
|
-
}
|
|
425
|
-
rpcSubscribeConnected(_request, cb, port) {
|
|
426
|
-
const provider = this.#injectedProviders.get(port);
|
|
427
|
-
assert(provider, 'Cannot call pub(rpc.subscribeConnected) before provider is set');
|
|
428
|
-
cb(null, provider.isConnected); // Immediately send back current isConnected
|
|
429
|
-
provider.on('connected', () => cb(null, true));
|
|
430
|
-
provider.on('disconnected', () => cb(null, false));
|
|
431
|
-
}
|
|
432
|
-
rpcUnsubscribe(request, port) {
|
|
433
|
-
const provider = this.#injectedProviders.get(port);
|
|
434
|
-
assert(provider, 'Cannot call pub(rpc.unsubscribe) before provider is set');
|
|
435
|
-
return provider.unsubscribe(request.type, request.method, request.subscriptionId);
|
|
436
|
-
}
|
|
437
|
-
async saveMetadata(meta) {
|
|
438
|
-
await this.#metaStore.set(meta.genesisHash, meta);
|
|
439
|
-
addMetadata(meta);
|
|
440
|
-
}
|
|
441
|
-
setNotification(notification) {
|
|
442
|
-
this.#notification = notification;
|
|
443
|
-
return true;
|
|
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
|
-
}
|
|
458
|
-
sign(url, request, account) {
|
|
459
|
-
const id = getId();
|
|
460
|
-
try {
|
|
461
|
-
this.handleSignRequest(url);
|
|
462
|
-
}
|
|
463
|
-
catch (error) {
|
|
464
|
-
return Promise.reject(error);
|
|
465
|
-
}
|
|
466
|
-
return new Promise((resolve, reject) => {
|
|
467
|
-
this.#signRequests[id] = {
|
|
468
|
-
...this.signComplete(id, resolve, reject),
|
|
469
|
-
account,
|
|
470
|
-
id,
|
|
471
|
-
request,
|
|
472
|
-
url
|
|
473
|
-
};
|
|
474
|
-
this.updateIconSign();
|
|
475
|
-
this.popupOpen();
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { MessageTypes, RequestTypes, ResponseTypes } from '../types.js';
|
|
2
|
-
import type State from './State.js';
|
|
3
|
-
export default class Tabs {
|
|
4
|
-
#private;
|
|
5
|
-
constructor(state: State);
|
|
6
|
-
private filterForAuthorizedAccounts;
|
|
7
|
-
private authorize;
|
|
8
|
-
private accountsListAuthorized;
|
|
9
|
-
private accountsSubscribeAuthorized;
|
|
10
|
-
private accountsUnsubscribe;
|
|
11
|
-
private getSigningPair;
|
|
12
|
-
private bytesSign;
|
|
13
|
-
private extrinsicSign;
|
|
14
|
-
private metadataProvide;
|
|
15
|
-
private metadataList;
|
|
16
|
-
private rpcListProviders;
|
|
17
|
-
private rpcSend;
|
|
18
|
-
private rpcStartProvider;
|
|
19
|
-
private rpcSubscribe;
|
|
20
|
-
private rpcSubscribeConnected;
|
|
21
|
-
private rpcUnsubscribe;
|
|
22
|
-
private redirectPhishingLanding;
|
|
23
|
-
private redirectIfPhishing;
|
|
24
|
-
handle<TMessageType extends MessageTypes>(id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port?: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]>;
|
|
25
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { combineLatest } from 'rxjs';
|
|
2
|
-
import { checkIfDenied } from '@pezkuwi/phishing';
|
|
3
|
-
import { keyring } from '@pezkuwi/ui-keyring';
|
|
4
|
-
import { accounts as accountsObservable } from '@pezkuwi/ui-keyring/observable/accounts';
|
|
5
|
-
import { assert, isNumber } from '@pezkuwi/util';
|
|
6
|
-
import { PHISHING_PAGE_REDIRECT } from '../../defaults.js';
|
|
7
|
-
import { canDerive } from '../../utils/index.js';
|
|
8
|
-
import RequestBytesSign from '../RequestBytesSign.js';
|
|
9
|
-
import RequestExtrinsicSign from '../RequestExtrinsicSign.js';
|
|
10
|
-
import { withErrorLog } from './helpers.js';
|
|
11
|
-
import { createSubscription, unsubscribe } from './subscriptions.js';
|
|
12
|
-
function transformAccounts(accounts, anyType = false) {
|
|
13
|
-
return Object
|
|
14
|
-
.values(accounts)
|
|
15
|
-
.filter(({ json: { meta: { isHidden } } }) => !isHidden)
|
|
16
|
-
.filter(({ type }) => anyType ? true : canDerive(type))
|
|
17
|
-
.sort((a, b) => (a.json.meta.whenCreated || 0) - (b.json.meta.whenCreated || 0))
|
|
18
|
-
.map(({ json: { address, meta: { genesisHash, name } }, type }) => ({
|
|
19
|
-
address,
|
|
20
|
-
genesisHash,
|
|
21
|
-
name,
|
|
22
|
-
type
|
|
23
|
-
}));
|
|
24
|
-
}
|
|
25
|
-
export default class Tabs {
|
|
26
|
-
#accountSubs = {};
|
|
27
|
-
#state;
|
|
28
|
-
constructor(state) {
|
|
29
|
-
this.#state = state;
|
|
30
|
-
}
|
|
31
|
-
filterForAuthorizedAccounts(accounts, url) {
|
|
32
|
-
const auth = this.#state.authUrls[this.#state.stripUrl(url)];
|
|
33
|
-
if (!auth) {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
return accounts.filter((allAcc) => auth.authorizedAccounts
|
|
37
|
-
// we have a list, use it
|
|
38
|
-
? auth.authorizedAccounts.includes(allAcc.address)
|
|
39
|
-
// if no authorizedAccounts and isAllowed return all - these are old converted urls
|
|
40
|
-
: auth.isAllowed);
|
|
41
|
-
}
|
|
42
|
-
authorize(url, request) {
|
|
43
|
-
return this.#state.authorizeUrl(url, request);
|
|
44
|
-
}
|
|
45
|
-
accountsListAuthorized(url, { anyType }) {
|
|
46
|
-
const transformedAccounts = transformAccounts(accountsObservable.subject.getValue(), anyType);
|
|
47
|
-
return this.filterForAuthorizedAccounts(transformedAccounts, url);
|
|
48
|
-
}
|
|
49
|
-
accountsSubscribeAuthorized(url, id, port) {
|
|
50
|
-
const cb = createSubscription(id, port);
|
|
51
|
-
const strippedUrl = this.#state.stripUrl(url);
|
|
52
|
-
const authUrlObservable = this.#state.authUrlSubjects[strippedUrl]?.asObservable();
|
|
53
|
-
if (!authUrlObservable) {
|
|
54
|
-
console.error(`No authUrlSubject found for ${strippedUrl}`);
|
|
55
|
-
return id;
|
|
56
|
-
}
|
|
57
|
-
this.#accountSubs[id] = {
|
|
58
|
-
subscription: combineLatest([accountsObservable.subject, authUrlObservable]).subscribe(([accounts, _authUrlInfo]) => {
|
|
59
|
-
const transformedAccounts = transformAccounts(accounts);
|
|
60
|
-
cb(this.filterForAuthorizedAccounts(transformedAccounts, url));
|
|
61
|
-
}),
|
|
62
|
-
url
|
|
63
|
-
};
|
|
64
|
-
port.onDisconnect.addListener(() => {
|
|
65
|
-
this.accountsUnsubscribe(url, { id });
|
|
66
|
-
});
|
|
67
|
-
return id;
|
|
68
|
-
}
|
|
69
|
-
accountsUnsubscribe(url, { id }) {
|
|
70
|
-
const sub = this.#accountSubs[id];
|
|
71
|
-
if (!sub || sub.url !== url) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
delete this.#accountSubs[id];
|
|
75
|
-
unsubscribe(id);
|
|
76
|
-
sub.subscription.unsubscribe();
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
getSigningPair(address) {
|
|
80
|
-
const pair = keyring.getPair(address);
|
|
81
|
-
assert(pair, 'Unable to find keypair');
|
|
82
|
-
return pair;
|
|
83
|
-
}
|
|
84
|
-
bytesSign(url, request) {
|
|
85
|
-
const address = request.address;
|
|
86
|
-
const pair = this.getSigningPair(address);
|
|
87
|
-
return this.#state.sign(url, new RequestBytesSign(request), { address, ...pair.meta });
|
|
88
|
-
}
|
|
89
|
-
extrinsicSign(url, request) {
|
|
90
|
-
const address = request.address;
|
|
91
|
-
const pair = this.getSigningPair(address);
|
|
92
|
-
return this.#state.sign(url, new RequestExtrinsicSign(request), { address, ...pair.meta });
|
|
93
|
-
}
|
|
94
|
-
metadataProvide(url, request) {
|
|
95
|
-
return this.#state.injectMetadata(url, request);
|
|
96
|
-
}
|
|
97
|
-
metadataList(_url) {
|
|
98
|
-
return this.#state.knownMetadata.map(({ genesisHash, specVersion }) => ({
|
|
99
|
-
genesisHash,
|
|
100
|
-
specVersion
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
rpcListProviders() {
|
|
104
|
-
return this.#state.rpcListProviders();
|
|
105
|
-
}
|
|
106
|
-
rpcSend(request, port) {
|
|
107
|
-
return this.#state.rpcSend(request, port);
|
|
108
|
-
}
|
|
109
|
-
rpcStartProvider(key, port) {
|
|
110
|
-
return this.#state.rpcStartProvider(key, port);
|
|
111
|
-
}
|
|
112
|
-
async rpcSubscribe(request, id, port) {
|
|
113
|
-
const innerCb = createSubscription(id, port);
|
|
114
|
-
const cb = (_error, data) => innerCb(data);
|
|
115
|
-
const subscriptionId = await this.#state.rpcSubscribe(request, cb, port);
|
|
116
|
-
port.onDisconnect.addListener(() => {
|
|
117
|
-
unsubscribe(id);
|
|
118
|
-
withErrorLog(() => this.rpcUnsubscribe({ ...request, subscriptionId }, port));
|
|
119
|
-
});
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
rpcSubscribeConnected(request, id, port) {
|
|
123
|
-
const innerCb = createSubscription(id, port);
|
|
124
|
-
const cb = (_error, data) => innerCb(data);
|
|
125
|
-
this.#state.rpcSubscribeConnected(request, cb, port);
|
|
126
|
-
port.onDisconnect.addListener(() => {
|
|
127
|
-
unsubscribe(id);
|
|
128
|
-
});
|
|
129
|
-
return Promise.resolve(true);
|
|
130
|
-
}
|
|
131
|
-
async rpcUnsubscribe(request, port) {
|
|
132
|
-
return this.#state.rpcUnsubscribe(request, port);
|
|
133
|
-
}
|
|
134
|
-
redirectPhishingLanding(phishingWebsite) {
|
|
135
|
-
const nonFragment = phishingWebsite.split('#')[0];
|
|
136
|
-
const encodedWebsite = encodeURIComponent(nonFragment);
|
|
137
|
-
const url = `${chrome.runtime.getURL('index.html')}#${PHISHING_PAGE_REDIRECT}/${encodedWebsite}`;
|
|
138
|
-
chrome.tabs.query({ url: nonFragment }, (tabs) => {
|
|
139
|
-
tabs
|
|
140
|
-
.map(({ id }) => id)
|
|
141
|
-
.filter((id) => isNumber(id))
|
|
142
|
-
.forEach((id) => withErrorLog(() => chrome.tabs.update(id, { url })));
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
async redirectIfPhishing(url) {
|
|
146
|
-
const isInDenyList = await checkIfDenied(url);
|
|
147
|
-
if (isInDenyList) {
|
|
148
|
-
this.redirectPhishingLanding(url);
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
async handle(id, type, request, url, port) {
|
|
154
|
-
if (type === 'pub(phishing.redirectIfDenied)') {
|
|
155
|
-
return this.redirectIfPhishing(url);
|
|
156
|
-
}
|
|
157
|
-
if (type !== 'pub(authorize.tab)') {
|
|
158
|
-
this.#state.ensureUrlAuthorized(url);
|
|
159
|
-
}
|
|
160
|
-
switch (type) {
|
|
161
|
-
case 'pub(authorize.tab)':
|
|
162
|
-
return this.authorize(url, request);
|
|
163
|
-
case 'pub(accounts.list)':
|
|
164
|
-
return this.accountsListAuthorized(url, request);
|
|
165
|
-
case 'pub(accounts.subscribe)':
|
|
166
|
-
return port && this.accountsSubscribeAuthorized(url, id, port);
|
|
167
|
-
case 'pub(accounts.unsubscribe)':
|
|
168
|
-
return this.accountsUnsubscribe(url, request);
|
|
169
|
-
case 'pub(bytes.sign)':
|
|
170
|
-
return this.bytesSign(url, request);
|
|
171
|
-
case 'pub(extrinsic.sign)':
|
|
172
|
-
return this.extrinsicSign(url, request);
|
|
173
|
-
case 'pub(metadata.list)':
|
|
174
|
-
return this.metadataList(url);
|
|
175
|
-
case 'pub(metadata.provide)':
|
|
176
|
-
return this.metadataProvide(url, request);
|
|
177
|
-
case 'pub(ping)':
|
|
178
|
-
return Promise.resolve(true);
|
|
179
|
-
case 'pub(rpc.listProviders)':
|
|
180
|
-
return this.rpcListProviders();
|
|
181
|
-
case 'pub(rpc.send)':
|
|
182
|
-
return port && this.rpcSend(request, port);
|
|
183
|
-
case 'pub(rpc.startProvider)':
|
|
184
|
-
return port && this.rpcStartProvider(request, port);
|
|
185
|
-
case 'pub(rpc.subscribe)':
|
|
186
|
-
return port && this.rpcSubscribe(request, id, port);
|
|
187
|
-
case 'pub(rpc.subscribeConnected)':
|
|
188
|
-
return port && this.rpcSubscribeConnected(request, id, port);
|
|
189
|
-
case 'pub(rpc.unsubscribe)':
|
|
190
|
-
return port && this.rpcUnsubscribe(request, port);
|
|
191
|
-
default:
|
|
192
|
-
throw new Error(`Unable to handle message of type ${type}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|