1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24const assert = require('assert'); 25const vm = require('vm'); 26 27// vm.runInNewContext 28{ 29 const sandbox = {}; 30 const result = vm.runInNewContext( 31 'foo = "bar"; this.typeofProcess = typeof process; typeof Object;', 32 sandbox 33 ); 34 assert.deepStrictEqual(sandbox, { 35 foo: 'bar', 36 typeofProcess: 'undefined', 37 }); 38 assert.strictEqual(result, 'function'); 39} 40 41// vm.runInContext 42{ 43 const sandbox = { foo: 'bar' }; 44 const context = vm.createContext(sandbox); 45 const result = vm.runInContext( 46 'baz = foo; this.typeofProcess = typeof process; typeof Object;', 47 context 48 ); 49 assert.deepStrictEqual(sandbox, { 50 foo: 'bar', 51 baz: 'bar', 52 typeofProcess: 'undefined' 53 }); 54 assert.strictEqual(result, 'function'); 55} 56 57// vm.runInThisContext 58{ 59 const result = vm.runInThisContext( 60 'vmResult = "foo"; Object.prototype.toString.call(process);' 61 ); 62 assert.strictEqual(global.vmResult, 'foo'); 63 assert.strictEqual(result, '[object process]'); 64 delete global.vmResult; 65} 66 67// vm.runInNewContext 68{ 69 const result = vm.runInNewContext( 70 'vmResult = "foo"; typeof process;' 71 ); 72 assert.strictEqual(global.vmResult, undefined); 73 assert.strictEqual(result, 'undefined'); 74} 75 76// vm.createContext 77{ 78 const sandbox = {}; 79 const context = vm.createContext(sandbox); 80 assert.strictEqual(sandbox, context); 81} 82 83// Run script with filename 84{ 85 const script = 'throw new Error("boom")'; 86 const filename = 'test-boom-error'; 87 const context = vm.createContext(); 88 89 function checkErr(err) { 90 return err.stack.startsWith('test-boom-error:1'); 91 } 92 93 assert.throws(() => vm.runInContext(script, context, filename), checkErr); 94 assert.throws(() => vm.runInNewContext(script, context, filename), checkErr); 95 assert.throws(() => vm.runInThisContext(script, filename), checkErr); 96} 97 98// Invalid arguments 99[null, 'string'].forEach((input) => { 100 assert.throws(() => { 101 vm.createContext({}, input); 102 }, { 103 code: 'ERR_INVALID_ARG_TYPE', 104 name: 'TypeError', 105 message: 'The "options" argument must be of type object.' + 106 common.invalidArgTypeHelper(input) 107 }); 108}); 109 110['name', 'origin'].forEach((propertyName) => { 111 assert.throws(() => { 112 vm.createContext({}, { [propertyName]: null }); 113 }, { 114 code: 'ERR_INVALID_ARG_TYPE', 115 name: 'TypeError', 116 message: `The "options.${propertyName}" property must be of type string. ` + 117 'Received null' 118 }); 119}); 120 121['contextName', 'contextOrigin'].forEach((propertyName) => { 122 assert.throws(() => { 123 vm.runInNewContext('', {}, { [propertyName]: null }); 124 }, { 125 code: 'ERR_INVALID_ARG_TYPE', 126 name: 'TypeError', 127 message: `The "options.${propertyName}" property must be of type string. ` + 128 'Received null' 129 }); 130}); 131 132// vm.compileFunction 133{ 134 assert.strictEqual( 135 vm.compileFunction('console.log("Hello, World!")').toString(), 136 'function () {\nconsole.log("Hello, World!")\n}' 137 ); 138 139 assert.strictEqual( 140 vm.compileFunction( 141 'return p + q + r + s + t', 142 ['p', 'q', 'r', 's', 't'] 143 )('ab', 'cd', 'ef', 'gh', 'ij'), 144 'abcdefghij' 145 ); 146 147 vm.compileFunction('return'); // Should not throw on 'return' 148 149 assert.throws(() => { 150 vm.compileFunction( 151 '});\n\n(function() {\nconsole.log(1);\n})();\n\n(function() {' 152 ); 153 }, { 154 name: 'SyntaxError', 155 message: "Unexpected token '}'" 156 }); 157 158 // Tests for failed argument validation 159 assert.throws(() => vm.compileFunction(), { 160 name: 'TypeError', 161 code: 'ERR_INVALID_ARG_TYPE', 162 message: 'The "code" argument must be of type string. ' + 163 'Received undefined' 164 }); 165 166 vm.compileFunction(''); // Should pass without params or options 167 168 assert.throws(() => vm.compileFunction('', null), { 169 name: 'TypeError', 170 code: 'ERR_INVALID_ARG_TYPE', 171 message: 'The "params" argument must be an instance of Array. ' + 172 'Received null' 173 }); 174 175 // vm.compileFunction('', undefined, null); 176 177 const optionTypes = { 178 'filename': 'string', 179 'columnOffset': 'number', 180 'lineOffset': 'number', 181 'cachedData': 'Buffer, TypedArray, or DataView', 182 'produceCachedData': 'boolean', 183 }; 184 185 for (const option in optionTypes) { 186 const typeErrorMessage = `The "options.${option}" property must be ` + 187 (option === 'cachedData' ? 'an instance of' : 'of type'); 188 assert.throws(() => { 189 vm.compileFunction('', undefined, { [option]: null }); 190 }, { 191 name: 'TypeError', 192 code: 'ERR_INVALID_ARG_TYPE', 193 message: typeErrorMessage + 194 ` ${optionTypes[option]}. Received null` 195 }); 196 } 197 198 // Testing for context-based failures 199 [Boolean(), Number(), null, String(), Symbol(), {}].forEach( 200 (value) => { 201 assert.throws(() => { 202 vm.compileFunction('', undefined, { parsingContext: value }); 203 }, { 204 name: 'TypeError', 205 code: 'ERR_INVALID_ARG_TYPE', 206 message: 'The "options.parsingContext" property must be an instance ' + 207 `of Context.${common.invalidArgTypeHelper(value)}` 208 }); 209 } 210 ); 211 212 // Testing for non Array type-based failures 213 [Boolean(), Number(), null, Object(), Symbol(), {}].forEach( 214 (value) => { 215 assert.throws(() => { 216 vm.compileFunction('', value); 217 }, { 218 name: 'TypeError', 219 code: 'ERR_INVALID_ARG_TYPE', 220 message: 'The "params" argument must be an instance of Array.' + 221 common.invalidArgTypeHelper(value) 222 }); 223 } 224 ); 225 226 assert.strictEqual( 227 vm.compileFunction( 228 'return a;', 229 undefined, 230 { contextExtensions: [{ a: 5 }] } 231 )(), 232 5 233 ); 234 235 assert.throws(() => { 236 vm.compileFunction('', undefined, { contextExtensions: null }); 237 }, { 238 name: 'TypeError', 239 code: 'ERR_INVALID_ARG_TYPE', 240 message: 'The "options.contextExtensions" property must be an instance of' + 241 ' Array. Received null' 242 }); 243 244 assert.throws(() => { 245 vm.compileFunction('', undefined, { contextExtensions: [0] }); 246 }, { 247 name: 'TypeError', 248 code: 'ERR_INVALID_ARG_TYPE', 249 message: 'The "options.contextExtensions[0]" property must be of type ' + 250 'object. Received type number (0)' 251 }); 252 253 const oldLimit = Error.stackTraceLimit; 254 // Setting value to run the last three tests 255 Error.stackTraceLimit = 1; 256 257 assert.throws(() => { 258 vm.compileFunction('throw new Error("Sample Error")')(); 259 }, { 260 message: 'Sample Error', 261 stack: 'Error: Sample Error\n at <anonymous>:1:7' 262 }); 263 264 assert.throws(() => { 265 vm.compileFunction( 266 'throw new Error("Sample Error")', 267 [], 268 { lineOffset: 3 } 269 )(); 270 }, { 271 message: 'Sample Error', 272 stack: 'Error: Sample Error\n at <anonymous>:4:7' 273 }); 274 275 assert.throws(() => { 276 vm.compileFunction( 277 'throw new Error("Sample Error")', 278 [], 279 { columnOffset: 3 } 280 )(); 281 }, { 282 message: 'Sample Error', 283 stack: 'Error: Sample Error\n at <anonymous>:1:10' 284 }); 285 286 assert.strictEqual( 287 vm.compileFunction( 288 'return varInContext', 289 [], 290 { 291 parsingContext: vm.createContext({ varInContext: 'abc' }) 292 } 293 )(), 294 'abc' 295 ); 296 297 assert.throws(() => { 298 vm.compileFunction( 299 'return varInContext', 300 [] 301 )(); 302 }, { 303 message: 'varInContext is not defined', 304 stack: 'ReferenceError: varInContext is not defined\n at <anonymous>:1:1' 305 }); 306 307 assert.notDeepStrictEqual( 308 vm.compileFunction( 309 'return global', 310 [], 311 { 312 parsingContext: vm.createContext({ global: {} }) 313 } 314 )(), 315 global 316 ); 317 318 assert.deepStrictEqual( 319 vm.compileFunction( 320 'return global', 321 [] 322 )(), 323 global 324 ); 325 326 // Resetting value 327 Error.stackTraceLimit = oldLimit; 328} 329