UNPKG

18.6 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var acorn = require('acorn');
6var bson = require('bson');
7
8function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
9
10function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
11
12function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
13
14function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
15
16function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
17
18function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
19
20function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
21
22function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
23
24function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
25
26function NumberLong(v) {
27 if (typeof v === 'string') {
28 return bson.Long.fromString(v);
29 } else {
30 return bson.Long.fromNumber(v);
31 }
32}
33
34var SCOPE = {
35 RegExp: RegExp,
36 Binary: function Binary(buffer, subType) {
37 return new bson.Binary(buffer, subType);
38 },
39 BinData: function BinData(t, d) {
40 return new bson.Binary(Buffer.from(d, 'base64'), t);
41 },
42 UUID: function UUID(u) {
43 return new bson.Binary(Buffer.from(u.replace(/-/g, ''), 'hex'), 4);
44 },
45 Code: function Code(c, s) {
46 return new bson.Code(c, s);
47 },
48 DBRef: function DBRef(namespace, oid, db, fields) {
49 return new bson.DBRef(namespace, oid, db, fields);
50 },
51 Decimal128: function Decimal128(s) {
52 return bson.Decimal128.fromString(s);
53 },
54 NumberDecimal: function NumberDecimal(s) {
55 return bson.Decimal128.fromString(s);
56 },
57 Double: function Double(s) {
58 return new bson.Double(s);
59 },
60 Int32: function Int32(i) {
61 return new bson.Int32(i);
62 },
63 NumberInt: function NumberInt(s) {
64 return parseInt(s, 10);
65 },
66 Long: function Long(low, high) {
67 return new bson.Long(low, high);
68 },
69 NumberLong: NumberLong,
70 Int64: NumberLong,
71 Map: function Map(arr) {
72 return new bson.Map(arr);
73 },
74 MaxKey: function MaxKey() {
75 return new bson.MaxKey();
76 },
77 MinKey: function MinKey() {
78 return new bson.MinKey();
79 },
80 ObjectID: function ObjectID(i) {
81 return new bson.ObjectID(i);
82 },
83 ObjectId: function ObjectId(i) {
84 return new bson.ObjectID(i);
85 },
86 Symbol: function Symbol(i) {
87 return new bson.BSONSymbol(i);
88 },
89 Timestamp: function Timestamp(low, high) {
90 return new bson.Timestamp(low, high);
91 },
92 ISODate: function ISODate() {
93 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
94 args[_key] = arguments[_key];
95 }
96
97 // casting our arguments as an empty array because we don't know
98 // the length of our arguments, and should allow users to pass what
99 // they want as date arguments
100 return _construct(Date, _toConsumableArray(args));
101 },
102 Date: function (_Date) {
103 function Date() {
104 return _Date.apply(this, arguments);
105 }
106
107 Date.toString = function () {
108 return _Date.toString();
109 };
110
111 return Date;
112 }(function () {
113 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
114 args[_key2] = arguments[_key2];
115 }
116
117 // casting our arguments as an empty array because we don't know
118 // the length of our arguments, and should allow users to pass what
119 // they want as date arguments
120 return _construct(Date, _toConsumableArray(args));
121 })
122};
123var GLOBALS = Object.freeze({
124 Infinity: Infinity,
125 NaN: NaN,
126 undefined: undefined
127});
128var ALLOWED_CLASS_EXPRESSIONS = {
129 Math: {
130 class: Math,
131 allowedMethods: {
132 abs: true,
133 acos: true,
134 acosh: true,
135 asin: true,
136 asinh: true,
137 atan: true,
138 atan2: true,
139 atanh: true,
140 cbrt: true,
141 ceil: true,
142 clz32: true,
143 cos: true,
144 cosh: true,
145 exp: true,
146 expm1: true,
147 floor: true,
148 fround: true,
149 hypot: true,
150 imul: true,
151 log: true,
152 log10: true,
153 log1p: true,
154 log2: true,
155 max: true,
156 min: true,
157 pow: true,
158 round: true,
159 sign: true,
160 sin: true,
161 sinh: true,
162 sqrt: true,
163 tan: true,
164 tanh: true,
165 trunc: true
166 }
167 },
168 Date: {
169 class: Date,
170 allowedMethods: {
171 getDate: true,
172 getDay: true,
173 getFullYear: true,
174 getHours: true,
175 getMilliseconds: true,
176 getMinutes: true,
177 getMonth: true,
178 getSeconds: true,
179 getTime: true,
180 getTimezoneOffset: true,
181 getUTCDate: true,
182 getUTCDay: true,
183 getUTCFullYear: true,
184 getUTCHours: true,
185 getUTCMilliseconds: true,
186 getUTCMinutes: true,
187 getUTCMonth: true,
188 getUTCSeconds: true,
189 getYear: true,
190 now: true,
191 setDate: true,
192 setFullYear: true,
193 setHours: true,
194 setMilliseconds: true,
195 setMinutes: true,
196 setMonth: true,
197 setSeconds: true,
198 setTime: true,
199 setUTCDate: true,
200 setUTCFullYear: true,
201 setUTCHours: true,
202 setUTCMilliseconds: true,
203 setUTCMinutes: true,
204 setUTCMonth: true,
205 setUTCSeconds: true,
206 setYear: true,
207 toISOString: true
208 }
209 },
210 ISODate: {
211 class: Date,
212 allowedMethods: 'Date'
213 }
214};
215var GLOBAL_FUNCTIONS = Object.freeze(Object.keys(SCOPE));
216function getScopeFunction(key) {
217 if (SCOPE[key]) {
218 return SCOPE[key];
219 }
220
221 throw new Error("Attempted to access scope property '".concat(key, "' that doesn't exist"));
222}
223function isMethodWhitelisted(member, property) {
224 if (ALLOWED_CLASS_EXPRESSIONS[member]) {
225 var allowedMethods = ALLOWED_CLASS_EXPRESSIONS[member].allowedMethods;
226
227 if (typeof allowedMethods === 'string') {
228 return ALLOWED_CLASS_EXPRESSIONS[allowedMethods].allowedMethods[property];
229 }
230
231 return allowedMethods[property];
232 }
233
234 return false;
235}
236function getClass(member) {
237 if (ALLOWED_CLASS_EXPRESSIONS[member]) {
238 return ALLOWED_CLASS_EXPRESSIONS[member].class;
239 }
240
241 throw new Error("Attempted to access member '".concat(member, "' that doesn't exist"));
242}
243
244function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
245
246function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
247
248var Checker = function Checker(options) {
249 var _this = this;
250
251 _classCallCheck(this, Checker);
252
253 this.options = options;
254
255 _defineProperty(this, "checkSafeCall", function (node) {
256 var allowMethods = _this.options.allowMethods;
257
258 if (node.callee.type === 'Identifier') {
259 return GLOBAL_FUNCTIONS.indexOf(node.callee.name) >= 0 && node.arguments.every(_this.checkSafeExpression);
260 }
261
262 if (allowMethods) {
263 if (node.callee.type === 'MemberExpression') {
264 var object = node.callee.object;
265 var property = node.callee.property; // If we're only referring to identifiers, we don't need to check deeply.
266
267 if (object.type === 'Identifier' && property.type === 'Identifier') {
268 return isMethodWhitelisted(object.name, property.name) && node.arguments.every(_this.checkSafeExpression);
269 } else if ((object.type === 'NewExpression' || object.type === 'CallExpression') && object.callee.type === 'Identifier') {
270 var callee = object.callee;
271 return isMethodWhitelisted(callee.name, property.name) && node.arguments.every(_this.checkSafeExpression);
272 } else {
273 return _this.checkSafeExpression(object) && node.arguments.every(_this.checkSafeExpression);
274 }
275 }
276 }
277
278 return false;
279 });
280
281 _defineProperty(this, "checkSafeExpression", function (node) {
282 switch (node.type) {
283 case 'Identifier':
284 return GLOBALS.hasOwnProperty(node.name);
285
286 case 'Literal':
287 return true;
288
289 case 'ArrayExpression':
290 return node.elements.every(_this.checkSafeExpression);
291
292 case 'UnaryExpression':
293 // Note: this does allow using the `delete`, `typeof`, and `void` operators
294 return _this.checkSafeExpression(node.argument);
295
296 case 'BinaryExpression':
297 // Note: this does allow using the `instanceof`, `in`, and bitwise operators
298 return _this.checkSafeExpression(node.left) && _this.checkSafeExpression(node.right);
299
300 case 'CallExpression':
301 case 'NewExpression':
302 // allows both `new Date(...)` and `Date(...)` function calls
303 return _this.checkSafeCall(node);
304
305 case 'ObjectExpression':
306 return node.properties.every(function (property) {
307 // don't allow computed values { [10 + 10]: ... }
308 // don't allow method properties { start() {...} }
309 if (property.computed || property.method) return false; // only allow literals { 10: ...} or identifiers { name: ... } as keys
310
311 if (!['Literal', 'Identifier'].includes(property.key.type)) return false; // object values can be a function expression or any safe expression
312
313 return ['FunctionExpression', 'ArrowFunctionExpression'].includes(property.value.type) || _this.checkSafeExpression(property.value);
314 });
315
316 default:
317 return false;
318 }
319 });
320}
321/**
322 * Only allow CallExpressions where the Identifier matches a whitelist of safe
323 * globals, and where the arguments are themselves safe expressions
324 */
325;
326
327var checkTree = function checkTree(node, options) {
328 if (node.type === 'Program') {
329 if (node.body.length === 1 && node.body[0].type === 'ExpressionStatement') {
330 var checker = new Checker(options);
331 return checker.checkSafeExpression(node.body[0].expression);
332 }
333 }
334
335 return false;
336};
337
338var unaryExpression = function unaryExpression(node) {
339 if (!node.prefix) throw new Error('Malformed UnaryExpression');
340
341 switch (node.operator) {
342 case '-':
343 return -walk(node.argument);
344
345 case '+':
346 return +walk(node.argument);
347
348 case '!':
349 return !walk(node.argument);
350
351 case '~':
352 return ~walk(node.argument);
353
354 default:
355 throw new Error("Invalid UnaryExpression Provided: '".concat(node.operator, "'"));
356 }
357};
358
359var binaryExpression = function binaryExpression(node) {
360 var left = node.left,
361 right = node.right;
362
363 switch (node.operator) {
364 case '==':
365 return walk(left) == walk(right);
366
367 case '!=':
368 return walk(left) != walk(right);
369
370 case '===':
371 return walk(left) === walk(right);
372
373 case '!==':
374 return walk(left) !== walk(right);
375
376 case '<':
377 return walk(left) < walk(right);
378
379 case '<=':
380 return walk(left) <= walk(right);
381
382 case '>':
383 return walk(left) > walk(right);
384
385 case '>=':
386 return walk(left) >= walk(right);
387
388 case '<<':
389 return walk(left) << walk(right);
390
391 case '>>':
392 return walk(left) >> walk(right);
393
394 case '>>>':
395 return walk(left) >>> walk(right);
396
397 case '+':
398 return walk(left) + walk(right);
399
400 case '-':
401 return walk(left) - walk(right);
402
403 case '*':
404 return walk(left) * walk(right);
405
406 case '/':
407 return walk(left) / walk(right);
408
409 case '%':
410 return walk(left) % walk(right);
411
412 case '**':
413 return Math.pow(walk(left), walk(right));
414
415 case '|':
416 return walk(left) | walk(right);
417
418 case '^':
419 return walk(left) ^ walk(right);
420
421 case '&':
422 return walk(left) & walk(right);
423
424 case 'in':
425 return walk(left) in walk(right);
426
427 case 'instanceof':
428 return walk(left) instanceof walk(right);
429
430 default:
431 throw new Error("Invalid BinaryExpression Provided: '".concat(node.operator, "'"));
432 }
433};
434
435var memberExpression = function memberExpression(node) {
436 switch (node.callee.type) {
437 case 'Identifier':
438 {
439 // Handing <Constructor>() and new <Constructor>() cases
440 var callee = getScopeFunction(node.callee.name);
441 var args = node.arguments.map(function (arg) {
442 return walk(arg);
443 });
444 return callee.apply(callee, args);
445 }
446
447 case 'MemberExpression':
448 {
449 // If they're using a static method or a member
450 var calleeThis = node.callee.object.type === 'Identifier' ? getClass(node.callee.object.name) : walk(node.callee.object);
451 var calleeFn = node.callee.property.type === 'Identifier' && node.callee.property.name;
452 if (!calleeFn) throw new Error('Expected CallExpression property to be an identifier');
453
454 var _args = node.arguments.map(function (arg) {
455 return walk(arg);
456 });
457
458 return calleeThis[calleeFn].apply(calleeThis, _args);
459 }
460
461 default:
462 throw new Error('Should not evaluate invalid expressions');
463 }
464};
465
466var functionExpression = function functionExpression(node) {
467 var _node$loc;
468
469 var source = ((_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.source) || '';
470 var range = node.range || [];
471 return source.slice(range[0], range[1]);
472};
473
474var walk = function walk(node) {
475 switch (node.type) {
476 case 'Identifier':
477 if (GLOBALS.hasOwnProperty(node.name)) {
478 return GLOBALS[node.name];
479 }
480
481 throw new Error("".concat(node.name, " is not a valid Identifier"));
482
483 case 'Literal':
484 return node.value;
485
486 case 'UnaryExpression':
487 return unaryExpression(node);
488
489 case 'BinaryExpression':
490 return binaryExpression(node);
491
492 case 'ArrayExpression':
493 return node.elements.map(function (node) {
494 return walk(node);
495 });
496
497 case 'CallExpression':
498 case 'NewExpression':
499 return memberExpression(node);
500
501 case 'ObjectExpression':
502 var obj = {};
503 node.properties.forEach(function (property) {
504 var key = property.key.type === 'Identifier' ? property.key.name : walk(property.key);
505 obj[key] = walk(property.value);
506 });
507 return obj;
508
509 case 'FunctionExpression':
510 case 'ArrowFunctionExpression':
511 return functionExpression(node);
512
513 default:
514 throw new Error();
515 }
516};
517
518var executeAST = function executeAST(node) {
519 if (node.type === 'Program') {
520 if (node.body.length === 1 && node.body[0].type === 'ExpressionStatement') {
521 return walk(node.body[0].expression);
522 }
523 }
524
525 throw new Error('Invalid AST Found');
526};
527
528function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
529
530function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty$1(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
531
532function _defineProperty$1(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
533
534var ParseMode;
535
536(function (ParseMode) {
537 ParseMode["Strict"] = "strict";
538 ParseMode["Extended"] = "extended";
539 ParseMode["Loose"] = "loose";
540})(ParseMode || (ParseMode = {}));
541
542var StrictOptions = {
543 allowMethods: false,
544 // Allow function calls, ie Date.now(), Math.Max(), (new Date()).getFullYear()
545 allowComments: false // Allow comments (// and /* */)
546
547};
548var ExtendedOptions = {
549 allowMethods: true
550};
551var LooseOptions = {
552 allowMethods: true,
553 allowComments: true
554};
555
556function getModeOptions(mode) {
557 switch (mode) {
558 case ParseMode.Strict:
559 return StrictOptions;
560
561 case ParseMode.Extended:
562 return ExtendedOptions;
563
564 case ParseMode.Loose:
565 return LooseOptions;
566 }
567}
568
569var DefaultOptions = _objectSpread({
570 mode: ParseMode.Strict
571}, StrictOptions);
572
573function buildOptions(options) {
574 return _objectSpread(_objectSpread(_objectSpread({}, DefaultOptions), getModeOptions(options && options.mode || ParseMode.Strict)), options);
575}
576
577function buildAST(input) {
578 var hasComments = false;
579 var ast = acorn.parse(input, {
580 ecmaVersion: 6,
581 onComment: function onComment() {
582 return hasComments = true;
583 },
584 locations: true,
585 ranges: true,
586 sourceFile: input
587 });
588 return {
589 ast: ast,
590 hasComments: hasComments
591 };
592}
593
594function parse(input, options) {
595 var parsedOptions = buildOptions(options);
596
597 var _buildAST = buildAST("(".concat(input, ")")),
598 hasComments = _buildAST.hasComments,
599 ast = _buildAST.ast;
600
601 var passedCommentsCheck = !hasComments || parsedOptions.allowComments;
602
603 if (passedCommentsCheck && checkTree(ast, parsedOptions)) {
604 return executeAST(ast);
605 }
606
607 return '';
608}
609
610exports.default = parse;