Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | import { makeDefaultExo } from '@metamask/kernel-utils/exo';
import { unwrapTestLogger } from '../test-powers.ts';
import type { TestPowers } from '../test-powers.ts';
/**
* Build a root object for a vat that exercises global endowments.
*
* @param vatPowers - The powers of the vat.
* @returns The root object.
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function buildRootObject(vatPowers: TestPowers) {
const tlog = unwrapTestLogger(vatPowers, 'endowment-globals');
tlog('buildRootObject');
const root = makeDefaultExo('root', {
bootstrap: () => {
tlog('bootstrap');
},
testTextCodec: () => {
const encoder = new TextEncoder();
const encoded = encoder.encode('hello');
const decoder = new TextDecoder();
const decoded = decoder.decode(encoded);
tlog(`textCodec: ${decoded}`);
return decoded;
},
testUrl: () => {
const url = new URL('https://example.com/path?a=1');
url.searchParams.set('b', '2');
const params = new URLSearchParams('x=10&y=20');
tlog(`url: ${url.pathname} params: ${params.get('x')}`);
return url.toString();
},
testBase64: () => {
const encoded = btoa('hello world');
const decoded = atob(encoded);
tlog(`base64: ${decoded}`);
return decoded;
},
testAbort: () => {
const controller = new AbortController();
const { signal } = controller;
const { aborted } = signal;
controller.abort('test reason');
tlog(`abort: before=${String(aborted)} after=${String(signal.aborted)}`);
return signal.aborted;
},
testTimers: async () => {
return new Promise((resolve) => {
setTimeout(() => {
tlog('timer: fired');
resolve('fired');
}, 10);
});
},
testInterval: async () => {
return new Promise((resolve) => {
let tickCount = 0;
const handle = setInterval(() => {
tickCount += 1;
if (tickCount >= 2) {
clearInterval(handle);
tlog(`interval: ticks=${tickCount}`);
resolve(tickCount);
}
}, 10);
});
},
testDate: () => {
const now = Date.now();
const isReal = !Number.isNaN(now) && now > 0;
tlog(`date: isReal=${String(isReal)}`);
return isReal;
},
testCrypto: () => {
// A 16-byte buffer makes the all-zero coincidence vanishingly small
// (2^-128); a 4-byte buffer has a ~1-in-4-billion false-negative rate.
const buffer = new Uint8Array(16);
// eslint-disable-next-line n/no-unsupported-features/node-builtins -- `crypto` here is the vat endowment, not the Node global; the rule's engines check does not apply inside a SES Compartment.
crypto.getRandomValues(buffer);
const hasRandomBytes = buffer.some((byte) => byte !== 0);
tlog(`crypto: hasRandomBytes=${String(hasRandomBytes)}`);
return hasRandomBytes;
},
testMath: () => {
const value = Math.random();
const inRange = value >= 0 && value < 1;
tlog(`math: inRange=${String(inRange)}`);
return inRange;
},
checkGlobal: (name: string) => {
// In a SES compartment, globalThis points to the compartment's own
// global object, so this correctly detects whether an endowment was
// provided. Intrinsics (e.g. ArrayBuffer) are always present;
// host/Web APIs (e.g. TextEncoder) are only present if endowed.
const exists = name in globalThis;
tlog(`checkGlobal: ${name}=${String(exists)}`);
return exists;
},
});
return root;
}
|