|
| 1 | +/** |
| 2 | + * Socket Security Bootstrap Loader |
| 3 | + * |
| 4 | + * ============================================================================ |
| 5 | + * CRITICAL: This module MUST be called early in Node.js pre-execution phase |
| 6 | + * ============================================================================ |
| 7 | + * |
| 8 | + * PURPOSE (What): |
| 9 | + * --------------- |
| 10 | + * Loads and executes Socket CLI security bootstrap code at Node.js startup. |
| 11 | + * The bootstrap monitors package installations, network requests, and file |
| 12 | + * operations to detect suspicious activity. |
| 13 | + * |
| 14 | + * WHY IT EXISTS (Why): |
| 15 | + * -------------------- |
| 16 | + * 1. EARLY LOADING: Must run before user code to intercept module loading |
| 17 | + * 2. ZERO FILESYSTEM: Bootstrap embedded as base64 - no file I/O needed |
| 18 | + * 3. ASYNC SUPPORT: Allows background security monitoring without blocking startup |
| 19 | + * 4. ISOLATED CONTEXT: Runs in separate VM context to avoid polluting globals |
| 20 | + * |
| 21 | + * HOW IT WORKS (Visual Flow): |
| 22 | + * --------------------------- |
| 23 | + * |
| 24 | + * Node.js Startup |
| 25 | + * │ |
| 26 | + * ├─→ lib/internal/process/pre_execution.js |
| 27 | + * │ └─→ loadPreloadModules() |
| 28 | + * │ └─→ require('internal/socketsecurity_bootstrap_loader')() ← 1-line injection |
| 29 | + * │ │ |
| 30 | + * │ ├─→ [THIS FILE] |
| 31 | + * │ │ ├─→ Decode base64 bootstrap |
| 32 | + * │ │ ├─→ Create module context |
| 33 | + * │ │ ├─→ Compile with vm.compileFunction() |
| 34 | + * │ │ └─→ Execute (async background) |
| 35 | + * │ │ |
| 36 | + * │ └─→ Returns immediately |
| 37 | + * │ |
| 38 | + * └─→ User code starts |
| 39 | + * (Bootstrap monitors in background) |
| 40 | + * |
| 41 | + * PERFORMANCE IMPACT: |
| 42 | + * ------------------- |
| 43 | + * - Decode: ~1-2ms (base64 → string) |
| 44 | + * - Compile: ~3-5ms (vm.compileFunction C++ API) |
| 45 | + * - Execute: Instant return (async code runs in background) |
| 46 | + * - Total: <10ms added to Node.js startup |
| 47 | + * |
| 48 | + * TECHNICAL DETAILS: |
| 49 | + * ------------------ |
| 50 | + * 1. Uses vm.compileFunction() (C++ API) instead of eval or vm.runInThisContext() |
| 51 | + * - Faster compilation |
| 52 | + * - Proper stack traces |
| 53 | + * - Module parameter injection (exports, require, module, __filename, __dirname) |
| 54 | + * |
| 55 | + * 2. Module context created manually: |
| 56 | + * - filename: '/internal/bootstrap-smol.js' (virtual path, validation only) |
| 57 | + * - paths: Standard node_modules resolution |
| 58 | + * - require: Full require() with resolve, cache, extensions |
| 59 | + * |
| 60 | + * 3. Async execution pattern: |
| 61 | + * - Bootstrap can use main().catch(...) pattern |
| 62 | + * - Starts executing but returns immediately |
| 63 | + * - Background monitoring continues while Node.js initializes |
| 64 | + * |
| 65 | + * ERROR HANDLING: |
| 66 | + * --------------- |
| 67 | + * - Catches all errors during load/compile/execute |
| 68 | + * - Prints to stderr (console not available this early) |
| 69 | + * - Never crashes Node.js (bootstrap failures are non-fatal) |
| 70 | + * |
| 71 | + * VISUAL EXAMPLE: |
| 72 | + * --------------- |
| 73 | + * |
| 74 | + * Base64 Embedded Bootstrap |
| 75 | + * ┌──────────────────────────┐ |
| 76 | + * │ ZnVuY3Rpb24gbWFpbigpIHs │ ← Build system embeds here |
| 77 | + * │ ...21,000+ lines... │ |
| 78 | + * └──────────────────────────┘ |
| 79 | + * │ |
| 80 | + * ├─→ Buffer.from(base64) → JavaScript source |
| 81 | + * │ |
| 82 | + * ├─→ vm.compileFunction(source, params) → Compiled function |
| 83 | + * │ |
| 84 | + * └─→ compiledFn(exports, require, module, ...) → Execute |
| 85 | + * │ |
| 86 | + * ├─→ Synchronous setup code runs immediately |
| 87 | + * └─→ main().catch(...) starts, returns immediately |
| 88 | + * │ |
| 89 | + * └─→ Async monitoring continues in background |
| 90 | + * |
| 91 | + * MAINTENANCE NOTES: |
| 92 | + * ------------------ |
| 93 | + * - This file is processed by build.mjs during compilation |
| 94 | + * - SOCKET_BOOTSTRAP_BASE64_PLACEHOLDER replaced with actual base64 |
| 95 | + * - Final file copied to Node.js source: lib/internal/socketsecurity_bootstrap_loader.js |
| 96 | + * - Patch injects 1 line: require('internal/socketsecurity_bootstrap_loader')() |
| 97 | + * |
| 98 | + * SECURITY CONSIDERATIONS: |
| 99 | + * ------------------------ |
| 100 | + * - Bootstrap runs with full Node.js internal access (can require internal modules) |
| 101 | + * - No filesystem or network access during load (embedded base64) |
| 102 | + * - Errors isolated (won't crash Node.js) |
| 103 | + * - Module context prevents global pollution |
| 104 | + * |
| 105 | + * @module internal/socketsecurity_bootstrap_loader |
| 106 | + * @requires internal/modules/cjs/loader - Module system access |
| 107 | + * @requires internal/modules/helpers - makeRequireFunction() |
| 108 | + * @requires vm - Compilation API |
| 109 | + * @requires buffer - Base64 decoding |
| 110 | + */ |
| 111 | + |
| 112 | +'use strict'; |
| 113 | + |
| 114 | +/** |
| 115 | + * Load and execute the Socket bootstrap. |
| 116 | + * |
| 117 | + * Called from lib/internal/process/pre_execution.js during Node.js startup. |
| 118 | + * This function MUST return quickly to avoid blocking Node.js initialization. |
| 119 | + * |
| 120 | + * EXECUTION FLOW: |
| 121 | + * --------------- |
| 122 | + * 1. Decode base64 → JavaScript source code |
| 123 | + * 2. Create module context (exports, require, module, __filename, __dirname) |
| 124 | + * 3. Compile source with vm.compileFunction() (C++ API, fast!) |
| 125 | + * 4. Execute compiled function (synchronous setup + async background) |
| 126 | + * 5. Return immediately (async code continues in background) |
| 127 | + * |
| 128 | + * ERROR BEHAVIOR: |
| 129 | + * --------------- |
| 130 | + * - Any error during load/compile/execute is caught |
| 131 | + * - Error printed to stderr (console not available) |
| 132 | + * - Node.js continues initialization (non-fatal) |
| 133 | + * - User code runs normally (bootstrap disabled) |
| 134 | + * |
| 135 | + * @returns {void} |
| 136 | + * @throws {never} All errors caught and logged to stderr |
| 137 | + */ |
| 138 | +module.exports = function loadSocketBootstrap() { |
| 139 | + // Bootstrap code embedded as base64 (build system replaces this placeholder). |
| 140 | + // Split across multiple lines for readability and to avoid line length limits. |
| 141 | + // |
| 142 | + // PLACEHOLDER REPLACEMENT: |
| 143 | + // ├─→ Build system (build.mjs) reads bootstrap source |
| 144 | + // ├─→ Encodes as base64 (1275KB → 1700KB) |
| 145 | + // ├─→ Splits into 80-char chunks |
| 146 | + // └─→ Replaces SOCKET_BOOTSTRAP_BASE64_PLACEHOLDER |
| 147 | + const SOCKET_BOOTSTRAP_B64 = ( |
| 148 | + SOCKET_BOOTSTRAP_BASE64_PLACEHOLDER |
| 149 | + ); |
| 150 | + |
| 151 | + try { |
| 152 | + // STEP 1: Load Node.js internals. |
| 153 | + // -------------------------------- |
| 154 | + // These modules MUST be available during pre-execution phase. |
| 155 | + // If any are missing, the catch block will handle gracefully. |
| 156 | + const Module = require('internal/modules/cjs/loader').Module; |
| 157 | + const { makeRequireFunction } = require('internal/modules/helpers'); |
| 158 | + const vm = require('vm'); |
| 159 | + const { Buffer } = require('buffer'); |
| 160 | + |
| 161 | + // STEP 2: Decode bootstrap from base64. |
| 162 | + // -------------------------------------- |
| 163 | + // Performance: ~1-2ms for 1700KB base64 → 1275KB JavaScript |
| 164 | + // Result: Plain JavaScript source code ready for compilation |
| 165 | + const bootstrapCode = Buffer.from(SOCKET_BOOTSTRAP_B64, 'base64').toString('utf8'); |
| 166 | + |
| 167 | + // STEP 3: Create module context. |
| 168 | + // ------------------------------- |
| 169 | + // This gives the bootstrap access to require(), module.exports, etc. |
| 170 | + // |
| 171 | + // CRITICAL: filename MUST be absolute path format for validation. |
| 172 | + // - Module.createRequire() validates filename is absolute |
| 173 | + // - Doesn't need to exist as real file, just valid path format |
| 174 | + // - Using '/internal/bootstrap-smol.js' (Node.js internal path style) |
| 175 | + const bootstrapModule = new Module('socket:bootstrap', null); |
| 176 | + bootstrapModule.filename = '/internal/bootstrap-smol.js'; // Virtual path (validation only) |
| 177 | + bootstrapModule.paths = Module._nodeModulePaths(process.cwd()); // Standard resolution |
| 178 | + const exports = {}; |
| 179 | + bootstrapModule.exports = exports; |
| 180 | + |
| 181 | + // STEP 4: Create require function. |
| 182 | + // --------------------------------- |
| 183 | + // makeRequireFunction() adds: |
| 184 | + // - require.resolve() |
| 185 | + // - require.cache |
| 186 | + // - require.extensions |
| 187 | + // - require.main |
| 188 | + const moduleRequire = makeRequireFunction(bootstrapModule); |
| 189 | + |
| 190 | + // STEP 5: Compile using C++ API. |
| 191 | + // ------------------------------- |
| 192 | + // vm.compileFunction() is FASTER than: |
| 193 | + // - eval() (no stack traces, security issues) |
| 194 | + // - vm.runInThisContext() (slower, no parameter injection) |
| 195 | + // - Module.wrap() + compilation (extra wrapping overhead) |
| 196 | + // |
| 197 | + // Parameters: ['exports', 'require', 'module', '__filename', '__dirname'] |
| 198 | + // These match standard CommonJS module parameters. |
| 199 | + const compiledFn = vm.compileFunction( |
| 200 | + bootstrapCode, |
| 201 | + ['exports', 'require', 'module', '__filename', '__dirname'], |
| 202 | + { |
| 203 | + filename: '/internal/bootstrap-smol.js', // For stack traces |
| 204 | + lineOffset: 0, // Source starts at line 0 |
| 205 | + columnOffset: 0, // Source starts at column 0 |
| 206 | + } |
| 207 | + ); |
| 208 | + |
| 209 | + // STEP 6: Execute with module context. |
| 210 | + // ------------------------------------- |
| 211 | + // Reflect.apply() provides clean invocation: |
| 212 | + // - thisArg: exports (standard CommonJS) |
| 213 | + // - args: [exports, require, module, __filename, __dirname] |
| 214 | + // |
| 215 | + // ASYNC BEHAVIOR: |
| 216 | + // - If bootstrap has main().catch(...), it starts executing |
| 217 | + // - Function returns immediately (doesn't wait for async) |
| 218 | + // - Async operations continue in background |
| 219 | + // - Node.js initialization proceeds normally |
| 220 | + Reflect.apply(compiledFn, exports, [ |
| 221 | + exports, // exports object |
| 222 | + moduleRequire, // require() with resolve, cache, etc. |
| 223 | + bootstrapModule, // module object |
| 224 | + '/internal/bootstrap-smol.js', // __filename (virtual) |
| 225 | + '/internal' // __dirname (virtual) |
| 226 | + ]); |
| 227 | + |
| 228 | + // Returns here immediately, even if bootstrap has async code! |
| 229 | + // Background monitoring continues while Node.js initializes. |
| 230 | + |
| 231 | + } catch (err) { |
| 232 | + // ERROR HANDLING: |
| 233 | + // --------------- |
| 234 | + // - console.* not available this early in bootstrap |
| 235 | + // - Use process.stderr.write() for direct output |
| 236 | + // - Include stack trace for debugging |
| 237 | + // - Never throw (would crash Node.js) |
| 238 | + // |
| 239 | + // FAILURE MODES: |
| 240 | + // 1. Module loading error (require fails) → Bootstrap disabled |
| 241 | + // 2. Base64 decode error → Bootstrap disabled |
| 242 | + // 3. Compilation error (invalid syntax) → Bootstrap disabled |
| 243 | + // 4. Execution error (runtime error) → Bootstrap disabled |
| 244 | + // |
| 245 | + // In all cases: Node.js continues, user code runs normally. |
| 246 | + process.stderr.write(`Socket bootstrap error: ${err.message}\n${err.stack}\n`); |
| 247 | + } |
| 248 | +}; |
0 commit comments