import { cbc as nobleAesCbc, ctr as nobleAesCtr, gcm as nobleAesGcm,} from "@noble/ciphers/aes.js";import { hmac as nobleHmac } from "@noble/hashes/hmac.js";import { md5, sha1 } from "@noble/hashes/legacy.js";import { pbkdf2 as noblePbkdf2 } from "@noble/hashes/pbkdf2.js";import { scrypt as nobleScrypt } from "@noble/hashes/scrypt.js";import { sha224, sha256, sha384, sha512 } from "@noble/hashes/sha2.js";import { transform } from "sucrase";import type { BrowserChildProcessPollEvent, BrowserChildProcessSpawnRequest,} from "./child-process-bridge.js";import { createBrowserNetworkAdapter } from "./driver.js";import { base64ToBytes, toUint8Array } from "./encoding.js";import { posixErrno } from "./errno.js";import type { ExecResult, NetworkAdapter, RunResult, StdioChannel, TimingMitigation, VirtualDirEntry, VirtualStat,} from "./runtime.js";import { createNetworkStub, exposeCustomGlobal, exposeMutableRuntimeStateGlobal, getIsolateRuntimeSource, getRequireSetupCode, getRuntimePolyfillCode, isESM, transformDynamicImport,} from "./runtime.js";import { defaultSignalExitCode, PROCESS_SIGNAL_NUMBERS, signalNumberForEvent,} from "./signals.js";import { assertBrowserSyncBridgeSupport, type BrowserSyncBridgeErrorPayload, type BrowserSyncBridgePayload, type BrowserWorkerSyncOperation, SYNC_BRIDGE_KIND_BINARY, SYNC_BRIDGE_KIND_JSON, SYNC_BRIDGE_KIND_NONE, SYNC_BRIDGE_KIND_TEXT, SYNC_BRIDGE_SIGNAL_KIND_INDEX, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, SYNC_BRIDGE_SIGNAL_STATE_IDLE, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, SYNC_BRIDGE_STATUS_ERROR,} from "./sync-bridge.js";import type { BrowserWorkerExecOptions, BrowserWorkerInitPayload, BrowserWorkerOutboundMessage, BrowserWorkerRequestMessage,} from "./worker-protocol.js"; let networkAdapter: NetworkAdapter | null = null;let initialized = false;let controlToken: string | null = null;let runtimeTimingMitigation: TimingMitigation = "freeze";let runtimeProcessConfig: Record<string, unknown> | null = null;let activeProcessRequestId: number | null = null;let activeExecutionId: string | null = null;let activeCaptureStdio = false;let activeSyncBridge: ReturnType<typeof createSyncBridgeClient> | null = null;const pendingExecutionSignals = new Map<string, (signal: number) => void>(); const dynamicImportCache = new Map<string, unknown>();// For a PERSISTENT execution (ExecOptions.persistent): process.exit resolves this// instead of throwing, so a long-running stdio program's async exit (e.g. an ACP agent// exiting on stdin EOF, from an async callback that can't be caught by the sync exec// wrapper) cleanly ends the run. Null in run-to-completion mode (exit throws as before).let persistentExitResolver: ((code: number) => void) | null = null;// Streaming stdin for a persistent execution: the host feeds more stdin while the program// runs (and ends it explicitly), rather than the one-shot exec stdin that auto-ends. Lets// the host drive a long-running stdio program (e.g. an ACP agent: write a request, read// the reply, write the next) as a proper external client.let streamingStdinEnabled = false;let activeStdinPush: ((data: string) => void) | null = null;let activeStdinEnd: (() => void) | null = null;// Safety bound so a persistent program that never exits can't hang the worker.const PERSISTENT_EXEC_TIMEOUT_MS = 120_000;const MAX_ERROR_MESSAGE_CHARS = 8192;const MAX_STDIO_MESSAGE_CHARS = 8192; function eventForSignalNumber(signal: number): string { return ( Object.entries(PROCESS_SIGNAL_NUMBERS).find( ([, value]) => value === signal, )?.[0] ?? `SIG${signal}` );} // Payload size defaults matching the Node runtime pathconst DEFAULT_BASE64_TRANSFER_BYTES = 16 * 1024 * 1024;const DEFAULT_JSON_PAYLOAD_BYTES = 4 * 1024 * 1024;const PAYLOAD_LIMIT_ERROR_CODE = "ERR_SANDBOX_PAYLOAD_TOO_LARGE";const DEFAULT_SCRYPT_COST = 16_384;const DEFAULT_SCRYPT_BLOCK_SIZE = 8;const DEFAULT_SCRYPT_PARALLELIZATION = 1; let base64TransferLimitBytes = DEFAULT_BASE64_TRANSFER_BYTES;let jsonPayloadLimitBytes = DEFAULT_JSON_PAYLOAD_BYTES; const encoder = new TextEncoder();const decoder = new TextDecoder();// biome-ignore lint/security/noGlobalEval: the browser worker intentionally evaluates isolated runtime source strings.const globalEval = eval as (source: string) => unknown;const SHARED_ARRAY_BUFFER_FREEZE_KEYS = [ "byteLength", "slice", "grow", "maxByteLength", "growable",] as const; type TimingGlobalsSnapshot = { captured: boolean; dateDescriptor?: PropertyDescriptor; dateValue?: DateConstructor; performanceDescriptor?: PropertyDescriptor; performanceValue?: Performance; sharedArrayBufferDescriptor?: PropertyDescriptor; sharedArrayBufferValue?: typeof SharedArrayBuffer; sharedArrayBufferPrototypeDescriptors: Map< string, PropertyDescriptor | undefined >;}; const timingGlobals: TimingGlobalsSnapshot = { captured: false, sharedArrayBufferPrototypeDescriptors: new Map(),}; function getUtf8ByteLength(text: string): number { return encoder.encode(text).byteLength;} function getRequiredControlToken(): string { if (!controlToken) { throw new Error( "Browser runtime worker control channel is not initialized", ); } return controlToken;} function captureTimingGlobals(): void { if (timingGlobals.captured) { return; } timingGlobals.captured = true; timingGlobals.dateDescriptor = Object.getOwnPropertyDescriptor( globalThis, "Date", ); timingGlobals.dateValue = globalThis.Date; timingGlobals.performanceDescriptor = Object.getOwnPropertyDescriptor( globalThis, "performance", ); timingGlobals.performanceValue = globalThis.performance; timingGlobals.sharedArrayBufferDescriptor = Object.getOwnPropertyDescriptor( globalThis, "SharedArrayBuffer", ); timingGlobals.sharedArrayBufferValue = globalThis.SharedArrayBuffer; const sharedArrayBufferCtor = globalThis.SharedArrayBuffer; if (typeof sharedArrayBufferCtor !== "function") { return; } const prototype = sharedArrayBufferCtor.prototype as Record<string, unknown>; for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) { timingGlobals.sharedArrayBufferPrototypeDescriptors.set( key, Object.getOwnPropertyDescriptor(prototype, key), ); }} function restoreGlobalProperty( name: "Date" | "performance" | "SharedArrayBuffer", descriptor?: PropertyDescriptor,): void { if (descriptor) { try { Object.defineProperty(globalThis, name, descriptor); return; } catch { if ("value" in descriptor) { (globalThis as Record<string, unknown>)[name] = descriptor.value; return; } } } Reflect.deleteProperty(globalThis, name);} function restoreSharedArrayBufferPrototype(): void { const sharedArrayBufferCtor = timingGlobals.sharedArrayBufferValue; if (typeof sharedArrayBufferCtor !== "function") { return; } const prototype = sharedArrayBufferCtor.prototype as Record<string, unknown>; for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) { const descriptor = timingGlobals.sharedArrayBufferPrototypeDescriptors.get(key); try { if (descriptor) { Object.defineProperty(prototype, key, descriptor); } else { delete prototype[key]; } } catch { // Ignore non-configurable SharedArrayBuffer prototype properties. } }} function restoreTimingMitigationOff(): void { captureTimingGlobals(); restoreGlobalProperty("Date", timingGlobals.dateDescriptor); restoreGlobalProperty("performance", timingGlobals.performanceDescriptor); restoreSharedArrayBufferPrototype(); restoreGlobalProperty( "SharedArrayBuffer", timingGlobals.sharedArrayBufferDescriptor, ); if ( typeof globalThis.performance === "undefined" || globalThis.performance === null ) { Object.defineProperty(globalThis, "performance", { value: { now: () => Date.now(), }, configurable: true, writable: true, }); }} function applyTimingMitigation( timingMitigation: TimingMitigation, frozenTimeMs?: number,): number | undefined { captureTimingGlobals(); restoreTimingMitigationOff(); if (timingMitigation !== "freeze") { return undefined; } const frozenTimeValue = typeof frozenTimeMs === "number" && Number.isFinite(frozenTimeMs) ? Math.trunc(frozenTimeMs) : Date.now(); const originalDate = timingGlobals.dateValue ?? timingGlobals.dateDescriptor?.value ?? Date; const frozenDateNow = () => frozenTimeValue; const FrozenDate = function (...args: unknown[]) { if (new.target) { if (args.length === 0) { return new originalDate(frozenTimeValue); } return new originalDate( ...(args as ConstructorParameters<DateConstructor>), ); } return originalDate(); } as unknown as DateConstructor; Object.defineProperty(FrozenDate, "prototype", { value: originalDate.prototype, writable: false, configurable: false, }); Object.defineProperty(FrozenDate, "now", { value: frozenDateNow, configurable: true, writable: false, }); FrozenDate.parse = originalDate.parse; FrozenDate.UTC = originalDate.UTC; try { Object.defineProperty(globalThis, "Date", { value: FrozenDate, configurable: true, writable: false, }); } catch { (globalThis as Record<string, unknown>).Date = FrozenDate; } const frozenPerformance = Object.create(null) as Record<string, unknown>; const originalPerformance = timingGlobals.performanceValue; if ( typeof originalPerformance !== "undefined" && originalPerformance !== null ) { const source = originalPerformance as unknown as Record<string, unknown>; for (const key of Object.getOwnPropertyNames( Object.getPrototypeOf(originalPerformance) ?? originalPerformance, )) { if (key === "now") { continue; } try { const value = source[key]; frozenPerformance[key] = typeof value === "function" ? value.bind(originalPerformance) : value; } catch { // Ignore performance accessors that throw in this host. } } } Object.defineProperty(frozenPerformance, "now", { value: () => 0, configurable: true, writable: false, }); Object.freeze(frozenPerformance); try { Object.defineProperty(globalThis, "performance", { value: frozenPerformance, configurable: true, writable: false, }); } catch { (globalThis as Record<string, unknown>).performance = frozenPerformance; } const sharedArrayBufferCtor = timingGlobals.sharedArrayBufferValue; if (typeof sharedArrayBufferCtor === "function") { const prototype = sharedArrayBufferCtor.prototype as Record< string, unknown >; for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) { try { Object.defineProperty(prototype, key, { get() { throw new TypeError( "SharedArrayBuffer is not available in sandbox", ); }, configurable: true, }); } catch { // Ignore non-configurable SharedArrayBuffer prototype properties. } } } try { Object.defineProperty(globalThis, "SharedArrayBuffer", { value: undefined, configurable: true, writable: false, enumerable: false, }); } catch { Reflect.deleteProperty(globalThis, "SharedArrayBuffer"); } return frozenTimeValue;} function assertPayloadByteLength( payloadLabel: string, actualBytes: number, maxBytes: number,): void { if (actualBytes <= maxBytes) return; const error = new Error( `[${PAYLOAD_LIMIT_ERROR_CODE}] ${payloadLabel}: payload is ${actualBytes} bytes, limit is ${maxBytes} bytes`, ); (error as { code?: string }).code = PAYLOAD_LIMIT_ERROR_CODE; throw error;} function assertTextPayloadSize( payloadLabel: string, text: string, maxBytes: number,): void { assertPayloadByteLength(payloadLabel, getUtf8ByteLength(text), maxBytes);} interface BrowserScryptOptions { cost?: unknown; N?: unknown; blockSize?: unknown; r?: unknown; parallelization?: unknown; p?: unknown;} function parseScryptOptions(value: unknown): BrowserScryptOptions { if (value == null) return {}; if (typeof value === "string") { const parsed = JSON.parse(value); return typeof parsed === "object" && parsed !== null ? (parsed as BrowserScryptOptions) : {}; } return typeof value === "object" ? (value as BrowserScryptOptions) : {};} function normalizeScryptPositiveInteger( value: unknown, fallback: number, label: string,): number { const normalized = value == null ? fallback : Number(value); if (!Number.isInteger(normalized) || normalized <= 0) { throw new Error(`crypto.scrypt ${label} must be a positive integer`); } return normalized;} function normalizeScryptOptions(value: unknown, keyLength: unknown) { const options = parseScryptOptions(value); const length = Number(keyLength); if (!Number.isInteger(length) || length < 0) { throw new Error("crypto.scrypt key length must be a non-negative integer"); } const cost = normalizeScryptPositiveInteger( options.cost ?? options.N, DEFAULT_SCRYPT_COST, "cost", ); if ((cost & (cost - 1)) !== 0) { throw new Error("crypto.scrypt cost must be a positive power of two"); } return { N: cost, r: normalizeScryptPositiveInteger( options.blockSize ?? options.r, DEFAULT_SCRYPT_BLOCK_SIZE, "block size", ), p: normalizeScryptPositiveInteger( options.parallelization ?? options.p, DEFAULT_SCRYPT_PARALLELIZATION, "parallelization", ), dkLen: length, };} const BROWSER_CRYPTO_HASHES = { md5, sha1, sha224, sha256, sha384, sha512,}; type BrowserCryptoHashName = keyof typeof BROWSER_CRYPTO_HASHES; function normalizeCryptoHashName(algorithm: unknown): BrowserCryptoHashName { const normalized = String(algorithm) .trim() .toLowerCase() .replace(/[-_]/g, ""); if (Object.hasOwn(BROWSER_CRYPTO_HASHES, normalized)) { return normalized as BrowserCryptoHashName; } throw new Error(`Unsupported browser crypto digest algorithm: ${algorithm}`);} const RSA_PKCS1_DIGEST_PREFIXES: Record<BrowserCryptoHashName, string> = { md5: "3020300c06082a864886f70d020505000410", sha1: "3021300906052b0e03021a05000414", sha224: "302d300d06096086480165030402040500041c", sha256: "3031300d060960864801650304020105000420", sha384: "3041300d060960864801650304020205000430", sha512: "3051300d060960864801650304020305000440",}; function browserCryptoHash(algorithm: unknown) { return BROWSER_CRYPTO_HASHES[normalizeCryptoHashName(algorithm)];} function hashDigestBytes(algorithm: unknown, data: unknown): Uint8Array {Lines 1–500 of 3759. Load more
import { cbc as nobleAesCbc, ctr as nobleAesCtr, gcm as nobleAesGcm, } from "@noble/ciphers/aes.js"; import { hmac as nobleHmac } from "@noble/hashes/hmac.js"; import { md5, sha1 } from "@noble/hashes/legacy.js"; import { pbkdf2 as noblePbkdf2 } from "@noble/hashes/pbkdf2.js"; import { scrypt as nobleScrypt } from "@noble/hashes/scrypt.js"; import { sha224, sha256, sha384, sha512 } from "@noble/hashes/sha2.js"; import { transform } from "sucrase"; import type { BrowserChildProcessPollEvent, BrowserChildProcessSpawnRequest, } from "./child-process-bridge.js"; import { createBrowserNetworkAdapter } from "./driver.js"; import { base64ToBytes, toUint8Array } from "./encoding.js"; import { posixErrno } from "./errno.js"; import type { ExecResult, NetworkAdapter, RunResult, StdioChannel, TimingMitigation, VirtualDirEntry, VirtualStat, } from "./runtime.js"; import { createNetworkStub, exposeCustomGlobal, exposeMutableRuntimeStateGlobal, getIsolateRuntimeSource, getRequireSetupCode, getRuntimePolyfillCode, isESM, transformDynamicImport, } from "./runtime.js"; import { defaultSignalExitCode, PROCESS_SIGNAL_NUMBERS, signalNumberForEvent, } from "./signals.js"; import { assertBrowserSyncBridgeSupport, type BrowserSyncBridgeErrorPayload, type BrowserSyncBridgePayload, type BrowserWorkerSyncOperation, SYNC_BRIDGE_KIND_BINARY, SYNC_BRIDGE_KIND_JSON, SYNC_BRIDGE_KIND_NONE, SYNC_BRIDGE_KIND_TEXT, SYNC_BRIDGE_SIGNAL_KIND_INDEX, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, SYNC_BRIDGE_SIGNAL_STATE_IDLE, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, SYNC_BRIDGE_STATUS_ERROR, } from "./sync-bridge.js"; import type { BrowserWorkerExecOptions, BrowserWorkerInitPayload, BrowserWorkerOutboundMessage, BrowserWorkerRequestMessage, } from "./worker-protocol.js"; let networkAdapter: NetworkAdapter | null = null; let initialized = false; let controlToken: string | null = null; let runtimeTimingMitigation: TimingMitigation = "freeze"; let runtimeProcessConfig: Record<string, unknown> | null = null; let activeProcessRequestId: number | null = null; let activeExecutionId: string | null = null; let activeCaptureStdio = false; let activeSyncBridge: ReturnType<typeof createSyncBridgeClient> | null = null; const pendingExecutionSignals = new Map<string, (signal: number) => void>(); const dynamicImportCache = new Map<string, unknown>(); // For a PERSISTENT execution (ExecOptions.persistent): process.exit resolves this // instead of throwing, so a long-running stdio program's async exit (e.g. an ACP agent // exiting on stdin EOF, from an async callback that can't be caught by the sync exec // wrapper) cleanly ends the run. Null in run-to-completion mode (exit throws as before). let persistentExitResolver: ((code: number) => void) | null = null; // Streaming stdin for a persistent execution: the host feeds more stdin while the program // runs (and ends it explicitly), rather than the one-shot exec stdin that auto-ends. Lets // the host drive a long-running stdio program (e.g. an ACP agent: write a request, read // the reply, write the next) as a proper external client. let streamingStdinEnabled = false; let activeStdinPush: ((data: string) => void) | null = null; let activeStdinEnd: (() => void) | null = null; // Safety bound so a persistent program that never exits can't hang the worker. const PERSISTENT_EXEC_TIMEOUT_MS = 120_000; const MAX_ERROR_MESSAGE_CHARS = 8192; const MAX_STDIO_MESSAGE_CHARS = 8192; function eventForSignalNumber(signal: number): string { return ( Object.entries(PROCESS_SIGNAL_NUMBERS).find( ([, value]) => value === signal, )?.[0] ?? `SIG${signal}` ); } // Payload size defaults matching the Node runtime path const DEFAULT_BASE64_TRANSFER_BYTES = 16 * 1024 * 1024; const DEFAULT_JSON_PAYLOAD_BYTES = 4 * 1024 * 1024; const PAYLOAD_LIMIT_ERROR_CODE = "ERR_SANDBOX_PAYLOAD_TOO_LARGE"; const DEFAULT_SCRYPT_COST = 16_384; const DEFAULT_SCRYPT_BLOCK_SIZE = 8; const DEFAULT_SCRYPT_PARALLELIZATION = 1; let base64TransferLimitBytes = DEFAULT_BASE64_TRANSFER_BYTES; let jsonPayloadLimitBytes = DEFAULT_JSON_PAYLOAD_BYTES; const encoder = new TextEncoder(); const decoder = new TextDecoder(); // biome-ignore lint/security/noGlobalEval: the browser worker intentionally evaluates isolated runtime source strings. const globalEval = eval as (source: string) => unknown; const SHARED_ARRAY_BUFFER_FREEZE_KEYS = [ "byteLength", "slice", "grow", "maxByteLength", "growable", ] as const; type TimingGlobalsSnapshot = { captured: boolean; dateDescriptor?: PropertyDescriptor; dateValue?: DateConstructor; performanceDescriptor?: PropertyDescriptor; performanceValue?: Performance; sharedArrayBufferDescriptor?: PropertyDescriptor; sharedArrayBufferValue?: typeof SharedArrayBuffer; sharedArrayBufferPrototypeDescriptors: Map< string, PropertyDescriptor | undefined >; }; const timingGlobals: TimingGlobalsSnapshot = { captured: false, sharedArrayBufferPrototypeDescriptors: new Map(), }; function getUtf8ByteLength(text: string): number { return encoder.encode(text).byteLength; } function getRequiredControlToken(): string { if (!controlToken) { throw new Error( "Browser runtime worker control channel is not initialized", ); } return controlToken; } function captureTimingGlobals(): void { if (timingGlobals.captured) { return; } timingGlobals.captured = true; timingGlobals.dateDescriptor = Object.getOwnPropertyDescriptor( globalThis, "Date", ); timingGlobals.dateValue = globalThis.Date; timingGlobals.performanceDescriptor = Object.getOwnPropertyDescriptor( globalThis, "performance", ); timingGlobals.performanceValue = globalThis.performance; timingGlobals.sharedArrayBufferDescriptor = Object.getOwnPropertyDescriptor( globalThis, "SharedArrayBuffer", ); timingGlobals.sharedArrayBufferValue = globalThis.SharedArrayBuffer; const sharedArrayBufferCtor = globalThis.SharedArrayBuffer; if (typeof sharedArrayBufferCtor !== "function") { return; } const prototype = sharedArrayBufferCtor.prototype as Record<string, unknown>; for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) { timingGlobals.sharedArrayBufferPrototypeDescriptors.set( key, Object.getOwnPropertyDescriptor(prototype, key), ); } } function restoreGlobalProperty( name: "Date" | "performance" | "SharedArrayBuffer", descriptor?: PropertyDescriptor, ): void { if (descriptor) { try { Object.defineProperty(globalThis, name, descriptor); return; } catch { if ("value" in descriptor) { (globalThis as Record<string, unknown>)[name] = descriptor.value; return; } } } Reflect.deleteProperty(globalThis, name); } function restoreSharedArrayBufferPrototype(): void { const sharedArrayBufferCtor = timingGlobals.sharedArrayBufferValue; if (typeof sharedArrayBufferCtor !== "function") { return; } const prototype = sharedArrayBufferCtor.prototype as Record<string, unknown>; for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) { const descriptor = timingGlobals.sharedArrayBufferPrototypeDescriptors.get(key); try { if (descriptor) { Object.defineProperty(prototype, key, descriptor); } else { delete prototype[key]; } } catch { // Ignore non-configurable SharedArrayBuffer prototype properties. } } } function restoreTimingMitigationOff(): void { captureTimingGlobals(); restoreGlobalProperty("Date", timingGlobals.dateDescriptor); restoreGlobalProperty("performance", timingGlobals.performanceDescriptor); restoreSharedArrayBufferPrototype(); restoreGlobalProperty( "SharedArrayBuffer", timingGlobals.sharedArrayBufferDescriptor, ); if ( typeof globalThis.performance === "undefined" || globalThis.performance === null ) { Object.defineProperty(globalThis, "performance", { value: { now: () => Date.now(), }, configurable: true, writable: true, }); } } function applyTimingMitigation( timingMitigation: TimingMitigation, frozenTimeMs?: number, ): number | undefined { captureTimingGlobals(); restoreTimingMitigationOff(); if (timingMitigation !== "freeze") { return undefined; } const frozenTimeValue = typeof frozenTimeMs === "number" && Number.isFinite(frozenTimeMs) ? Math.trunc(frozenTimeMs) : Date.now(); const originalDate = timingGlobals.dateValue ?? timingGlobals.dateDescriptor?.value ?? Date; const frozenDateNow = () => frozenTimeValue; const FrozenDate = function (...args: unknown[]) { if (new.target) { if (args.length === 0) { return new originalDate(frozenTimeValue); } return new originalDate( ...(args as ConstructorParameters<DateConstructor>), ); } return originalDate(); } as unknown as DateConstructor; Object.defineProperty(FrozenDate, "prototype", { value: originalDate.prototype, writable: false, configurable: false, }); Object.defineProperty(FrozenDate, "now", { value: frozenDateNow, configurable: true, writable: false, }); FrozenDate.parse = originalDate.parse; FrozenDate.UTC = originalDate.UTC; try { Object.defineProperty(globalThis, "Date", { value: FrozenDate, configurable: true, writable: false, }); } catch { (globalThis as Record<string, unknown>).Date = FrozenDate; } const frozenPerformance = Object.create(null) as Record<string, unknown>; const originalPerformance = timingGlobals.performanceValue; if ( typeof originalPerformance !== "undefined" && originalPerformance !== null ) { const source = originalPerformance as unknown as Record<string, unknown>; for (const key of Object.getOwnPropertyNames( Object.getPrototypeOf(originalPerformance) ?? originalPerformance, )) { if (key === "now") { continue; } try { const value = source[key]; frozenPerformance[key] = typeof value === "function" ? value.bind(originalPerformance) : value; } catch { // Ignore performance accessors that throw in this host. } } } Object.defineProperty(frozenPerformance, "now", { value: () => 0, configurable: true, writable: false, }); Object.freeze(frozenPerformance); try { Object.defineProperty(globalThis, "performance", { value: frozenPerformance, configurable: true, writable: false, }); } catch { (globalThis as Record<string, unknown>).performance = frozenPerformance; } const sharedArrayBufferCtor = timingGlobals.sharedArrayBufferValue; if (typeof sharedArrayBufferCtor === "function") { const prototype = sharedArrayBufferCtor.prototype as Record< string, unknown >; for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) { try { Object.defineProperty(prototype, key, { get() { throw new TypeError( "SharedArrayBuffer is not available in sandbox", ); }, configurable: true, }); } catch { // Ignore non-configurable SharedArrayBuffer prototype properties. } } } try { Object.defineProperty(globalThis, "SharedArrayBuffer", { value: undefined, configurable: true, writable: false, enumerable: false, }); } catch { Reflect.deleteProperty(globalThis, "SharedArrayBuffer"); } return frozenTimeValue; } function assertPayloadByteLength( payloadLabel: string, actualBytes: number, maxBytes: number, ): void { if (actualBytes <= maxBytes) return; const error = new Error( `[${PAYLOAD_LIMIT_ERROR_CODE}] ${payloadLabel}: payload is ${actualBytes} bytes, limit is ${maxBytes} bytes`, ); (error as { code?: string }).code = PAYLOAD_LIMIT_ERROR_CODE; throw error; } function assertTextPayloadSize( payloadLabel: string, text: string, maxBytes: number, ): void { assertPayloadByteLength(payloadLabel, getUtf8ByteLength(text), maxBytes); } interface BrowserScryptOptions { cost?: unknown; N?: unknown; blockSize?: unknown; r?: unknown; parallelization?: unknown; p?: unknown; } function parseScryptOptions(value: unknown): BrowserScryptOptions { if (value == null) return {}; if (typeof value === "string") { const parsed = JSON.parse(value); return typeof parsed === "object" && parsed !== null ? (parsed as BrowserScryptOptions) : {}; } return typeof value === "object" ? (value as BrowserScryptOptions) : {}; } function normalizeScryptPositiveInteger( value: unknown, fallback: number, label: string, ): number { const normalized = value == null ? fallback : Number(value); if (!Number.isInteger(normalized) || normalized <= 0) { throw new Error(`crypto.scrypt ${label} must be a positive integer`); } return normalized; } function normalizeScryptOptions(value: unknown, keyLength: unknown) { const options = parseScryptOptions(value); const length = Number(keyLength); if (!Number.isInteger(length) || length < 0) { throw new Error("crypto.scrypt key length must be a non-negative integer"); } const cost = normalizeScryptPositiveInteger( options.cost ?? options.N, DEFAULT_SCRYPT_COST, "cost", ); if ((cost & (cost - 1)) !== 0) { throw new Error("crypto.scrypt cost must be a positive power of two"); } return { N: cost, r: normalizeScryptPositiveInteger( options.blockSize ?? options.r, DEFAULT_SCRYPT_BLOCK_SIZE, "block size", ), p: normalizeScryptPositiveInteger( options.parallelization ?? options.p, DEFAULT_SCRYPT_PARALLELIZATION, "parallelization", ), dkLen: length, }; } const BROWSER_CRYPTO_HASHES = { md5, sha1, sha224, sha256, sha384, sha512, }; type BrowserCryptoHashName = keyof typeof BROWSER_CRYPTO_HASHES; function normalizeCryptoHashName(algorithm: unknown): BrowserCryptoHashName { const normalized = String(algorithm) .trim() .toLowerCase() .replace(/[-_]/g, ""); if (Object.hasOwn(BROWSER_CRYPTO_HASHES, normalized)) { return normalized as BrowserCryptoHashName; } throw new Error(`Unsupported browser crypto digest algorithm: ${algorithm}`); } const RSA_PKCS1_DIGEST_PREFIXES: Record<BrowserCryptoHashName, string> = { md5: "3020300c06082a864886f70d020505000410", sha1: "3021300906052b0e03021a05000414", sha224: "302d300d06096086480165030402040500041c", sha256: "3031300d060960864801650304020105000420", sha384: "3041300d060960864801650304020205000430", sha512: "3051300d060960864801650304020305000440", }; function browserCryptoHash(algorithm: unknown) { return BROWSER_CRYPTO_HASHES[normalizeCryptoHashName(algorithm)]; } function hashDigestBytes(algorithm: unknown, data: unknown): Uint8Array {