Skip to content

Commit c8e5888

Browse files
working POC bundle + context
1 parent 358ede2 commit c8e5888

3 files changed

Lines changed: 312 additions & 0 deletions

File tree

etc/bundle-driver.mjs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env node
2+
import * as esbuild from 'esbuild';
3+
import { fileURLToPath } from 'node:url';
4+
import { isBuiltin } from 'node:module';
5+
import path from 'node:path';
6+
7+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
8+
const rootDir = path.join(__dirname, '..');
9+
10+
await esbuild.build({
11+
entryPoints: [path.join(rootDir, 'src/index.ts')],
12+
bundle: true,
13+
outfile: path.join(rootDir, 'test/tools/runner/driver.bundle.js'),
14+
platform: 'node',
15+
format: 'cjs',
16+
target: 'node20',
17+
external: [
18+
'bson',
19+
'mongodb-connection-string-url',
20+
'@mongodb-js/saslprep',
21+
'@mongodb-js/zstd',
22+
'mongodb-client-encryption',
23+
'snappy',
24+
'@napi-rs/snappy*',
25+
'kerberos',
26+
'gcp-metadata',
27+
'@aws-sdk/credential-providers'
28+
],
29+
plugins: [{
30+
name: 'externalize-node-builtins',
31+
setup(build) {
32+
build.onResolve({ filter: /.*/ }, args => {
33+
if (isBuiltin(args.path)) {
34+
return { path: args.path, external: true };
35+
}
36+
});
37+
}
38+
}],
39+
sourcemap: 'inline',
40+
logLevel: 'info'
41+
});
42+
43+
console.log('✓ Driver bundle created at test/tools/runner/driver.bundle.js');

test/mongodb.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import * as fs from 'node:fs';
2+
import * as path from 'node:path';
3+
4+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
5+
function printExports() {
6+
function* walk(root: string): Generator<string> {
7+
const directoryContents = fs.readdirSync(root);
8+
for (const filepath of directoryContents) {
9+
const fullPath = path.join(root, filepath);
10+
const stat = fs.statSync(fullPath);
11+
if (stat.isDirectory()) {
12+
yield* walk(fullPath);
13+
} else if (stat.isFile()) {
14+
yield fullPath;
15+
}
16+
}
17+
}
18+
const driverSourceFiles = Array.from(walk(path.resolve(__dirname, '..', 'src')));
19+
20+
for (const srcFile of driverSourceFiles) {
21+
console.log(`export * from '${path.relative(__dirname, srcFile)}';`);
22+
}
23+
}
24+
25+
export * from '../src/admin';
26+
export * from '../src/bson';
27+
export * from '../src/bulk/common';
28+
export * from '../src/bulk/ordered';
29+
export * from '../src/bulk/unordered';
30+
export * from '../src/change_stream';
31+
export * from '../src/client-side-encryption/auto_encrypter';
32+
export * from '../src/client-side-encryption/client_encryption';
33+
export * from '../src/client-side-encryption/errors';
34+
export * from '../src/client-side-encryption/mongocryptd_manager';
35+
export * from '../src/client-side-encryption/providers/aws';
36+
export * from '../src/client-side-encryption/providers/azure';
37+
export * from '../src/client-side-encryption/providers/gcp';
38+
export * from '../src/client-side-encryption/providers/index';
39+
export * from '../src/client-side-encryption/state_machine';
40+
export * from '../src/cmap/auth/auth_provider';
41+
export * from '../src/cmap/auth/aws_temporary_credentials';
42+
export * from '../src/cmap/auth/gssapi';
43+
export * from '../src/cmap/auth/mongo_credentials';
44+
export * from '../src/cmap/auth/mongodb_aws';
45+
export * from '../src/cmap/auth/mongodb_oidc';
46+
export * from '../src/cmap/auth/mongodb_oidc/automated_callback_workflow';
47+
export * from '../src/cmap/auth/mongodb_oidc/azure_machine_workflow';
48+
export * from '../src/cmap/auth/mongodb_oidc/callback_workflow';
49+
export * from '../src/cmap/auth/plain';
50+
export * from '../src/cmap/auth/providers';
51+
export * from '../src/cmap/auth/scram';
52+
export * from '../src/cmap/auth/x509';
53+
export * from '../src/cmap/command_monitoring_events';
54+
export * from '../src/cmap/commands';
55+
export * from '../src/cmap/connect';
56+
export * from '../src/cmap/connection';
57+
export * from '../src/cmap/connection_pool';
58+
export * from '../src/cmap/connection_pool_events';
59+
export * from '../src/cmap/errors';
60+
export * from '../src/cmap/handshake/client_metadata';
61+
export * from '../src/cmap/metrics';
62+
export * from '../src/cmap/stream_description';
63+
export * from '../src/cmap/wire_protocol/compression';
64+
export * from '../src/cmap/wire_protocol/constants';
65+
export * from '../src/cmap/wire_protocol/on_demand/document';
66+
export * from '../src/cmap/wire_protocol/responses';
67+
export * from '../src/cmap/wire_protocol/shared';
68+
export * from '../src/collection';
69+
export * from '../src/connection_string';
70+
export * from '../src/constants';
71+
export * from '../src/cursor/abstract_cursor';
72+
export * from '../src/cursor/aggregation_cursor';
73+
export * from '../src/cursor/change_stream_cursor';
74+
export * from '../src/cursor/find_cursor';
75+
export * from '../src/cursor/list_collections_cursor';
76+
export * from '../src/cursor/list_indexes_cursor';
77+
export * from '../src/cursor/run_command_cursor';
78+
export * from '../src/db';
79+
export * from '../src/deps';
80+
export * from '../src/encrypter';
81+
export * from '../src/error';
82+
export * from '../src/explain';
83+
export * from '../src/gridfs/download';
84+
export * from '../src/gridfs/index';
85+
export * from '../src/gridfs/upload';
86+
export * from '../src/mongo_client';
87+
export * from '../src/mongo_logger';
88+
export * from '../src/mongo_types';
89+
export * from '../src/operations/aggregate';
90+
export * from '../src/operations/client_bulk_write/command_builder';
91+
export * from '../src/operations/client_bulk_write/common';
92+
export * from '../src/operations/client_bulk_write/results_merger';
93+
export * from '../src/operations/command';
94+
export * from '../src/operations/count';
95+
export * from '../src/operations/create_collection';
96+
export * from '../src/operations/delete';
97+
export * from '../src/operations/distinct';
98+
export * from '../src/operations/drop';
99+
export * from '../src/operations/estimated_document_count';
100+
export * from '../src/operations/execute_operation';
101+
export * from '../src/operations/find';
102+
export * from '../src/operations/find_and_modify';
103+
export * from '../src/operations/get_more';
104+
export * from '../src/operations/indexes';
105+
export * from '../src/operations/insert';
106+
export * from '../src/operations/kill_cursors';
107+
export * from '../src/operations/list_collections';
108+
export * from '../src/operations/list_databases';
109+
export * from '../src/operations/operation';
110+
export * from '../src/operations/profiling_level';
111+
export * from '../src/operations/remove_user';
112+
export * from '../src/operations/rename';
113+
export * from '../src/operations/run_command';
114+
export * from '../src/operations/search_indexes/create';
115+
export * from '../src/operations/search_indexes/drop';
116+
export * from '../src/operations/search_indexes/update';
117+
export * from '../src/operations/set_profiling_level';
118+
export * from '../src/operations/stats';
119+
export * from '../src/operations/update';
120+
export * from '../src/operations/validate_collection';
121+
export * from '../src/read_concern';
122+
export * from '../src/read_preference';
123+
export * from '../src/sdam/common';
124+
export * from '../src/sdam/events';
125+
export * from '../src/sdam/monitor';
126+
export * from '../src/sdam/server';
127+
export * from '../src/sdam/server_description';
128+
export * from '../src/sdam/server_selection';
129+
export * from '../src/sdam/srv_polling';
130+
export * from '../src/sdam/topology';
131+
export * from '../src/sdam/topology_description';
132+
export * from '../src/sessions';
133+
export * from '../src/sort';
134+
export * from '../src/timeout';
135+
export * from '../src/transactions';
136+
export * from '../src/utils';
137+
export * from '../src/write_concern';
138+
139+
// Must be last for precedence
140+
export * from '../src/index';
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* eslint-disable no-restricted-globals, @typescript-eslint/no-require-imports */
2+
3+
import * as fs from 'node:fs';
4+
import { isBuiltin } from 'node:module';
5+
import * as path from 'node:path';
6+
import * as vm from 'node:vm';
7+
8+
/**
9+
* Creates a require function that blocks access to specified core modules
10+
*/
11+
function createRestrictedRequire() {
12+
const blockedModules = new Set(['os']);
13+
14+
return function restrictedRequire(moduleName: string) {
15+
// Block core modules
16+
if (isBuiltin(moduleName) && blockedModules.has(moduleName)) {
17+
throw new Error(`Access to core module '${moduleName}' is restricted in this context`);
18+
}
19+
20+
return require(moduleName);
21+
} as NodeRequire;
22+
}
23+
24+
// Create a sandbox context with necessary globals
25+
const sandbox = vm.createContext({
26+
__proto__: null,
27+
28+
// Console and timing
29+
console: console,
30+
AbortController: AbortController,
31+
AbortSignal: AbortSignal,
32+
Date: global.Date,
33+
Error: global.Error,
34+
URL: global.URL,
35+
URLSearchParams: global.URLSearchParams,
36+
queueMicrotask: queueMicrotask,
37+
performance: global.performance,
38+
setTimeout: global.setTimeout,
39+
clearTimeout: global.clearTimeout,
40+
setInterval: global.setInterval,
41+
clearInterval: global.clearInterval,
42+
setImmediate: global.setImmediate,
43+
clearImmediate: global.clearImmediate,
44+
45+
// Process
46+
process: process,
47+
48+
// Global objects needed for runtime
49+
Buffer: Buffer,
50+
Promise: Promise,
51+
Map: Map,
52+
Set: Set,
53+
WeakMap: WeakMap,
54+
WeakSet: WeakSet,
55+
ArrayBuffer: ArrayBuffer,
56+
SharedArrayBuffer: SharedArrayBuffer,
57+
Atomics: Atomics,
58+
DataView: DataView,
59+
Int8Array: Int8Array,
60+
Uint8Array: Uint8Array,
61+
Uint8ClampedArray: Uint8ClampedArray,
62+
Int16Array: Int16Array,
63+
Uint16Array: Uint16Array,
64+
Int32Array: Int32Array,
65+
Uint32Array: Uint32Array,
66+
Float32Array: Float32Array,
67+
Float64Array: Float64Array,
68+
BigInt64Array: BigInt64Array,
69+
BigUint64Array: BigUint64Array,
70+
71+
// Other necessary globals
72+
TextEncoder: global.TextEncoder,
73+
TextDecoder: global.TextDecoder,
74+
BigInt: global.BigInt,
75+
Symbol: Symbol,
76+
Proxy: Proxy,
77+
Reflect: Reflect,
78+
Object: Object,
79+
Array: Array,
80+
Function: Function,
81+
String: String,
82+
Number: Number,
83+
Boolean: Boolean,
84+
RegExp: RegExp,
85+
Math: Math,
86+
JSON: JSON,
87+
Intl: global.Intl,
88+
89+
// Custom require that blocks core modules
90+
require: createRestrictedRequire(),
91+
92+
// Needed for some modules
93+
global: undefined as any,
94+
globalThis: undefined as any
95+
});
96+
97+
// Make global and globalThis point to the sandbox
98+
sandbox.global = sandbox;
99+
sandbox.globalThis = sandbox;
100+
101+
/**
102+
* Load the bundled MongoDB driver module in a VM context
103+
* This allows us to control the globals that the driver has access to
104+
*/
105+
export function loadContextifiedMongoDBModule() {
106+
const bundlePath = path.join(__dirname, 'driver.bundle.js');
107+
108+
if (!fs.existsSync(bundlePath)) {
109+
throw new Error(`Driver bundle not found at ${bundlePath}. Run 'npm run bundle:driver' first.`);
110+
}
111+
112+
const bundleCode = fs.readFileSync(bundlePath, 'utf8');
113+
114+
const exportsContainer = {};
115+
const moduleContainer = { exports: exportsContainer };
116+
117+
// Wrap the bundle in a CommonJS-style wrapper
118+
const wrapper = `(function(exports, module, require) {
119+
${bundleCode}
120+
})`;
121+
122+
const script = new vm.Script(wrapper, { filename: bundlePath });
123+
const fn = script.runInContext(sandbox);
124+
125+
// Execute the bundle with the restricted require from the sandbox
126+
fn(moduleContainer.exports, moduleContainer, sandbox.require);
127+
128+
return moduleContainer.exports;
129+
}

0 commit comments

Comments
 (0)