@metamask-previews/permission-log-controller 0.0.0-preview.3fbb6b41 → 0.0.0-preview.57d41d4

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.
@@ -1,6 +1,6 @@
1
1
  import { BaseController, type RestrictedControllerMessenger } from '@metamask/base-controller';
2
2
  import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';
3
- import type { Json, JsonRpcRequest, JsonRpcParams, PendingJsonRpcResponse } from '@metamask/utils';
3
+ import { type Json, type JsonRpcRequest, type JsonRpcParams } from '@metamask/utils';
4
4
  import { LOG_METHOD_TYPES } from './enums';
5
5
  export declare type JsonRpcRequestWithOrigin<Params extends JsonRpcParams = JsonRpcParams> = JsonRpcRequest<Params> & {
6
6
  origin?: string;
@@ -22,14 +22,12 @@ export declare type PermissionActivityLog = {
22
22
  responseTime: number | null;
23
23
  success: boolean | null;
24
24
  };
25
- export declare type PermissionName = string;
26
25
  export declare type PermissionLog = {
27
26
  accounts?: Record<string, number>;
28
- lastApproved: number;
27
+ lastApproved?: number;
29
28
  };
30
- export declare type PermissionEntry = Record<PermissionName, PermissionLog>;
31
- export declare type PermissionOrigin = string;
32
- export declare type PermissionHistory = Record<PermissionOrigin, PermissionEntry>;
29
+ export declare type PermissionEntry = Record<string, PermissionLog>;
30
+ export declare type PermissionHistory = Record<string, PermissionEntry>;
33
31
  /**
34
32
  *
35
33
  * Permission log controller state
@@ -55,32 +53,8 @@ declare const name = "PermissionLogController";
55
53
  * and permissions-related methods.
56
54
  */
57
55
  export declare class PermissionLogController extends BaseController<typeof name, PermissionLogControllerState, PermissionLogControllerMessenger> {
58
- restrictedMethods: Set<string>;
56
+ #private;
59
57
  constructor({ messenger, restrictedMethods, state, }: PermissionLogControllerOptions);
60
- /**
61
- * Get the restricted method activity log.
62
- *
63
- * @returns The activity log.
64
- */
65
- getActivityLog(): PermissionActivityLog[];
66
- /**
67
- * Update the restricted method activity log.
68
- *
69
- * @param logs - The new activity log array.
70
- */
71
- updateActivityLog(logs: PermissionActivityLog[]): void;
72
- /**
73
- * Get the permission history log.
74
- *
75
- * @returns The permissions history log.
76
- */
77
- getHistory(): PermissionHistory;
78
- /**
79
- * Update the permission history log.
80
- *
81
- * @param history - The new permissions history log object.
82
- */
83
- updateHistory(history: PermissionHistory): void;
84
58
  /**
85
59
  * Updates the exposed account history for the given origin.
86
60
  * Sets the 'last seen' time to Date.now() for the given accounts.
@@ -102,66 +76,6 @@ export declare class PermissionLogController extends BaseController<typeof name,
102
76
  * @returns The permissions log middleware.
103
77
  */
104
78
  createMiddleware(): JsonRpcMiddleware<JsonRpcParams, Json>;
105
- /**
106
- * Creates and commits an activity log entry, without response data.
107
- *
108
- * @param request - The request object.
109
- * @param isInternal - Whether the request is internal.
110
- * @returns new added activity entry
111
- */
112
- logRequest(request: JsonRpcRequestWithOrigin, isInternal: boolean): PermissionActivityLog;
113
- /**
114
- * Adds response data to an existing activity log entry.
115
- * Entry assumed already committed (i.e., in the log).
116
- *
117
- * @param entry - The entry to add a response to.
118
- * @param response - The response object.
119
- * @param time - Output from Date.now()
120
- */
121
- logResponse(entry: PermissionActivityLog, response: PendingJsonRpcResponse<Json>, time: number): void;
122
- /**
123
- * Commit a new entry to the activity log.
124
- * Removes the oldest entry from the log if it exceeds the log limit.
125
- *
126
- * @param entry - The activity log entry.
127
- */
128
- commitNewActivity(entry: PermissionActivityLog): void;
129
- /**
130
- * Create new permissions history log entries, if any, and commit them.
131
- *
132
- * @param requestedMethods - The method names corresponding to the requested permissions.
133
- * @param origin - The origin of the permissions request.
134
- * @param result - The permissions request response.result.
135
- * @param time - The time of the request, i.e. Date.now().
136
- * @param isEthRequestAccounts - Whether the permissions request was 'eth_requestAccounts'.
137
- */
138
- logPermissionsHistory(requestedMethods: string[], origin: string, result: Json, time: number, isEthRequestAccounts: boolean): void;
139
- /**
140
- * Commit new entries to the permissions history log.
141
- * Merges the history for the given origin, overwriting existing entries
142
- * with the same key (permission name).
143
- *
144
- * @param origin - The requesting origin.
145
- * @param newEntries - The new entries to commit.
146
- */
147
- commitNewHistory(origin: string, newEntries: Record<PermissionName, Partial<PermissionLog>>): void;
148
- /**
149
- * Get all requested methods from a permissions request.
150
- *
151
- * @param request - The request object.
152
- * @returns The names of the requested permissions.
153
- */
154
- getRequestedMethods(request: JsonRpcRequestWithOrigin<any>): string[] | null;
155
- /**
156
- * Get the permitted accounts from an eth_accounts permissions object.
157
- * Returns an empty array if the permission is not eth_accounts.
158
- *
159
- * @param perm - The permissions object.
160
- * @param perm.parentCapability - The permissions parentCapability.
161
- * @param perm.caveats - The permissions caveats.
162
- * @returns The permitted accounts.
163
- */
164
- getAccountsFromPermission(perm: Permission): string[];
165
79
  }
166
80
  export {};
167
81
  //# sourceMappingURL=PermissionLogController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionLogController.d.ts","sourceRoot":"","sources":["../src/PermissionLogController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PermissionLogController.d.ts","sourceRoot":"","sources":["../src/PermissionLogController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EACL,KAAK,IAAI,EACT,KAAK,cAAc,EACnB,KAAK,aAAa,EAGnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAGL,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAEjB,oBAAY,wBAAwB,CAClC,MAAM,SAAS,aAAa,GAAG,aAAa,IAC1C,cAAc,CAAC,MAAM,CAAC,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,oBAAY,MAAM,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,oBAAY,UAAU,GAAG;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,oBAAY,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,oBAAY,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AACF,oBAAY,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE5D,oBAAY,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,oBAAY,4BAA4B,GAAG;IACzC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,qBAAqB,EAAE,qBAAqB,EAAE,CAAC;CAChD,CAAC;AAEF,oBAAY,8BAA8B,GAAG;IAC3C,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC9C,SAAS,EAAE,gCAAgC,CAAC;CAC7C,CAAC;AAEF,oBAAY,gCAAgC,GAAG,6BAA6B,CAC1E,OAAO,IAAI,EACX,KAAK,EACL,KAAK,EACL,KAAK,EACL,KAAK,CACN,CAAC;AAOF;;GAEG;AACH,QAAA,MAAM,IAAI,4BAA4B,CAAC;AAEvC;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,cAAc,CACzD,OAAO,IAAI,EACX,4BAA4B,EAC5B,gCAAgC,CACjC;;gBAGa,EACV,SAAS,EACT,iBAAiB,EACjB,KAAK,GACN,EAAE,8BAA8B;IAmBjC;;;;;;;;OAQG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAYxD;;;;;;;;;OASG;IACH,gBAAgB,IAAI,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC;CAoS3D"}
@@ -1,7 +1,20 @@
1
1
  "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ 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");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _PermissionLogController_instances, _PermissionLogController_restrictedMethods, _PermissionLogController_getAccountToTimeMap, _PermissionLogController_logRequest, _PermissionLogController_logResponse, _PermissionLogController_logPermissionsHistory, _PermissionLogController_commitNewHistory, _PermissionLogController_getRequestedMethods, _PermissionLogController_getAccountsFromPermission;
2
14
  Object.defineProperty(exports, "__esModule", { value: true });
3
15
  exports.PermissionLogController = void 0;
4
16
  const base_controller_1 = require("@metamask/base-controller");
17
+ const utils_1 = require("@metamask/utils");
5
18
  const enums_1 = require("./enums");
6
19
  const defaultState = {
7
20
  permissionHistory: {},
@@ -32,43 +45,9 @@ class PermissionLogController extends base_controller_1.BaseController {
32
45
  },
33
46
  state: Object.assign(Object.assign({}, defaultState), state),
34
47
  });
35
- this.restrictedMethods = restrictedMethods;
36
- }
37
- /**
38
- * Get the restricted method activity log.
39
- *
40
- * @returns The activity log.
41
- */
42
- getActivityLog() {
43
- return this.state.permissionActivityLog;
44
- }
45
- /**
46
- * Update the restricted method activity log.
47
- *
48
- * @param logs - The new activity log array.
49
- */
50
- updateActivityLog(logs) {
51
- this.update((state) => {
52
- state.permissionActivityLog = logs;
53
- });
54
- }
55
- /**
56
- * Get the permission history log.
57
- *
58
- * @returns The permissions history log.
59
- */
60
- getHistory() {
61
- return this.state.permissionHistory;
62
- }
63
- /**
64
- * Update the permission history log.
65
- *
66
- * @param history - The new permissions history log object.
67
- */
68
- updateHistory(history) {
69
- this.update((state) => {
70
- state.permissionHistory = history;
71
- });
48
+ _PermissionLogController_instances.add(this);
49
+ _PermissionLogController_restrictedMethods.set(this, void 0);
50
+ __classPrivateFieldSet(this, _PermissionLogController_restrictedMethods, restrictedMethods, "f");
72
51
  }
73
52
  /**
74
53
  * Updates the exposed account history for the given origin.
@@ -83,12 +62,12 @@ class PermissionLogController extends base_controller_1.BaseController {
83
62
  if (accounts.length === 0) {
84
63
  return;
85
64
  }
86
- const accountToTimeMap = getAccountToTimeMap(accounts, Date.now());
87
- this.commitNewHistory(origin, {
65
+ const newEntries = {
88
66
  eth_accounts: {
89
- accounts: accountToTimeMap,
67
+ accounts: __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_getAccountToTimeMap).call(this, accounts, Date.now()),
90
68
  },
91
- });
69
+ };
70
+ __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_commitNewHistory).call(this, origin, newEntries);
92
71
  }
93
72
  /**
94
73
  * Create a permissions log middleware. Records permissions activity and history:
@@ -102,238 +81,159 @@ class PermissionLogController extends base_controller_1.BaseController {
102
81
  */
103
82
  createMiddleware() {
104
83
  return (req, res, next) => {
105
- let activityEntry, requestedMethods;
106
84
  const { origin, method } = req;
107
85
  const isInternal = method.startsWith(enums_1.WALLET_PREFIX);
108
- // we only log certain methods
109
- if (!enums_1.LOG_IGNORE_METHODS.includes(method) &&
110
- (isInternal || this.restrictedMethods.has(method))) {
111
- activityEntry = this.logRequest(req, isInternal);
112
- if (method === `${enums_1.WALLET_PREFIX}requestPermissions`) {
113
- // get the corresponding methods from the requested permissions so
114
- // that we can record permissions history
115
- requestedMethods = this.getRequestedMethods(req);
116
- }
117
- }
118
- else if (method === 'eth_requestAccounts') {
119
- // eth_requestAccounts is a special case; we need to extract the accounts
120
- // from it
121
- activityEntry = this.logRequest(req, isInternal);
122
- requestedMethods = ['eth_accounts'];
123
- }
124
- else {
125
- // no-op
126
- next();
86
+ const isEthRequestAccounts = method === 'eth_requestAccounts';
87
+ // Determine if the method should be logged
88
+ if ((!enums_1.LOG_IGNORE_METHODS.includes(method) &&
89
+ (isInternal || __classPrivateFieldGet(this, _PermissionLogController_restrictedMethods, "f").has(method))) ||
90
+ isEthRequestAccounts) {
91
+ const activityEntry = __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_logRequest).call(this, req, isInternal);
92
+ const requestedMethods = __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_getRequestedMethods).call(this, req);
93
+ // Call next with a return handler for capturing the response
94
+ next((cb) => {
95
+ const time = Date.now();
96
+ __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_logResponse).call(this, activityEntry, res, time);
97
+ if (requestedMethods && !res.error && res.result && origin) {
98
+ __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_logPermissionsHistory).call(this, requestedMethods, origin, res.result, time, isEthRequestAccounts);
99
+ }
100
+ cb();
101
+ });
127
102
  return;
128
103
  }
129
- // call next with a return handler for capturing the response
130
- next((cb) => {
131
- const time = Date.now();
132
- this.logResponse(activityEntry, res, time);
133
- if (requestedMethods && !res.error && res.result && origin) {
134
- // any permissions or accounts changes will be recorded on the response,
135
- // so we only log permissions history here
136
- this.logPermissionsHistory(requestedMethods, origin, res.result, time, method === 'eth_requestAccounts');
137
- }
138
- cb();
139
- });
104
+ next();
140
105
  };
141
106
  }
142
- /**
143
- * Creates and commits an activity log entry, without response data.
144
- *
145
- * @param request - The request object.
146
- * @param isInternal - Whether the request is internal.
147
- * @returns new added activity entry
148
- */
149
- logRequest(request, isInternal) {
150
- const activityEntry = {
151
- id: request.id,
152
- method: request.method,
153
- methodType: isInternal
154
- ? enums_1.LOG_METHOD_TYPES.internal
155
- : enums_1.LOG_METHOD_TYPES.restricted,
156
- origin: request.origin,
157
- requestTime: Date.now(),
158
- responseTime: null,
159
- success: null,
160
- };
161
- this.commitNewActivity(activityEntry);
162
- return activityEntry;
107
+ }
108
+ exports.PermissionLogController = PermissionLogController;
109
+ _PermissionLogController_restrictedMethods = new WeakMap(), _PermissionLogController_instances = new WeakSet(), _PermissionLogController_getAccountToTimeMap = function _PermissionLogController_getAccountToTimeMap(accounts, time) {
110
+ return accounts.reduce((acc, account) => (Object.assign(Object.assign({}, acc), { [account]: time })), {});
111
+ }, _PermissionLogController_logRequest = function _PermissionLogController_logRequest(request, isInternal) {
112
+ const activityEntry = {
113
+ id: request.id,
114
+ method: request.method,
115
+ methodType: isInternal
116
+ ? enums_1.LOG_METHOD_TYPES.internal
117
+ : enums_1.LOG_METHOD_TYPES.restricted,
118
+ origin: request.origin,
119
+ requestTime: Date.now(),
120
+ responseTime: null,
121
+ success: null,
122
+ };
123
+ this.update((state) => {
124
+ const newLogs = [...state.permissionActivityLog, activityEntry];
125
+ state.permissionActivityLog =
126
+ // remove oldest log if exceeding size limit
127
+ newLogs.length > enums_1.LOG_LIMIT ? newLogs.slice(1) : newLogs;
128
+ });
129
+ return activityEntry;
130
+ }, _PermissionLogController_logResponse = function _PermissionLogController_logResponse(entry, response, time) {
131
+ if (!entry || !response) {
132
+ return;
163
133
  }
164
- /**
165
- * Adds response data to an existing activity log entry.
166
- * Entry assumed already committed (i.e., in the log).
167
- *
168
- * @param entry - The entry to add a response to.
169
- * @param response - The response object.
170
- * @param time - Output from Date.now()
171
- */
172
- logResponse(entry, response, time) {
173
- if (!entry || !response) {
174
- return;
175
- }
176
- // The JSON-RPC 2.0 specification defines "success" by the presence of
177
- // either the "result" or "error" property. The specification forbids
178
- // both properties from being present simultaneously, and our JSON-RPC
179
- // stack is spec-compliant at the time of writing.
180
- this.update((state) => {
181
- const targetPermissionActivyLogIndex = state.permissionActivityLog.findIndex((pActivityLog) => pActivityLog.id === entry.id);
182
- state.permissionActivityLog[targetPermissionActivyLogIndex] = Object.assign(Object.assign({}, entry), { success: Object.hasOwnProperty.call(response, 'result'), responseTime: time });
134
+ // The JSON-RPC 2.0 specification defines "success" by the presence of
135
+ // either the "result" or "error" property. The specification forbids
136
+ // both properties from being present simultaneously, and our JSON-RPC
137
+ // stack is spec-compliant at the time of writing.
138
+ this.update((state) => {
139
+ state.permissionActivityLog = state.permissionActivityLog.map((log) => {
140
+ // Update the log entry that matches the given entry id
141
+ if (log.id === entry.id) {
142
+ return Object.assign(Object.assign({}, log), { success: (0, utils_1.hasProperty)(response, 'result'), responseTime: time });
143
+ }
144
+ return log;
183
145
  });
146
+ });
147
+ }, _PermissionLogController_logPermissionsHistory = function _PermissionLogController_logPermissionsHistory(requestedMethods, origin, result, time, isEthRequestAccounts) {
148
+ let newEntries;
149
+ if (isEthRequestAccounts) {
150
+ // Type assertion: We are assuming that the response data contains
151
+ // a set of accounts if the RPC method is "eth_requestAccounts".
152
+ const accounts = result;
153
+ newEntries = {
154
+ eth_accounts: {
155
+ accounts: __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_getAccountToTimeMap).call(this, accounts, time),
156
+ lastApproved: time,
157
+ },
158
+ };
184
159
  }
185
- /**
186
- * Commit a new entry to the activity log.
187
- * Removes the oldest entry from the log if it exceeds the log limit.
188
- *
189
- * @param entry - The activity log entry.
190
- */
191
- commitNewActivity(entry) {
192
- const logs = [...this.getActivityLog(), entry];
193
- // remove oldest log if exceeding size limit
194
- if (logs.length > enums_1.LOG_LIMIT) {
195
- logs.shift();
196
- }
197
- this.updateActivityLog(logs);
198
- }
199
- /**
200
- * Create new permissions history log entries, if any, and commit them.
201
- *
202
- * @param requestedMethods - The method names corresponding to the requested permissions.
203
- * @param origin - The origin of the permissions request.
204
- * @param result - The permissions request response.result.
205
- * @param time - The time of the request, i.e. Date.now().
206
- * @param isEthRequestAccounts - Whether the permissions request was 'eth_requestAccounts'.
207
- */
208
- logPermissionsHistory(requestedMethods, origin, result, time, isEthRequestAccounts) {
209
- let accounts;
210
- let newEntries;
211
- if (isEthRequestAccounts) {
212
- // Type assertion: We are assuming that the response data contains
213
- // a set of accounts if the RPC method is "eth_requestAccounts".
214
- accounts = result;
215
- const accountToTimeMap = getAccountToTimeMap(accounts, time);
216
- newEntries = {
217
- eth_accounts: {
218
- accounts: accountToTimeMap,
219
- lastApproved: time,
220
- },
221
- };
222
- }
223
- else {
224
- // Records new "lastApproved" times for the granted permissions, if any.
225
- // Special handling for eth_accounts, in order to record the time the
226
- // accounts were last seen or approved by the origin.
227
- // Type assertion: We are assuming that the response data contains
228
- // a set of permissions if the RPC method is "eth_requestPermissions".
229
- newEntries = result
230
- .map((perm) => {
231
- if (perm.parentCapability === 'eth_accounts') {
232
- accounts = this.getAccountsFromPermission(perm);
233
- }
234
- return perm.parentCapability;
235
- })
236
- .reduce((acc, method) => {
237
- // all approved permissions will be included in the response,
238
- // not just the newly requested ones
239
- if (requestedMethods.includes(method)) {
240
- if (method === 'eth_accounts') {
241
- const accountToTimeMap = getAccountToTimeMap(accounts, time);
242
- acc[method] = {
243
- lastApproved: time,
244
- accounts: accountToTimeMap,
245
- };
246
- }
247
- else {
248
- acc[method] = { lastApproved: time };
249
- }
250
- }
160
+ else {
161
+ // Records new "lastApproved" times for the granted permissions, if any.
162
+ // Special handling for eth_accounts, in order to record the time the
163
+ // accounts were last seen or approved by the origin.
164
+ // Type assertion: We are assuming that the response data contains
165
+ // a set of permissions if the RPC method is "eth_requestPermissions".
166
+ const permissions = result;
167
+ newEntries = permissions.reduce((acc, permission) => {
168
+ const method = permission.parentCapability;
169
+ if (!requestedMethods.includes(method)) {
251
170
  return acc;
252
- }, {});
253
- }
254
- if (Object.keys(newEntries).length > 0) {
255
- this.commitNewHistory(origin, newEntries);
256
- }
171
+ }
172
+ if (method === 'eth_accounts') {
173
+ const accounts = __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_getAccountsFromPermission).call(this, permission);
174
+ return Object.assign(Object.assign({}, acc), { [method]: {
175
+ lastApproved: time,
176
+ accounts: __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_getAccountToTimeMap).call(this, accounts, time),
177
+ } });
178
+ }
179
+ return Object.assign(Object.assign({}, acc), { [method]: {
180
+ lastApproved: time,
181
+ } });
182
+ }, {});
257
183
  }
258
- /**
259
- * Commit new entries to the permissions history log.
260
- * Merges the history for the given origin, overwriting existing entries
261
- * with the same key (permission name).
262
- *
263
- * @param origin - The requesting origin.
264
- * @param newEntries - The new entries to commit.
265
- */
266
- commitNewHistory(origin, newEntries) {
267
- var _a;
268
- // a simple merge updates most permissions
269
- const history = this.getHistory();
270
- const newOriginHistory = Object.assign(Object.assign({}, history[origin]), newEntries);
271
- // eth_accounts requires special handling, because of information
272
- // we store about the accounts
273
- const existingEthAccountsEntry = history[origin] && history[origin].eth_accounts;
274
- const newEthAccountsEntry = newEntries.eth_accounts;
275
- if (existingEthAccountsEntry && newEthAccountsEntry) {
276
- // we may intend to update just the accounts, not the permission
277
- // itself
278
- const lastApproved = (_a = newEthAccountsEntry.lastApproved) !== null && _a !== void 0 ? _a : existingEthAccountsEntry.lastApproved;
279
- // merge old and new eth_accounts history entries
280
- newOriginHistory.eth_accounts = {
281
- lastApproved,
282
- accounts: Object.assign(Object.assign({}, existingEthAccountsEntry.accounts), newEthAccountsEntry.accounts),
283
- };
284
- }
285
- this.updateHistory(Object.assign(Object.assign({}, history), { [origin]: newOriginHistory }));
184
+ if (Object.keys(newEntries).length > 0) {
185
+ __classPrivateFieldGet(this, _PermissionLogController_instances, "m", _PermissionLogController_commitNewHistory).call(this, origin, newEntries);
286
186
  }
287
- /**
288
- * Get all requested methods from a permissions request.
289
- *
290
- * @param request - The request object.
291
- * @returns The names of the requested permissions.
292
- */
293
- getRequestedMethods(request) {
294
- if (!request.params ||
295
- !request.params[0] ||
296
- typeof request.params[0] !== 'object' ||
297
- Array.isArray(request.params[0])) {
298
- return null;
299
- }
300
- return Object.keys(request.params[0]);
187
+ }, _PermissionLogController_commitNewHistory = function _PermissionLogController_commitNewHistory(origin, newEntries) {
188
+ var _a, _b;
189
+ const { permissionHistory } = this.state;
190
+ // a simple merge updates most permissions
191
+ const oldOriginHistory = (_a = permissionHistory[origin]) !== null && _a !== void 0 ? _a : {};
192
+ const newOriginHistory = Object.assign(Object.assign({}, oldOriginHistory), newEntries);
193
+ // eth_accounts requires special handling, because of information
194
+ // we store about the accounts
195
+ const existingEthAccountsEntry = oldOriginHistory.eth_accounts;
196
+ const newEthAccountsEntry = newEntries.eth_accounts;
197
+ if (existingEthAccountsEntry && newEthAccountsEntry) {
198
+ // we may intend to update just the accounts, not the permission
199
+ // itself
200
+ const lastApproved = (_b = newEthAccountsEntry.lastApproved) !== null && _b !== void 0 ? _b : existingEthAccountsEntry.lastApproved;
201
+ // merge old and new eth_accounts history entries
202
+ newOriginHistory.eth_accounts = {
203
+ lastApproved,
204
+ accounts: Object.assign(Object.assign({}, existingEthAccountsEntry.accounts), newEthAccountsEntry.accounts),
205
+ };
301
206
  }
302
- /**
303
- * Get the permitted accounts from an eth_accounts permissions object.
304
- * Returns an empty array if the permission is not eth_accounts.
305
- *
306
- * @param perm - The permissions object.
307
- * @param perm.parentCapability - The permissions parentCapability.
308
- * @param perm.caveats - The permissions caveats.
309
- * @returns The permitted accounts.
310
- */
311
- getAccountsFromPermission(perm) {
312
- if (perm.parentCapability !== 'eth_accounts' || !perm.caveats) {
313
- return [];
314
- }
315
- const accounts = new Set();
316
- for (const caveat of perm.caveats) {
317
- if (caveat.type === enums_1.CAVEAT_TYPES.restrictReturnedAccounts &&
318
- Array.isArray(caveat.value)) {
319
- for (const value of caveat.value) {
320
- accounts.add(value);
321
- }
207
+ this.update((state) => {
208
+ state.permissionHistory = Object.assign(Object.assign({}, permissionHistory), { [origin]: newOriginHistory });
209
+ });
210
+ }, _PermissionLogController_getRequestedMethods = function _PermissionLogController_getRequestedMethods(request) {
211
+ const { method, params } = request;
212
+ if (method === 'eth_requestAccounts') {
213
+ return ['eth_accounts'];
214
+ }
215
+ else if (method === `${enums_1.WALLET_PREFIX}requestPermissions` &&
216
+ params &&
217
+ Array.isArray(params) &&
218
+ params[0] &&
219
+ typeof params[0] === 'object' &&
220
+ !Array.isArray(params[0])) {
221
+ return Object.keys(params[0]);
222
+ }
223
+ return null;
224
+ }, _PermissionLogController_getAccountsFromPermission = function _PermissionLogController_getAccountsFromPermission(permission) {
225
+ if (permission.parentCapability !== 'eth_accounts' || !permission.caveats) {
226
+ return [];
227
+ }
228
+ const accounts = new Set();
229
+ for (const caveat of permission.caveats) {
230
+ if (caveat.type === enums_1.CAVEAT_TYPES.restrictReturnedAccounts &&
231
+ Array.isArray(caveat.value)) {
232
+ for (const value of caveat.value) {
233
+ accounts.add(value);
322
234
  }
323
235
  }
324
- return [...accounts];
325
236
  }
326
- }
327
- exports.PermissionLogController = PermissionLogController;
328
- // helper functions
329
- /**
330
- * Get a map from account addresses to the given time.
331
- *
332
- * @param accounts - An array of addresses.
333
- * @param time - A time, e.g. Date.now().
334
- * @returns A string:number map of addresses to time.
335
- */
336
- function getAccountToTimeMap(accounts, time) {
337
- return accounts.reduce((acc, account) => (Object.assign(Object.assign({}, acc), { [account]: time })), {});
338
- }
237
+ return [...accounts];
238
+ };
339
239
  //# sourceMappingURL=PermissionLogController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionLogController.js","sourceRoot":"","sources":["../src/PermissionLogController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseController,\n type RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type {\n Json,\n JsonRpcRequest,\n JsonRpcParams,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\n\nimport {\n LOG_IGNORE_METHODS,\n LOG_LIMIT,\n LOG_METHOD_TYPES,\n WALLET_PREFIX,\n CAVEAT_TYPES,\n} from './enums';\n\nexport type JsonRpcRequestWithOrigin<\n Params extends JsonRpcParams = JsonRpcParams,\n> = JsonRpcRequest<Params> & {\n origin?: string;\n};\n\nexport type Caveat = {\n type: string;\n value: string[];\n};\n\nexport type Permission = {\n parentCapability: string;\n caveats?: Caveat[];\n};\n\nexport type PermissionActivityLog = {\n id: string | number | null;\n method: string;\n methodType: LOG_METHOD_TYPES;\n origin?: string;\n requestTime: number;\n responseTime: number | null;\n success: boolean | null;\n};\n\nexport type PermissionName = string;\nexport type PermissionLog = {\n accounts?: Record<string, number>;\n lastApproved: number;\n};\nexport type PermissionEntry = Record<PermissionName, PermissionLog>;\n\nexport type PermissionOrigin = string;\nexport type PermissionHistory = Record<PermissionOrigin, PermissionEntry>;\n\n/**\n *\n * Permission log controller state\n * @property permissionHistory - permission history\n * @property permissionActivityLog - permission activity logs\n */\nexport type PermissionLogControllerState = {\n permissionHistory: PermissionHistory;\n permissionActivityLog: PermissionActivityLog[];\n};\n\nexport type PermissionLogControllerOptions = {\n restrictedMethods: Set<string>;\n state?: Partial<PermissionLogControllerState>;\n messenger: PermissionLogControllerMessenger;\n};\n\nexport type PermissionLogControllerMessenger = RestrictedControllerMessenger<\n typeof name,\n never,\n never,\n never,\n never\n>;\n\nconst defaultState: PermissionLogControllerState = {\n permissionHistory: {},\n permissionActivityLog: [],\n};\n\n/**\n * The name of the {@link PermissionController}.\n */\nconst name = 'PermissionLogController';\n\n/**\n * Controller with middleware for logging requests and responses to restricted\n * and permissions-related methods.\n */\nexport class PermissionLogController extends BaseController<\n typeof name,\n PermissionLogControllerState,\n PermissionLogControllerMessenger\n> {\n restrictedMethods: Set<string>;\n\n constructor({\n messenger,\n restrictedMethods,\n state,\n }: PermissionLogControllerOptions) {\n super({\n messenger,\n name,\n metadata: {\n permissionHistory: {\n persist: true,\n anonymous: false,\n },\n permissionActivityLog: {\n persist: true,\n anonymous: false,\n },\n },\n state: { ...defaultState, ...state },\n });\n this.restrictedMethods = restrictedMethods;\n }\n\n /**\n * Get the restricted method activity log.\n *\n * @returns The activity log.\n */\n getActivityLog(): PermissionActivityLog[] {\n return this.state.permissionActivityLog;\n }\n\n /**\n * Update the restricted method activity log.\n *\n * @param logs - The new activity log array.\n */\n updateActivityLog(logs: PermissionActivityLog[]) {\n this.update((state) => {\n state.permissionActivityLog = logs;\n });\n }\n\n /**\n * Get the permission history log.\n *\n * @returns The permissions history log.\n */\n getHistory(): PermissionHistory {\n return this.state.permissionHistory;\n }\n\n /**\n * Update the permission history log.\n *\n * @param history - The new permissions history log object.\n */\n updateHistory(history: PermissionHistory) {\n this.update((state) => {\n state.permissionHistory = history;\n });\n }\n\n /**\n * Updates the exposed account history for the given origin.\n * Sets the 'last seen' time to Date.now() for the given accounts.\n * Does **not** update the 'lastApproved' time for the permission itself.\n * Returns if the accounts array is empty.\n *\n * @param origin - The origin that the accounts are exposed to.\n * @param accounts - The accounts.\n */\n updateAccountsHistory(origin: string, accounts: string[]) {\n if (accounts.length === 0) {\n return;\n }\n\n const accountToTimeMap = getAccountToTimeMap(accounts, Date.now());\n\n this.commitNewHistory(origin, {\n eth_accounts: {\n accounts: accountToTimeMap,\n },\n });\n }\n\n /**\n * Create a permissions log middleware. Records permissions activity and history:\n *\n * Activity: requests and responses for restricted and most wallet_ methods.\n *\n * History: for each origin, the last time a permission was granted, including\n * which accounts were exposed, if any.\n *\n * @returns The permissions log middleware.\n */\n createMiddleware(): JsonRpcMiddleware<JsonRpcParams, Json> {\n return (req: JsonRpcRequestWithOrigin, res, next) => {\n let activityEntry: PermissionActivityLog,\n requestedMethods: string[] | null;\n const { origin, method } = req;\n const isInternal = method.startsWith(WALLET_PREFIX);\n\n // we only log certain methods\n if (\n !LOG_IGNORE_METHODS.includes(method) &&\n (isInternal || this.restrictedMethods.has(method))\n ) {\n activityEntry = this.logRequest(req, isInternal);\n\n if (method === `${WALLET_PREFIX}requestPermissions`) {\n // get the corresponding methods from the requested permissions so\n // that we can record permissions history\n requestedMethods = this.getRequestedMethods(req);\n }\n } else if (method === 'eth_requestAccounts') {\n // eth_requestAccounts is a special case; we need to extract the accounts\n // from it\n activityEntry = this.logRequest(req, isInternal);\n requestedMethods = ['eth_accounts'];\n } else {\n // no-op\n next();\n return;\n }\n\n // call next with a return handler for capturing the response\n next((cb) => {\n const time = Date.now();\n this.logResponse(activityEntry, res, time);\n\n if (requestedMethods && !res.error && res.result && origin) {\n // any permissions or accounts changes will be recorded on the response,\n // so we only log permissions history here\n this.logPermissionsHistory(\n requestedMethods,\n origin,\n res.result,\n time,\n method === 'eth_requestAccounts',\n );\n }\n cb();\n });\n };\n }\n\n /**\n * Creates and commits an activity log entry, without response data.\n *\n * @param request - The request object.\n * @param isInternal - Whether the request is internal.\n * @returns new added activity entry\n */\n logRequest(\n request: JsonRpcRequestWithOrigin,\n isInternal: boolean,\n ): PermissionActivityLog {\n const activityEntry: PermissionActivityLog = {\n id: request.id,\n method: request.method,\n methodType: isInternal\n ? LOG_METHOD_TYPES.internal\n : LOG_METHOD_TYPES.restricted,\n origin: request.origin,\n requestTime: Date.now(),\n responseTime: null,\n success: null,\n };\n this.commitNewActivity(activityEntry);\n return activityEntry;\n }\n\n /**\n * Adds response data to an existing activity log entry.\n * Entry assumed already committed (i.e., in the log).\n *\n * @param entry - The entry to add a response to.\n * @param response - The response object.\n * @param time - Output from Date.now()\n */\n logResponse(\n entry: PermissionActivityLog,\n response: PendingJsonRpcResponse<Json>,\n time: number,\n ) {\n if (!entry || !response) {\n return;\n }\n\n // The JSON-RPC 2.0 specification defines \"success\" by the presence of\n // either the \"result\" or \"error\" property. The specification forbids\n // both properties from being present simultaneously, and our JSON-RPC\n // stack is spec-compliant at the time of writing.\n this.update((state) => {\n const targetPermissionActivyLogIndex =\n state.permissionActivityLog.findIndex(\n (pActivityLog) => pActivityLog.id === entry.id,\n );\n state.permissionActivityLog[targetPermissionActivyLogIndex] = {\n ...entry,\n success: Object.hasOwnProperty.call(response, 'result'),\n responseTime: time,\n };\n });\n }\n\n /**\n * Commit a new entry to the activity log.\n * Removes the oldest entry from the log if it exceeds the log limit.\n *\n * @param entry - The activity log entry.\n */\n commitNewActivity(entry: PermissionActivityLog) {\n const logs = [...this.getActivityLog(), entry];\n\n // remove oldest log if exceeding size limit\n if (logs.length > LOG_LIMIT) {\n logs.shift();\n }\n\n this.updateActivityLog(logs);\n }\n\n /**\n * Create new permissions history log entries, if any, and commit them.\n *\n * @param requestedMethods - The method names corresponding to the requested permissions.\n * @param origin - The origin of the permissions request.\n * @param result - The permissions request response.result.\n * @param time - The time of the request, i.e. Date.now().\n * @param isEthRequestAccounts - Whether the permissions request was 'eth_requestAccounts'.\n */\n logPermissionsHistory(\n requestedMethods: string[],\n origin: string,\n result: Json,\n time: number,\n isEthRequestAccounts: boolean,\n ) {\n let accounts: string[];\n let newEntries: PermissionEntry;\n\n if (isEthRequestAccounts) {\n // Type assertion: We are assuming that the response data contains\n // a set of accounts if the RPC method is \"eth_requestAccounts\".\n accounts = result as string[];\n const accountToTimeMap = getAccountToTimeMap(accounts, time);\n\n newEntries = {\n eth_accounts: {\n accounts: accountToTimeMap,\n lastApproved: time,\n },\n };\n } else {\n // Records new \"lastApproved\" times for the granted permissions, if any.\n // Special handling for eth_accounts, in order to record the time the\n // accounts were last seen or approved by the origin.\n\n // Type assertion: We are assuming that the response data contains\n // a set of permissions if the RPC method is \"eth_requestPermissions\".\n newEntries = (result as Permission[])\n .map((perm) => {\n if (perm.parentCapability === 'eth_accounts') {\n accounts = this.getAccountsFromPermission(perm);\n }\n\n return perm.parentCapability;\n })\n .reduce((acc: PermissionEntry, method) => {\n // all approved permissions will be included in the response,\n // not just the newly requested ones\n if (requestedMethods.includes(method)) {\n if (method === 'eth_accounts') {\n const accountToTimeMap = getAccountToTimeMap(accounts, time);\n\n acc[method] = {\n lastApproved: time,\n accounts: accountToTimeMap,\n };\n } else {\n acc[method] = { lastApproved: time };\n }\n }\n\n return acc;\n }, {});\n }\n\n if (Object.keys(newEntries).length > 0) {\n this.commitNewHistory(origin, newEntries);\n }\n }\n\n /**\n * Commit new entries to the permissions history log.\n * Merges the history for the given origin, overwriting existing entries\n * with the same key (permission name).\n *\n * @param origin - The requesting origin.\n * @param newEntries - The new entries to commit.\n */\n commitNewHistory(\n origin: string,\n newEntries: Record<PermissionName, Partial<PermissionLog>>,\n ) {\n // a simple merge updates most permissions\n const history = this.getHistory();\n const newOriginHistory = {\n ...history[origin],\n ...newEntries,\n };\n\n // eth_accounts requires special handling, because of information\n // we store about the accounts\n const existingEthAccountsEntry =\n history[origin] && history[origin].eth_accounts;\n const newEthAccountsEntry = newEntries.eth_accounts;\n\n if (existingEthAccountsEntry && newEthAccountsEntry) {\n // we may intend to update just the accounts, not the permission\n // itself\n const lastApproved =\n newEthAccountsEntry.lastApproved ??\n existingEthAccountsEntry.lastApproved;\n\n // merge old and new eth_accounts history entries\n newOriginHistory.eth_accounts = {\n lastApproved,\n accounts: {\n ...existingEthAccountsEntry.accounts,\n ...newEthAccountsEntry.accounts,\n },\n };\n }\n\n this.updateHistory({\n ...history,\n [origin]: newOriginHistory as PermissionEntry,\n });\n }\n\n /**\n * Get all requested methods from a permissions request.\n *\n * @param request - The request object.\n * @returns The names of the requested permissions.\n */\n getRequestedMethods(request: JsonRpcRequestWithOrigin<any>): string[] | null {\n if (\n !request.params ||\n !request.params[0] ||\n typeof request.params[0] !== 'object' ||\n Array.isArray(request.params[0])\n ) {\n return null;\n }\n return Object.keys(request.params[0]);\n }\n\n /**\n * Get the permitted accounts from an eth_accounts permissions object.\n * Returns an empty array if the permission is not eth_accounts.\n *\n * @param perm - The permissions object.\n * @param perm.parentCapability - The permissions parentCapability.\n * @param perm.caveats - The permissions caveats.\n * @returns The permitted accounts.\n */\n getAccountsFromPermission(perm: Permission): string[] {\n if (perm.parentCapability !== 'eth_accounts' || !perm.caveats) {\n return [];\n }\n\n const accounts = new Set<string>();\n for (const caveat of perm.caveats) {\n if (\n caveat.type === CAVEAT_TYPES.restrictReturnedAccounts &&\n Array.isArray(caveat.value)\n ) {\n for (const value of caveat.value) {\n accounts.add(value);\n }\n }\n }\n return [...accounts];\n }\n}\n\n// helper functions\n\n/**\n * Get a map from account addresses to the given time.\n *\n * @param accounts - An array of addresses.\n * @param time - A time, e.g. Date.now().\n * @returns A string:number map of addresses to time.\n */\nfunction getAccountToTimeMap(\n accounts: string[],\n time: number,\n): Record<string, number> {\n return accounts.reduce((acc, account) => ({ ...acc, [account]: time }), {});\n}\n"]}
1
+ {"version":3,"file":"PermissionLogController.js","sourceRoot":"","sources":["../src/PermissionLogController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseController,\n type RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport {\n type Json,\n type JsonRpcRequest,\n type JsonRpcParams,\n type PendingJsonRpcResponse,\n hasProperty,\n} from '@metamask/utils';\n\nimport {\n LOG_IGNORE_METHODS,\n LOG_LIMIT,\n LOG_METHOD_TYPES,\n WALLET_PREFIX,\n CAVEAT_TYPES,\n} from './enums';\n\nexport type JsonRpcRequestWithOrigin<\n Params extends JsonRpcParams = JsonRpcParams,\n> = JsonRpcRequest<Params> & {\n origin?: string;\n};\n\nexport type Caveat = {\n type: string;\n value: string[];\n};\n\nexport type Permission = {\n parentCapability: string;\n caveats?: Caveat[];\n};\n\nexport type PermissionActivityLog = {\n id: string | number | null;\n method: string;\n methodType: LOG_METHOD_TYPES;\n origin?: string;\n requestTime: number;\n responseTime: number | null;\n success: boolean | null;\n};\n\nexport type PermissionLog = {\n accounts?: Record<string, number>;\n lastApproved?: number;\n};\nexport type PermissionEntry = Record<string, PermissionLog>;\n\nexport type PermissionHistory = Record<string, PermissionEntry>;\n\n/**\n *\n * Permission log controller state\n * @property permissionHistory - permission history\n * @property permissionActivityLog - permission activity logs\n */\nexport type PermissionLogControllerState = {\n permissionHistory: PermissionHistory;\n permissionActivityLog: PermissionActivityLog[];\n};\n\nexport type PermissionLogControllerOptions = {\n restrictedMethods: Set<string>;\n state?: Partial<PermissionLogControllerState>;\n messenger: PermissionLogControllerMessenger;\n};\n\nexport type PermissionLogControllerMessenger = RestrictedControllerMessenger<\n typeof name,\n never,\n never,\n never,\n never\n>;\n\nconst defaultState: PermissionLogControllerState = {\n permissionHistory: {},\n permissionActivityLog: [],\n};\n\n/**\n * The name of the {@link PermissionController}.\n */\nconst name = 'PermissionLogController';\n\n/**\n * Controller with middleware for logging requests and responses to restricted\n * and permissions-related methods.\n */\nexport class PermissionLogController extends BaseController<\n typeof name,\n PermissionLogControllerState,\n PermissionLogControllerMessenger\n> {\n #restrictedMethods: Set<string>;\n\n constructor({\n messenger,\n restrictedMethods,\n state,\n }: PermissionLogControllerOptions) {\n super({\n messenger,\n name,\n metadata: {\n permissionHistory: {\n persist: true,\n anonymous: false,\n },\n permissionActivityLog: {\n persist: true,\n anonymous: false,\n },\n },\n state: { ...defaultState, ...state },\n });\n this.#restrictedMethods = restrictedMethods;\n }\n\n /**\n * Updates the exposed account history for the given origin.\n * Sets the 'last seen' time to Date.now() for the given accounts.\n * Does **not** update the 'lastApproved' time for the permission itself.\n * Returns if the accounts array is empty.\n *\n * @param origin - The origin that the accounts are exposed to.\n * @param accounts - The accounts.\n */\n updateAccountsHistory(origin: string, accounts: string[]) {\n if (accounts.length === 0) {\n return;\n }\n const newEntries = {\n eth_accounts: {\n accounts: this.#getAccountToTimeMap(accounts, Date.now()),\n },\n };\n this.#commitNewHistory(origin, newEntries);\n }\n\n /**\n * Create a permissions log middleware. Records permissions activity and history:\n *\n * Activity: requests and responses for restricted and most wallet_ methods.\n *\n * History: for each origin, the last time a permission was granted, including\n * which accounts were exposed, if any.\n *\n * @returns The permissions log middleware.\n */\n createMiddleware(): JsonRpcMiddleware<JsonRpcParams, Json> {\n return (req: JsonRpcRequestWithOrigin, res, next) => {\n const { origin, method } = req;\n const isInternal = method.startsWith(WALLET_PREFIX);\n const isEthRequestAccounts = method === 'eth_requestAccounts';\n\n // Determine if the method should be logged\n if (\n (!LOG_IGNORE_METHODS.includes(method) &&\n (isInternal || this.#restrictedMethods.has(method))) ||\n isEthRequestAccounts\n ) {\n const activityEntry = this.#logRequest(req, isInternal);\n\n const requestedMethods = this.#getRequestedMethods(req);\n\n // Call next with a return handler for capturing the response\n next((cb) => {\n const time = Date.now();\n this.#logResponse(activityEntry, res, time);\n\n if (requestedMethods && !res.error && res.result && origin) {\n this.#logPermissionsHistory(\n requestedMethods,\n origin,\n res.result,\n time,\n isEthRequestAccounts,\n );\n }\n cb();\n });\n return;\n }\n\n next();\n };\n }\n\n /**\n * Get a map from account addresses to the given time.\n *\n * @param accounts - An array of addresses.\n * @param time - A time, e.g. Date.now().\n * @returns A string:number map of addresses to time.\n */\n #getAccountToTimeMap(\n accounts: string[],\n time: number,\n ): Record<string, number> {\n return accounts.reduce(\n (acc, account) => ({\n ...acc,\n [account]: time,\n }),\n {},\n );\n }\n\n /**\n * Creates and commits an activity log entry, without response data.\n *\n * @param request - The request object.\n * @param isInternal - Whether the request is internal.\n * @returns new added activity entry\n */\n #logRequest(\n request: JsonRpcRequestWithOrigin,\n isInternal: boolean,\n ): PermissionActivityLog {\n const activityEntry: PermissionActivityLog = {\n id: request.id,\n method: request.method,\n methodType: isInternal\n ? LOG_METHOD_TYPES.internal\n : LOG_METHOD_TYPES.restricted,\n origin: request.origin,\n requestTime: Date.now(),\n responseTime: null,\n success: null,\n };\n this.update((state) => {\n const newLogs = [...state.permissionActivityLog, activityEntry];\n state.permissionActivityLog =\n // remove oldest log if exceeding size limit\n newLogs.length > LOG_LIMIT ? newLogs.slice(1) : newLogs;\n });\n return activityEntry;\n }\n\n /**\n * Adds response data to an existing activity log entry.\n * Entry assumed already committed (i.e., in the log).\n *\n * @param entry - The entry to add a response to.\n * @param response - The response object.\n * @param time - Output from Date.now()\n */\n #logResponse(\n entry: PermissionActivityLog,\n response: PendingJsonRpcResponse<Json>,\n time: number,\n ) {\n if (!entry || !response) {\n return;\n }\n\n // The JSON-RPC 2.0 specification defines \"success\" by the presence of\n // either the \"result\" or \"error\" property. The specification forbids\n // both properties from being present simultaneously, and our JSON-RPC\n // stack is spec-compliant at the time of writing.\n this.update((state) => {\n state.permissionActivityLog = state.permissionActivityLog.map((log) => {\n // Update the log entry that matches the given entry id\n if (log.id === entry.id) {\n return {\n ...log,\n success: hasProperty(response, 'result'),\n responseTime: time,\n };\n }\n return log;\n });\n });\n }\n\n /**\n * Create new permissions history log entries, if any, and commit them.\n *\n * @param requestedMethods - The method names corresponding to the requested permissions.\n * @param origin - The origin of the permissions request.\n * @param result - The permissions request response.result.\n * @param time - The time of the request, i.e. Date.now().\n * @param isEthRequestAccounts - Whether the permissions request was 'eth_requestAccounts'.\n */\n #logPermissionsHistory(\n requestedMethods: string[],\n origin: string,\n result: Json,\n time: number,\n isEthRequestAccounts: boolean,\n ) {\n let newEntries: PermissionEntry;\n\n if (isEthRequestAccounts) {\n // Type assertion: We are assuming that the response data contains\n // a set of accounts if the RPC method is \"eth_requestAccounts\".\n const accounts = result as string[];\n newEntries = {\n eth_accounts: {\n accounts: this.#getAccountToTimeMap(accounts, time),\n lastApproved: time,\n },\n };\n } else {\n // Records new \"lastApproved\" times for the granted permissions, if any.\n // Special handling for eth_accounts, in order to record the time the\n // accounts were last seen or approved by the origin.\n // Type assertion: We are assuming that the response data contains\n // a set of permissions if the RPC method is \"eth_requestPermissions\".\n const permissions = result as Permission[];\n newEntries = permissions.reduce((acc: PermissionEntry, permission) => {\n const method = permission.parentCapability;\n\n if (!requestedMethods.includes(method)) {\n return acc;\n }\n\n if (method === 'eth_accounts') {\n const accounts = this.#getAccountsFromPermission(permission);\n return {\n ...acc,\n [method]: {\n lastApproved: time,\n accounts: this.#getAccountToTimeMap(accounts, time),\n },\n };\n }\n\n return {\n ...acc,\n [method]: {\n lastApproved: time,\n },\n };\n }, {});\n }\n\n if (Object.keys(newEntries).length > 0) {\n this.#commitNewHistory(origin, newEntries);\n }\n }\n\n /**\n * Commit new entries to the permissions history log.\n * Merges the history for the given origin, overwriting existing entries\n * with the same key (permission name).\n *\n * @param origin - The requesting origin.\n * @param newEntries - The new entries to commit.\n */\n #commitNewHistory(origin: string, newEntries: PermissionEntry) {\n const { permissionHistory } = this.state;\n\n // a simple merge updates most permissions\n const oldOriginHistory = permissionHistory[origin] ?? {};\n const newOriginHistory = {\n ...oldOriginHistory,\n ...newEntries,\n };\n\n // eth_accounts requires special handling, because of information\n // we store about the accounts\n const existingEthAccountsEntry = oldOriginHistory.eth_accounts;\n const newEthAccountsEntry = newEntries.eth_accounts;\n\n if (existingEthAccountsEntry && newEthAccountsEntry) {\n // we may intend to update just the accounts, not the permission\n // itself\n const lastApproved =\n newEthAccountsEntry.lastApproved ??\n existingEthAccountsEntry.lastApproved;\n\n // merge old and new eth_accounts history entries\n newOriginHistory.eth_accounts = {\n lastApproved,\n accounts: {\n ...existingEthAccountsEntry.accounts,\n ...newEthAccountsEntry.accounts,\n },\n };\n }\n\n this.update((state) => {\n state.permissionHistory = {\n ...permissionHistory,\n [origin]: newOriginHistory,\n };\n });\n }\n\n /**\n * Get all requested methods from a permissions request.\n *\n * @param request - The request object.\n * @returns The names of the requested permissions.\n */\n #getRequestedMethods(request: JsonRpcRequestWithOrigin): string[] | null {\n const { method, params } = request;\n if (method === 'eth_requestAccounts') {\n return ['eth_accounts'];\n } else if (\n method === `${WALLET_PREFIX}requestPermissions` &&\n params &&\n Array.isArray(params) &&\n params[0] &&\n typeof params[0] === 'object' &&\n !Array.isArray(params[0])\n ) {\n return Object.keys(params[0]);\n }\n return null;\n }\n\n /**\n * Get the permitted accounts from an eth_accounts permissions object.\n * Returns an empty array if the permission is not eth_accounts.\n *\n * @param permission - The permissions object.\n * @param permission.parentCapability - The permissions parentCapability.\n * @param permission.caveats - The permissions caveats.\n * @returns The permitted accounts.\n */\n #getAccountsFromPermission(permission: Permission): string[] {\n if (permission.parentCapability !== 'eth_accounts' || !permission.caveats) {\n return [];\n }\n\n const accounts = new Set<string>();\n for (const caveat of permission.caveats) {\n if (\n caveat.type === CAVEAT_TYPES.restrictReturnedAccounts &&\n Array.isArray(caveat.value)\n ) {\n for (const value of caveat.value) {\n accounts.add(value);\n }\n }\n }\n\n return [...accounts];\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/permission-log-controller",
3
- "version": "0.0.0-preview.3fbb6b41",
3
+ "version": "0.0.0-preview.57d41d4",
4
4
  "description": "Controller with middleware for logging requests and responses to restricted and permissions-related methods",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -22,6 +22,7 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "build:docs": "typedoc",
25
+ "changelog:update": "../../scripts/update-changelog.sh @metamask/permission-log-controller",
25
26
  "changelog:validate": "../../scripts/validate-changelog.sh @metamask/permission-log-controller",
26
27
  "publish:preview": "yarn npm publish --tag preview",
27
28
  "test": "jest --reporters=jest-silent-reporter",
@@ -30,19 +31,18 @@
30
31
  "test:watch": "jest --watch"
31
32
  },
32
33
  "dependencies": {
33
- "@metamask/base-controller": "^4.0.0",
34
- "@metamask/json-rpc-engine": "^7.3.0",
35
- "@metamask/utils": "^8.2.0"
34
+ "@metamask/base-controller": "^4.1.0",
35
+ "@metamask/json-rpc-engine": "^7.3.1",
36
+ "@metamask/utils": "^8.3.0"
36
37
  },
37
38
  "devDependencies": {
38
- "@metamask/auto-changelog": "^3.4.3",
39
+ "@metamask/auto-changelog": "^3.4.4",
39
40
  "@types/deep-freeze-strict": "^1.1.0",
40
41
  "@types/jest": "^27.4.1",
41
42
  "deep-freeze-strict": "^1.1.1",
42
43
  "deepmerge": "^4.2.2",
43
44
  "jest": "^27.5.1",
44
45
  "nanoid": "^3.1.31",
45
- "sinon": "^9.2.4",
46
46
  "ts-jest": "^27.1.4",
47
47
  "typedoc": "^0.24.8",
48
48
  "typedoc-plugin-missing-exports": "^2.0.0",