Skip to content

Commit 474b554

Browse files
authored
fix: Simplify endowment handling (#4012)
This removes some dead code. <!-- CURSOR_SUMMARY --> > [!NOTE] > **High Risk** > Removing unhardened global passthrough is a breaking sandbox/API change for any snap that relied on undeclared globals; misconfigured endowment lists will fail at runtime instead of receiving raw host objects. > > **Overview** > Tightens how Snap execution environments resolve endowments by **removing the fallback that exposed raw `rootRealmGlobal` values** for names without a registered factory. Endowments must now come from the common factory registry (or the existing `ethereum` special case); anything else throws **Unknown endowment** instead of silently wiring unhardened globals (and the warning path is dropped). > > Tests follow that model: the unit case that asserted passthrough for arbitrary globals is removed. The iframe browser test for secured events now drives an **`abort` listener via `AbortController`** (with `AbortController` in the snap endowment list) instead of `XMLHttpRequest`, and expectations are corrected for **`currentTarget`** and **`composedPath`** on the event object. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ee3b6e9. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent c539aa0 commit 474b554

3 files changed

Lines changed: 18 additions & 38 deletions

File tree

packages/snaps-controllers/src/services/iframe/IframeExecutionService.test.browser.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -180,27 +180,27 @@ describe('IframeExecutionService', () => {
180180
snapId: MOCK_SNAP_ID,
181181
sourceCode: `
182182
module.exports.onRpcRequest = async ({ request }) => {
183-
let result;
184-
const promise = new Promise((resolve) => {
185-
const xhr = new XMLHttpRequest();
186-
xhr.open('GET', 'https://metamask.io/');
187-
xhr.send();
188-
xhr.onreadystatechange = (ev) => {
189-
result = ev;
190-
resolve();
191-
};
183+
const controller = new AbortController();
184+
let result;
185+
const promise = new Promise((resolve) => {
186+
controller.signal.addEventListener('abort', (ev) => {
187+
result = ev;
188+
resolve();
192189
});
193-
await promise;
194-
195-
return {
196-
targetIsUndefined: result.target === undefined,
197-
currentTargetIsUndefined: result.target === undefined,
198-
srcElementIsUndefined: result.target === undefined,
199-
composedPathIsUndefined: result.target === undefined
200-
};
190+
});
191+
192+
controller.abort();
193+
await promise;
194+
195+
return {
196+
targetIsUndefined: result.target === undefined,
197+
currentTargetIsUndefined: result.currentTarget === undefined,
198+
srcElementIsUndefined: result.srcElement === undefined,
199+
composedPathIsUndefined: result.composedPath === undefined,
200+
};
201201
};
202202
`,
203-
endowments: ['console', 'XMLHttpRequest'],
203+
endowments: ['console', 'AbortController'],
204204
});
205205

206206
const result = await service.handleRpcRequest(MOCK_SNAP_ID, {

packages/snaps-execution-environments/src/common/endowments/index.test.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,6 @@ describe('Endowment utils', () => {
3838
expect(result.endowments.snap).toBe(mockSnapAPI);
3939
});
4040

41-
it('handles special cases where endowment is not available as part of a factory', () => {
42-
const mockEndowment = {};
43-
Object.assign(globalThis, { mockEndowment });
44-
const { endowments } = createEndowments({
45-
...mockOptions,
46-
endowments: ['mockEndowment'],
47-
});
48-
expect(endowments.mockEndowment).toBeDefined();
49-
});
50-
5141
it('handles special case for ethereum endowment', () => {
5242
Object.assign(globalThis, { ethereum: {} });
5343
const { endowments } = createEndowments({

packages/snaps-execution-environments/src/common/endowments/index.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { rpcErrors } from '@metamask/rpc-errors';
22
import type { SnapsEthereumProvider, SnapsProvider } from '@metamask/snaps-sdk';
3-
import { logWarning } from '@metamask/snaps-utils';
43
import { hasProperty } from '@metamask/utils';
54

65
import type {
@@ -9,7 +8,6 @@ import type {
98
NotifyFunction,
109
} from './commonEndowmentFactory';
1110
import buildCommonEndowments from './commonEndowmentFactory';
12-
import { rootRealmGlobal } from '../globalObject';
1311

1412
/**
1513
* Retrieve consolidated endowment factories for common endowments.
@@ -89,14 +87,6 @@ export function createEndowments({
8987
} else if (endowmentName === 'ethereum') {
9088
// Special case for adding the EIP-1193 provider.
9189
allEndowments[endowmentName] = ethereum;
92-
} else if (endowmentName in rootRealmGlobal) {
93-
logWarning(`Access to unhardened global ${endowmentName}.`);
94-
// If the endowment doesn't have a factory, just use whatever is on the
95-
// global object.
96-
const globalValue = (rootRealmGlobal as Record<string, unknown>)[
97-
endowmentName
98-
];
99-
allEndowments[endowmentName] = globalValue;
10090
} else {
10191
// If we get to this point, we've been passed an endowment that doesn't
10292
// exist in our current environment.

0 commit comments

Comments
 (0)