handlebars-source 4.7.8 → 4.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/handlebars.js +480 -155
  3. data/handlebars.runtime.js +149 -93
  4. metadata +3 -3
data/handlebars.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**!
2
2
 
3
3
  @license
4
- handlebars v4.7.8
4
+ handlebars v4.7.10
5
5
 
6
6
  Copyright (C) 2011-2019 by Yehuda Katz
7
7
 
@@ -92,11 +92,11 @@ return /******/ (function(modules) { // webpackBootstrap
92
92
 
93
93
  // Compiler imports
94
94
 
95
- var _handlebarsCompilerAst = __webpack_require__(84);
95
+ var _handlebarsCompilerAst = __webpack_require__(83);
96
96
 
97
97
  var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst);
98
98
 
99
- var _handlebarsCompilerBase = __webpack_require__(85);
99
+ var _handlebarsCompilerBase = __webpack_require__(84);
100
100
 
101
101
  var _handlebarsCompilerCompiler = __webpack_require__(90);
102
102
 
@@ -104,11 +104,11 @@ return /******/ (function(modules) { // webpackBootstrap
104
104
 
105
105
  var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler);
106
106
 
107
- var _handlebarsCompilerVisitor = __webpack_require__(88);
107
+ var _handlebarsCompilerVisitor = __webpack_require__(87);
108
108
 
109
109
  var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor);
110
110
 
111
- var _handlebarsNoConflict = __webpack_require__(83);
111
+ var _handlebarsNoConflict = __webpack_require__(82);
112
112
 
113
113
  var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
114
114
 
@@ -178,7 +178,7 @@ return /******/ (function(modules) { // webpackBootstrap
178
178
  // Each of these augment the Handlebars object. No need to setup here.
179
179
  // (This is done to easily share code between commonjs and browse envs)
180
180
 
181
- var _handlebarsSafeString = __webpack_require__(77);
181
+ var _handlebarsSafeString = __webpack_require__(76);
182
182
 
183
183
  var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString);
184
184
 
@@ -190,11 +190,11 @@ return /******/ (function(modules) { // webpackBootstrap
190
190
 
191
191
  var Utils = _interopRequireWildcard(_handlebarsUtils);
192
192
 
193
- var _handlebarsRuntime = __webpack_require__(78);
193
+ var _handlebarsRuntime = __webpack_require__(77);
194
194
 
195
195
  var runtime = _interopRequireWildcard(_handlebarsRuntime);
196
196
 
197
- var _handlebarsNoConflict = __webpack_require__(83);
197
+ var _handlebarsNoConflict = __webpack_require__(82);
198
198
 
199
199
  var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict);
200
200
 
@@ -278,7 +278,7 @@ return /******/ (function(modules) { // webpackBootstrap
278
278
 
279
279
  var _internalProtoAccess = __webpack_require__(73);
280
280
 
281
- var VERSION = '4.7.8';
281
+ var VERSION = '4.7.10';
282
282
  exports.VERSION = VERSION;
283
283
  var COMPILER_REVISION = 8;
284
284
  exports.COMPILER_REVISION = COMPILER_REVISION;
@@ -452,8 +452,9 @@ return /******/ (function(modules) { // webpackBootstrap
452
452
 
453
453
  function escapeExpression(string) {
454
454
  if (typeof string !== 'string') {
455
- // don't escape SafeStrings, since they're already safe
456
- if (string && string.toHTML) {
455
+ // don't escape SafeStrings, since they're already safe. Other values with a
456
+ // "toHTML" key, e.g. from JSON context data, are escaped like any object.
457
+ if (string && typeof string.toHTML === 'function') {
457
458
  return string.toHTML();
458
459
  } else if (string == null) {
459
460
  return '';
@@ -516,11 +517,15 @@ return /******/ (function(modules) { // webpackBootstrap
516
517
  column = undefined,
517
518
  endColumn = undefined;
518
519
 
519
- if (loc) {
520
+ // Hand-built ASTs may carry a partial loc; never let the location
521
+ // bookkeeping mask the actual error message.
522
+ if (loc && loc.start) {
520
523
  line = loc.start.line;
521
- endLineNumber = loc.end.line;
522
524
  column = loc.start.column;
523
- endColumn = loc.end.column;
525
+ if (loc.end) {
526
+ endLineNumber = loc.end.line;
527
+ endColumn = loc.end.column;
528
+ }
524
529
 
525
530
  message += ' - ' + line + ':' + column;
526
531
  }
@@ -655,7 +660,8 @@ return /******/ (function(modules) { // webpackBootstrap
655
660
  if (instance.helpers[helperName]) {
656
661
  instance.hooks[helperName] = instance.helpers[helperName];
657
662
  if (!keepHelper) {
658
- delete instance.helpers[helperName];
663
+ // Using delete is slow
664
+ instance.helpers[helperName] = undefined;
659
665
  }
660
666
  }
661
667
  }
@@ -727,6 +733,26 @@ return /******/ (function(modules) { // webpackBootstrap
727
733
 
728
734
  var _exception2 = _interopRequireDefault(_exception);
729
735
 
736
+ /**
737
+ * Close an unfinished iterator after the block body threw, like for...of does,
738
+ * so that generators can run their cleanup code.
739
+ *
740
+ * Errors thrown while closing, including by a `return` getter, are ignored:
741
+ * the block's error is the cause and is the one the caller must see.
742
+ *
743
+ * @param {Iterator} iterator the iterator that was being consumed
744
+ */
745
+ function closeIterator(iterator) {
746
+ try {
747
+ var _close = iterator['return'];
748
+ if (_utils.isFunction(_close)) {
749
+ _close.call(iterator);
750
+ }
751
+ } catch (closeError) {
752
+ // NOP
753
+ }
754
+ }
755
+
730
756
  exports['default'] = function (instance) {
731
757
  instance.registerHelper('each', function (context, options) {
732
758
  if (!options) {
@@ -752,7 +778,7 @@ return /******/ (function(modules) { // webpackBootstrap
752
778
  data = _utils.createFrame(options.data);
753
779
  }
754
780
 
755
- function execIteration(field, index, last) {
781
+ function execIteration(field, index, last, value) {
756
782
  if (data) {
757
783
  data.key = field;
758
784
  data.index = index;
@@ -764,9 +790,9 @@ return /******/ (function(modules) { // webpackBootstrap
764
790
  }
765
791
  }
766
792
 
767
- ret = ret + fn(context[field], {
793
+ ret = ret + fn(value, {
768
794
  data: data,
769
- blockParams: _utils.blockParams([context[field], field], [contextPath + field, null])
795
+ blockParams: _utils.blockParams([value, field], [contextPath + field, null])
770
796
  });
771
797
  }
772
798
 
@@ -774,18 +800,24 @@ return /******/ (function(modules) { // webpackBootstrap
774
800
  if (_utils.isArray(context)) {
775
801
  for (var j = context.length; i < j; i++) {
776
802
  if (i in context) {
777
- execIteration(i, i, i === context.length - 1);
803
+ execIteration(i, i, i === context.length - 1, context[i]);
778
804
  }
779
805
  }
780
806
  } else if (typeof _Symbol === 'function' && context[_Symbol$iterator]) {
781
- var newContext = [];
782
807
  var iterator = _getIterator(context);
783
- for (var it = iterator.next(); !it.done; it = iterator.next()) {
784
- newContext.push(it.value);
785
- }
786
- context = newContext;
787
- for (var j = context.length; i < j; i++) {
788
- execIteration(i, i, i === context.length - 1);
808
+ var current = iterator.next();
809
+ while (!current.done) {
810
+ var next = iterator.next();
811
+ try {
812
+ execIteration(i, i, next.done, current.value);
813
+ } catch (e) {
814
+ if (!next.done) {
815
+ closeIterator(iterator);
816
+ }
817
+ throw e;
818
+ }
819
+ current = next;
820
+ i++;
789
821
  }
790
822
  } else {
791
823
  (function () {
@@ -796,13 +828,13 @@ return /******/ (function(modules) { // webpackBootstrap
796
828
  // the last iteration without have to scan the object twice and create
797
829
  // an itermediate keys array.
798
830
  if (priorKey !== undefined) {
799
- execIteration(priorKey, i - 1);
831
+ execIteration(priorKey, i - 1, false, context[priorKey]);
800
832
  }
801
833
  priorKey = key;
802
834
  i++;
803
835
  });
804
836
  if (priorKey !== undefined) {
805
- execIteration(priorKey, i - 1, true);
837
+ execIteration(priorKey, i - 1, true, context[priorKey]);
806
838
  }
807
839
  })();
808
840
  }
@@ -2039,9 +2071,10 @@ return /******/ (function(modules) { // webpackBootstrap
2039
2071
  exports.__esModule = true;
2040
2072
  exports.createProtoAccessControl = createProtoAccessControl;
2041
2073
  exports.resultIsAllowed = resultIsAllowed;
2074
+ exports.isPrototypeConstructor = isPrototypeConstructor;
2042
2075
  exports.resetLoggedProperties = resetLoggedProperties;
2043
2076
 
2044
- var _createNewLookupObject = __webpack_require__(76);
2077
+ var _utils = __webpack_require__(5);
2045
2078
 
2046
2079
  var _logger = __webpack_require__(72);
2047
2080
 
@@ -2050,23 +2083,28 @@ return /******/ (function(modules) { // webpackBootstrap
2050
2083
  var loggedProperties = _Object$create(null);
2051
2084
 
2052
2085
  function createProtoAccessControl(runtimeOptions) {
2053
- var defaultMethodWhiteList = _Object$create(null);
2054
- defaultMethodWhiteList['constructor'] = false;
2055
- defaultMethodWhiteList['__defineGetter__'] = false;
2056
- defaultMethodWhiteList['__defineSetter__'] = false;
2057
- defaultMethodWhiteList['__lookupGetter__'] = false;
2058
-
2059
- var defaultPropertyWhiteList = _Object$create(null);
2086
+ // Create an object with "null"-prototype to avoid truthy results on
2087
+ // prototype properties.
2088
+ var propertyWhiteList = _Object$create(null);
2060
2089
  // eslint-disable-next-line no-proto
2061
- defaultPropertyWhiteList['__proto__'] = false;
2090
+ propertyWhiteList['__proto__'] = false;
2091
+ _utils.extend(propertyWhiteList, runtimeOptions.allowedProtoProperties);
2092
+
2093
+ var methodWhiteList = _Object$create(null);
2094
+ methodWhiteList['constructor'] = false;
2095
+ methodWhiteList['__defineGetter__'] = false;
2096
+ methodWhiteList['__defineSetter__'] = false;
2097
+ methodWhiteList['__lookupGetter__'] = false;
2098
+ methodWhiteList['__lookupSetter__'] = false;
2099
+ _utils.extend(methodWhiteList, runtimeOptions.allowedProtoMethods);
2062
2100
 
2063
2101
  return {
2064
2102
  properties: {
2065
- whitelist: _createNewLookupObject.createNewLookupObject(defaultPropertyWhiteList, runtimeOptions.allowedProtoProperties),
2103
+ whitelist: propertyWhiteList,
2066
2104
  defaultValue: runtimeOptions.allowProtoPropertiesByDefault
2067
2105
  },
2068
2106
  methods: {
2069
- whitelist: _createNewLookupObject.createNewLookupObject(defaultMethodWhiteList, runtimeOptions.allowedProtoMethods),
2107
+ whitelist: methodWhiteList,
2070
2108
  defaultValue: runtimeOptions.allowProtoMethodsByDefault
2071
2109
  }
2072
2110
  };
@@ -2080,6 +2118,25 @@ return /******/ (function(modules) { // webpackBootstrap
2080
2118
  }
2081
2119
  }
2082
2120
 
2121
+ /**
2122
+ * Detect a prototype object's own "constructor" back-reference, e.g.
2123
+ * `Function.prototype.constructor === Function`. Because "constructor" is an
2124
+ * "own" property of such objects, it would otherwise bypass the prototype-access
2125
+ * checks and expose dangerous constructors (allowing arbitrary code execution).
2126
+ *
2127
+ * Only functions can be constructors, so other values are never treated as one
2128
+ * and their `prototype` is never read.
2129
+ *
2130
+ * @param {*} parent the object the property is being read from
2131
+ * @param {string} propertyName the property being looked up
2132
+ * @param {*} result the already-resolved value of `parent[propertyName]`
2133
+ * @returns {boolean} true if the lookup resolves `parent`'s own constructor
2134
+ */
2135
+
2136
+ function isPrototypeConstructor(parent, propertyName, result) {
2137
+ return propertyName === 'constructor' && typeof result === 'function' && parent === result.prototype;
2138
+ }
2139
+
2083
2140
  function checkWhiteList(protoAccessControlForType, propertyName) {
2084
2141
  if (protoAccessControlForType.whitelist[propertyName] !== undefined) {
2085
2142
  return protoAccessControlForType.whitelist[propertyName] === true;
@@ -2121,34 +2178,6 @@ return /******/ (function(modules) { // webpackBootstrap
2121
2178
 
2122
2179
  /***/ }),
2123
2180
  /* 76 */
2124
- /***/ (function(module, exports, __webpack_require__) {
2125
-
2126
- 'use strict';
2127
-
2128
- var _Object$create = __webpack_require__(74)['default'];
2129
-
2130
- exports.__esModule = true;
2131
- exports.createNewLookupObject = createNewLookupObject;
2132
-
2133
- var _utils = __webpack_require__(5);
2134
-
2135
- /**
2136
- * Create a new object with "null"-prototype to avoid truthy results on prototype properties.
2137
- * The resulting object can be used with "object[property]" to check if a property exists
2138
- * @param {...object} sources a varargs parameter of source objects that will be merged
2139
- * @returns {object}
2140
- */
2141
-
2142
- function createNewLookupObject() {
2143
- for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
2144
- sources[_key] = arguments[_key];
2145
- }
2146
-
2147
- return _utils.extend.apply(undefined, [_Object$create(null)].concat(sources));
2148
- }
2149
-
2150
- /***/ }),
2151
- /* 77 */
2152
2181
  /***/ (function(module, exports) {
2153
2182
 
2154
2183
  // Build out our basic SafeString type
@@ -2167,12 +2196,12 @@ return /******/ (function(modules) { // webpackBootstrap
2167
2196
  module.exports = exports['default'];
2168
2197
 
2169
2198
  /***/ }),
2170
- /* 78 */
2199
+ /* 77 */
2171
2200
  /***/ (function(module, exports, __webpack_require__) {
2172
2201
 
2173
2202
  'use strict';
2174
2203
 
2175
- var _Object$seal = __webpack_require__(79)['default'];
2204
+ var _Object$seal = __webpack_require__(78)['default'];
2176
2205
 
2177
2206
  var _Object$keys = __webpack_require__(60)['default'];
2178
2207
 
@@ -2200,7 +2229,7 @@ return /******/ (function(modules) { // webpackBootstrap
2200
2229
 
2201
2230
  var _helpers = __webpack_require__(10);
2202
2231
 
2203
- var _internalWrapHelper = __webpack_require__(82);
2232
+ var _internalWrapHelper = __webpack_require__(81);
2204
2233
 
2205
2234
  var _internalProtoAccess = __webpack_require__(73);
2206
2235
 
@@ -2249,16 +2278,26 @@ return /******/ (function(modules) { // webpackBootstrap
2249
2278
  }
2250
2279
  partial = env.VM.resolvePartial.call(this, partial, context, options);
2251
2280
 
2252
- var extendedOptions = Utils.extend({}, options, {
2253
- hooks: this.hooks,
2254
- protoAccessControl: this.protoAccessControl
2255
- });
2281
+ options.hooks = this.hooks;
2282
+ options.protoAccessControl = this.protoAccessControl;
2283
+
2284
+ var result = env.VM.invokePartial.call(this, partial, context, options);
2256
2285
 
2257
- var result = env.VM.invokePartial.call(this, partial, context, extendedOptions);
2286
+ // An empty result normally means the partial is template source that still
2287
+ // has to be compiled. A function partial has already been invoked, so an
2288
+ // empty result from it is a bug in that partial.
2289
+ if (result == null && Utils.isFunction(partial)) {
2290
+ throw new _exception2['default']('The partial ' + options.name + ' returned no output: partials must return a string');
2291
+ }
2258
2292
 
2259
2293
  if (result == null && env.compile) {
2294
+ // Only template source registered as a partial may be compiled here.
2295
+ // Anything else (e.g. an AST-shaped object) must not become code.
2296
+ if (typeof partial !== 'string') {
2297
+ throw new _exception2['default']('The partial ' + options.name + ' could not be compiled: partials must be strings or functions returning strings');
2298
+ }
2260
2299
  options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
2261
- result = options.partials[options.name](context, extendedOptions);
2300
+ result = options.partials[options.name](context, options);
2262
2301
  }
2263
2302
  if (result != null) {
2264
2303
  if (options.indent) {
@@ -2293,7 +2332,12 @@ return /******/ (function(modules) { // webpackBootstrap
2293
2332
  if (result == null) {
2294
2333
  return result;
2295
2334
  }
2296
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
2335
+ // Own properties are trusted context data and returned as-is, except for
2336
+ // a prototype's own "constructor" back-reference (e.g.
2337
+ // Function.prototype.constructor === Function): that must still pass the
2338
+ // prototype-access deny list, otherwise the Function constructor becomes
2339
+ // reachable and arbitrary code can be executed.
2340
+ if (Object.prototype.hasOwnProperty.call(parent, propertyName) && !_internalProtoAccess.isPrototypeConstructor(parent, propertyName, result)) {
2297
2341
  return result;
2298
2342
  }
2299
2343
 
@@ -2307,7 +2351,7 @@ return /******/ (function(modules) { // webpackBootstrap
2307
2351
  for (var i = 0; i < len; i++) {
2308
2352
  var result = depths[i] && container.lookupProperty(depths[i], name);
2309
2353
  if (result != null) {
2310
- return depths[i][name];
2354
+ return result;
2311
2355
  }
2312
2356
  }
2313
2357
  },
@@ -2389,8 +2433,9 @@ return /******/ (function(modules) { // webpackBootstrap
2389
2433
 
2390
2434
  ret._setup = function (options) {
2391
2435
  if (!options.partial) {
2392
- var mergedHelpers = Utils.extend({}, env.helpers, options.helpers);
2393
- wrapHelpersToPassLookupProperty(mergedHelpers, container);
2436
+ var mergedHelpers = {};
2437
+ addHelpers(mergedHelpers, env.helpers, container);
2438
+ addHelpers(mergedHelpers, options.helpers, container);
2394
2439
  container.helpers = mergedHelpers;
2395
2440
 
2396
2441
  if (templateSpec.usePartial) {
@@ -2456,21 +2501,24 @@ return /******/ (function(modules) { // webpackBootstrap
2456
2501
  function resolvePartial(partial, context, options) {
2457
2502
  if (!partial) {
2458
2503
  if (options.name === '@partial-block') {
2459
- partial = options.data['partial-block'];
2504
+ partial = lookupOwnProperty(options.data, 'partial-block');
2460
2505
  } else {
2461
- partial = options.partials[options.name];
2506
+ partial = lookupOwnProperty(options.partials, options.name);
2462
2507
  }
2463
- } else if (!partial.call && !options.name) {
2464
- // This is a dynamic partial that returned a string
2508
+ } else if (typeof partial !== 'function' && !options.name) {
2509
+ // This is a dynamic partial that returned a name rather than a compiled
2510
+ // partial. Only functions are accepted as partials here; any other value
2511
+ // (including an object with a "call" property) is treated as a name, so
2512
+ // that context data can never be compiled as a template.
2465
2513
  options.name = partial;
2466
- partial = options.partials[partial];
2514
+ partial = lookupOwnProperty(options.partials, partial);
2467
2515
  }
2468
2516
  return partial;
2469
2517
  }
2470
2518
 
2471
2519
  function invokePartial(partial, context, options) {
2472
2520
  // Use the current closure context to save the partial-block if this partial
2473
- var currentPartialBlock = options.data && options.data['partial-block'];
2521
+ var currentPartialBlock = lookupOwnProperty(options.data, 'partial-block');
2474
2522
  options.partial = true;
2475
2523
  if (options.ids) {
2476
2524
  options.data.contextPath = options.ids[0] || options.data.contextPath;
@@ -2512,6 +2560,12 @@ return /******/ (function(modules) { // webpackBootstrap
2512
2560
  return '';
2513
2561
  }
2514
2562
 
2563
+ function lookupOwnProperty(obj, name) {
2564
+ if (obj && Object.prototype.hasOwnProperty.call(obj, name)) {
2565
+ return obj[name];
2566
+ }
2567
+ }
2568
+
2515
2569
  function initData(context, data) {
2516
2570
  if (!data || !('root' in data)) {
2517
2571
  data = data ? _base.createFrame(data) : {};
@@ -2529,9 +2583,10 @@ return /******/ (function(modules) { // webpackBootstrap
2529
2583
  return prog;
2530
2584
  }
2531
2585
 
2532
- function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
2533
- _Object$keys(mergedHelpers).forEach(function (helperName) {
2534
- var helper = mergedHelpers[helperName];
2586
+ function addHelpers(mergedHelpers, helpers, container) {
2587
+ if (!helpers) return;
2588
+ _Object$keys(helpers).forEach(function (helperName) {
2589
+ var helper = helpers[helperName];
2535
2590
  mergedHelpers[helperName] = passLookupPropertyOption(helper, container);
2536
2591
  });
2537
2592
  }
@@ -2539,25 +2594,26 @@ return /******/ (function(modules) { // webpackBootstrap
2539
2594
  function passLookupPropertyOption(helper, container) {
2540
2595
  var lookupProperty = container.lookupProperty;
2541
2596
  return _internalWrapHelper.wrapHelper(helper, function (options) {
2542
- return Utils.extend({ lookupProperty: lookupProperty }, options);
2597
+ options.lookupProperty = lookupProperty;
2598
+ return options;
2543
2599
  });
2544
2600
  }
2545
2601
 
2546
2602
  /***/ }),
2547
- /* 79 */
2603
+ /* 78 */
2548
2604
  /***/ (function(module, exports, __webpack_require__) {
2549
2605
 
2550
- module.exports = { "default": __webpack_require__(80), __esModule: true };
2606
+ module.exports = { "default": __webpack_require__(79), __esModule: true };
2551
2607
 
2552
2608
  /***/ }),
2553
- /* 80 */
2609
+ /* 79 */
2554
2610
  /***/ (function(module, exports, __webpack_require__) {
2555
2611
 
2556
- __webpack_require__(81);
2612
+ __webpack_require__(80);
2557
2613
  module.exports = __webpack_require__(21).Object.seal;
2558
2614
 
2559
2615
  /***/ }),
2560
- /* 81 */
2616
+ /* 80 */
2561
2617
  /***/ (function(module, exports, __webpack_require__) {
2562
2618
 
2563
2619
  // 19.1.2.17 Object.seal(O)
@@ -2570,7 +2626,7 @@ return /******/ (function(modules) { // webpackBootstrap
2570
2626
  });
2571
2627
 
2572
2628
  /***/ }),
2573
- /* 82 */
2629
+ /* 81 */
2574
2630
  /***/ (function(module, exports) {
2575
2631
 
2576
2632
  'use strict';
@@ -2593,7 +2649,7 @@ return /******/ (function(modules) { // webpackBootstrap
2593
2649
  }
2594
2650
 
2595
2651
  /***/ }),
2596
- /* 83 */
2652
+ /* 82 */
2597
2653
  /***/ (function(module, exports) {
2598
2654
 
2599
2655
  /* global globalThis */
@@ -2627,7 +2683,7 @@ return /******/ (function(modules) { // webpackBootstrap
2627
2683
  module.exports = exports['default'];
2628
2684
 
2629
2685
  /***/ }),
2630
- /* 84 */
2686
+ /* 83 */
2631
2687
  /***/ (function(module, exports) {
2632
2688
 
2633
2689
  'use strict';
@@ -2662,7 +2718,7 @@ return /******/ (function(modules) { // webpackBootstrap
2662
2718
  module.exports = exports['default'];
2663
2719
 
2664
2720
  /***/ }),
2665
- /* 85 */
2721
+ /* 84 */
2666
2722
  /***/ (function(module, exports, __webpack_require__) {
2667
2723
 
2668
2724
  'use strict';
@@ -2675,11 +2731,11 @@ return /******/ (function(modules) { // webpackBootstrap
2675
2731
  exports.parseWithoutProcessing = parseWithoutProcessing;
2676
2732
  exports.parse = parse;
2677
2733
 
2678
- var _parser = __webpack_require__(86);
2734
+ var _parser = __webpack_require__(85);
2679
2735
 
2680
2736
  var _parser2 = _interopRequireDefault(_parser);
2681
2737
 
2682
- var _whitespaceControl = __webpack_require__(87);
2738
+ var _whitespaceControl = __webpack_require__(86);
2683
2739
 
2684
2740
  var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl);
2685
2741
 
@@ -2720,7 +2776,7 @@ return /******/ (function(modules) { // webpackBootstrap
2720
2776
  }
2721
2777
 
2722
2778
  /***/ }),
2723
- /* 86 */
2779
+ /* 85 */
2724
2780
  /***/ (function(module, exports) {
2725
2781
 
2726
2782
  // File ignored in coverage tests via setting in .istanbul.yml
@@ -3461,7 +3517,7 @@ return /******/ (function(modules) { // webpackBootstrap
3461
3517
  module.exports = exports["default"];
3462
3518
 
3463
3519
  /***/ }),
3464
- /* 87 */
3520
+ /* 86 */
3465
3521
  /***/ (function(module, exports, __webpack_require__) {
3466
3522
 
3467
3523
  'use strict';
@@ -3470,7 +3526,7 @@ return /******/ (function(modules) { // webpackBootstrap
3470
3526
 
3471
3527
  exports.__esModule = true;
3472
3528
 
3473
- var _visitor = __webpack_require__(88);
3529
+ var _visitor = __webpack_require__(87);
3474
3530
 
3475
3531
  var _visitor2 = _interopRequireDefault(_visitor);
3476
3532
 
@@ -3516,7 +3572,7 @@ return /******/ (function(modules) { // webpackBootstrap
3516
3572
  // If we are on a standalone node, save the indent info for partials
3517
3573
  if (current.type === 'PartialStatement') {
3518
3574
  // Pull out the whitespace from the final line
3519
- current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
3575
+ current.indent = /(?:^|[^ \t])([ \t]+)$/.exec(body[i - 1].original)[1];
3520
3576
  }
3521
3577
  }
3522
3578
  }
@@ -3624,7 +3680,9 @@ return /******/ (function(modules) { // webpackBootstrap
3624
3680
  }
3625
3681
 
3626
3682
  if (prev.type === 'ContentStatement') {
3627
- return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original);
3683
+ // Matches when the trailing whitespace contains a newline (or, for the
3684
+ // root's first node, is the whole string). See omitLeft for the anchoring.
3685
+ return (sibling || !isRoot ? /(?:^|\S)[^\S\n]*\n\s*$/ : /^\s*$|(?:^|\S)[^\S\n]*\n\s*$/).test(prev.original);
3628
3686
  }
3629
3687
  }
3630
3688
  function isNextWhitespace(body, i, isRoot) {
@@ -3676,7 +3734,10 @@ return /******/ (function(modules) { // webpackBootstrap
3676
3734
 
3677
3735
  // We omit the last node if it's whitespace only and not preceded by a non-content node.
3678
3736
  var original = current.value;
3679
- current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
3737
+ // The match must start after a non-whitespace character (or at the start):
3738
+ // a plain /\s+$/ is retried at every position of a whitespace run, which
3739
+ // takes quadratic time on long runs.
3740
+ current.value = current.value.replace(multiple ? /(^|\S)\s+$/ : /(^|[^ \t])[ \t]+$/, '$1');
3680
3741
  current.leftStripped = current.value !== original;
3681
3742
  return current.leftStripped;
3682
3743
  }
@@ -3685,7 +3746,7 @@ return /******/ (function(modules) { // webpackBootstrap
3685
3746
  module.exports = exports['default'];
3686
3747
 
3687
3748
  /***/ }),
3688
- /* 88 */
3749
+ /* 87 */
3689
3750
  /***/ (function(module, exports, __webpack_require__) {
3690
3751
 
3691
3752
  'use strict';
@@ -3698,10 +3759,34 @@ return /******/ (function(modules) { // webpackBootstrap
3698
3759
 
3699
3760
  var _exception2 = _interopRequireDefault(_exception);
3700
3761
 
3762
+ var _utils = __webpack_require__(5);
3763
+
3764
+ var _nodeTypes = __webpack_require__(88);
3765
+
3701
3766
  function Visitor() {
3702
3767
  this.parents = [];
3703
3768
  }
3704
3769
 
3770
+ /**
3771
+ * Whether a visitor may dispatch on a node type.
3772
+ *
3773
+ * A type is dispatchable if the parser produces it, or if the visitor
3774
+ * (typically a subclass) defines a handler for it that is not a method of
3775
+ * Visitor itself. `in` is used for the latter so that accept, acceptKey and
3776
+ * everything inherited from Object.prototype (constructor, __proto__, ...)
3777
+ * can never be reached through a crafted `type`.
3778
+ *
3779
+ * Subclasses that visit untrusted ASTs, such as WhitespaceControl, must
3780
+ * therefore define nothing but node handlers as function properties.
3781
+ *
3782
+ * @param {Visitor} visitor the visitor that would handle the node
3783
+ * @param {*} type the untrusted `type` of the node
3784
+ * @returns {boolean} true if the visitor may call its `type` method
3785
+ */
3786
+ function isDispatchable(visitor, type) {
3787
+ return _utils.indexOf(_nodeTypes.NODE_TYPES, type) !== -1 || !(type in Visitor.prototype) && typeof visitor[type] === 'function';
3788
+ }
3789
+
3705
3790
  Visitor.prototype = {
3706
3791
  constructor: Visitor,
3707
3792
  mutating: false,
@@ -3710,9 +3795,7 @@ return /******/ (function(modules) { // webpackBootstrap
3710
3795
  acceptKey: function acceptKey(node, name) {
3711
3796
  var value = this.accept(node[name]);
3712
3797
  if (this.mutating) {
3713
- // Hacky sanity check: This may have a few false positives for type for the helper
3714
- // methods but will generally do the right thing without a lot of overhead.
3715
- if (value && !Visitor.prototype[value.type]) {
3798
+ if (value && !isDispatchable(this, value.type)) {
3716
3799
  throw new _exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
3717
3800
  }
3718
3801
  node[name] = value;
@@ -3748,8 +3831,9 @@ return /******/ (function(modules) { // webpackBootstrap
3748
3831
  return;
3749
3832
  }
3750
3833
 
3751
- /* istanbul ignore next: Sanity code */
3752
- if (!this[object.type]) {
3834
+ // Dispatching on an unknown type could call an arbitrary method of the
3835
+ // visitor (e.g. `accept` itself) with an untrusted AST node.
3836
+ if (!isDispatchable(this, object.type) || !this[object.type]) {
3753
3837
  throw new _exception2['default']('Unknown type: ' + object.type, object);
3754
3838
  }
3755
3839
 
@@ -3827,6 +3911,20 @@ return /******/ (function(modules) { // webpackBootstrap
3827
3911
  exports['default'] = Visitor;
3828
3912
  module.exports = exports['default'];
3829
3913
 
3914
+ /***/ }),
3915
+ /* 88 */
3916
+ /***/ (function(module, exports) {
3917
+
3918
+ // AST node types produced by the parser. Visitor#accept and Compiler#accept
3919
+ // dispatch on the untrusted `node.type`, so they must never call any other
3920
+ // method of the visiting object. The Compiler only accepts these types; a
3921
+ // Visitor also accepts types for which a subclass defines its own handler.
3922
+ 'use strict';
3923
+
3924
+ exports.__esModule = true;
3925
+ var NODE_TYPES = ['Program', 'BlockStatement', 'DecoratorBlock', 'PartialStatement', 'PartialBlockStatement', 'Decorator', 'MustacheStatement', 'ContentStatement', 'CommentStatement', 'SubExpression', 'PathExpression', 'StringLiteral', 'NumberLiteral', 'BooleanLiteral', 'UndefinedLiteral', 'NullLiteral', 'Hash', 'HashPair'];
3926
+ exports.NODE_TYPES = NODE_TYPES;
3927
+
3830
3928
  /***/ }),
3831
3929
  /* 89 */
3832
3930
  /***/ (function(module, exports, __webpack_require__) {
@@ -4081,10 +4179,12 @@ return /******/ (function(modules) { // webpackBootstrap
4081
4179
 
4082
4180
  var _utils = __webpack_require__(5);
4083
4181
 
4084
- var _ast = __webpack_require__(84);
4182
+ var _ast = __webpack_require__(83);
4085
4183
 
4086
4184
  var _ast2 = _interopRequireDefault(_ast);
4087
4185
 
4186
+ var _nodeTypes = __webpack_require__(88);
4187
+
4088
4188
  var slice = [].slice;
4089
4189
 
4090
4190
  function Compiler() {}
@@ -4164,8 +4264,8 @@ return /******/ (function(modules) { // webpackBootstrap
4164
4264
  },
4165
4265
 
4166
4266
  accept: function accept(node) {
4167
- /* istanbul ignore next: Sanity code */
4168
- if (!this[node.type]) {
4267
+ // Dispatching on an unknown type could call an arbitrary Compiler method.
4268
+ if (_utils.indexOf(_nodeTypes.NODE_TYPES, node.type) === -1 || !this[node.type]) {
4169
4269
  throw new _exception2['default']('Unknown type: ' + node.type, node);
4170
4270
  }
4171
4271
 
@@ -4176,6 +4276,7 @@ return /******/ (function(modules) { // webpackBootstrap
4176
4276
  },
4177
4277
 
4178
4278
  Program: function Program(program) {
4279
+ validateBlockParams(program);
4179
4280
  this.options.blockParams.unshift(program.blockParams);
4180
4281
 
4181
4282
  var body = program.body,
@@ -4291,6 +4392,9 @@ return /******/ (function(modules) { // webpackBootstrap
4291
4392
  },
4292
4393
 
4293
4394
  ContentStatement: function ContentStatement(content) {
4395
+ if (content.value != null && typeof content.value !== 'string') {
4396
+ throw invalidAst(content, 'value must be a string');
4397
+ }
4294
4398
  if (content.value) {
4295
4399
  this.opcode('appendContent', content.value);
4296
4400
  }
@@ -4315,7 +4419,7 @@ return /******/ (function(modules) { // webpackBootstrap
4315
4419
  name = path.parts[0],
4316
4420
  isBlock = program != null || inverse != null;
4317
4421
 
4318
- this.opcode('getContext', path.depth);
4422
+ this.opcode('getContext', validateDepth(path));
4319
4423
 
4320
4424
  this.opcode('pushProgram', program);
4321
4425
  this.opcode('pushProgram', inverse);
@@ -4352,35 +4456,43 @@ return /******/ (function(modules) { // webpackBootstrap
4352
4456
  },
4353
4457
 
4354
4458
  PathExpression: function PathExpression(path) {
4355
- this.addDepth(path.depth);
4356
- this.opcode('getContext', path.depth);
4459
+ // Validate AST values at the compiler boundary: javascript-compiler.js
4460
+ // trusts all opcode arguments to be safe.
4461
+ var depth = validateDepth(path);
4462
+ var parts = validateParts(path);
4357
4463
 
4358
- var name = path.parts[0],
4464
+ this.addDepth(depth);
4465
+ this.opcode('getContext', depth);
4466
+
4467
+ var name = parts[0],
4359
4468
  scoped = _ast2['default'].helpers.scopedId(path),
4360
- blockParamId = !path.depth && !scoped && this.blockParamIndex(name);
4469
+ blockParamId = !depth && !scoped && this.blockParamIndex(name);
4361
4470
 
4362
4471
  if (blockParamId) {
4363
- this.opcode('lookupBlockParam', blockParamId, path.parts);
4472
+ this.opcode('lookupBlockParam', blockParamId, parts);
4364
4473
  } else if (!name) {
4365
4474
  // Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
4366
4475
  this.opcode('pushContext');
4367
4476
  } else if (path.data) {
4368
4477
  this.options.data = true;
4369
- this.opcode('lookupData', path.depth, path.parts, path.strict);
4478
+ this.opcode('lookupData', depth, parts, path.strict);
4370
4479
  } else {
4371
- this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped);
4480
+ this.opcode('lookupOnContext', parts, path.falsy, path.strict, scoped);
4372
4481
  }
4373
4482
  },
4374
4483
 
4375
4484
  StringLiteral: function StringLiteral(string) {
4485
+ validateLiteral(string);
4376
4486
  this.opcode('pushString', string.value);
4377
4487
  },
4378
4488
 
4379
4489
  NumberLiteral: function NumberLiteral(number) {
4490
+ validateLiteral(number);
4380
4491
  this.opcode('pushLiteral', number.value);
4381
4492
  },
4382
4493
 
4383
4494
  BooleanLiteral: function BooleanLiteral(bool) {
4495
+ validateLiteral(bool);
4384
4496
  this.opcode('pushLiteral', bool.value);
4385
4497
  },
4386
4498
 
@@ -4395,7 +4507,17 @@ return /******/ (function(modules) { // webpackBootstrap
4395
4507
  Hash: function Hash(hash) {
4396
4508
  var pairs = hash.pairs,
4397
4509
  i = 0,
4398
- l = pairs.length;
4510
+ l = undefined;
4511
+
4512
+ if (!_utils.isArray(pairs)) {
4513
+ throw invalidAst(hash, 'pairs must be an array');
4514
+ }
4515
+ l = pairs.length;
4516
+ for (var j = 0; j < l; j++) {
4517
+ if (!pairs[j] || typeof pairs[j].key !== 'string') {
4518
+ throw invalidAst(hash, 'pair keys must be strings');
4519
+ }
4520
+ }
4399
4521
 
4400
4522
  this.opcode('pushHash');
4401
4523
 
@@ -4426,6 +4548,9 @@ return /******/ (function(modules) { // webpackBootstrap
4426
4548
  },
4427
4549
 
4428
4550
  classifySexpr: function classifySexpr(sexpr) {
4551
+ // The first part is used as a helper name below, e.g. for known helpers,
4552
+ // before the PathExpression handler gets to validate the parts.
4553
+ validateParts(sexpr.path);
4429
4554
  var isSimple = _ast2['default'].helpers.simpleId(sexpr.path);
4430
4555
 
4431
4556
  var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);
@@ -4467,17 +4592,26 @@ return /******/ (function(modules) { // webpackBootstrap
4467
4592
  },
4468
4593
 
4469
4594
  pushParam: function pushParam(val) {
4595
+ // Literal params are not visited by their node handlers in stringParams
4596
+ // mode, so their values must be validated here as well.
4597
+ validateLiteral(val);
4470
4598
  var value = val.value != null ? val.value : val.original || '';
4599
+ var depth = validateDepth(val);
4471
4600
 
4472
4601
  if (this.stringParams) {
4602
+ // pushStringParam emits non-string values as raw JavaScript literals.
4603
+ // Literal values are validated above, but `original` is not, so
4604
+ // anything that is not a number or boolean is stringified.
4605
+ if (typeof value !== 'number' && typeof value !== 'boolean') {
4606
+ value = String(value);
4607
+ }
4473
4608
  if (value.replace) {
4474
4609
  value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.');
4475
4610
  }
4476
-
4477
- if (val.depth) {
4478
- this.addDepth(val.depth);
4611
+ if (depth) {
4612
+ this.addDepth(depth);
4479
4613
  }
4480
- this.opcode('getContext', val.depth || 0);
4614
+ this.opcode('getContext', depth);
4481
4615
  this.opcode('pushStringParam', value, val.type);
4482
4616
 
4483
4617
  if (val.type === 'SubExpression') {
@@ -4488,8 +4622,10 @@ return /******/ (function(modules) { // webpackBootstrap
4488
4622
  } else {
4489
4623
  if (this.trackIds) {
4490
4624
  var blockParamIndex = undefined;
4491
- if (val.parts && !_ast2['default'].helpers.scopedId(val) && !val.depth) {
4492
- blockParamIndex = this.blockParamIndex(val.parts[0]);
4625
+ if (val.parts && !_ast2['default'].helpers.scopedId(val) && !depth) {
4626
+ // The parts are read here before the PathExpression handler,
4627
+ // invoked by accept() below, gets to validate them.
4628
+ blockParamIndex = this.blockParamIndex(validateParts(val)[0]);
4493
4629
  }
4494
4630
  if (blockParamIndex) {
4495
4631
  var blockParamChild = val.parts.slice(1).join('.');
@@ -4598,6 +4734,111 @@ return /******/ (function(modules) { // webpackBootstrap
4598
4734
  return ret;
4599
4735
  }
4600
4736
 
4737
+ /**
4738
+ * Create the exception thrown for an AST node that fails validation.
4739
+ *
4740
+ * @param {Object} node the offending AST node; its `type` is included in the
4741
+ * message and its `loc`, if present, in the error location
4742
+ * @param {string} message what is wrong with the node
4743
+ * @returns {Exception} the exception to throw
4744
+ */
4745
+ function invalidAst(node, message) {
4746
+ return new _exception2['default']('Invalid AST: ' + node.type + ' ' + message, node);
4747
+ }
4748
+
4749
+ /**
4750
+ * Validate the `depth` of a path or param node.
4751
+ *
4752
+ * A missing depth is treated as 0, because ASTs built by hand or by other
4753
+ * tools commonly omit it. Any depth that is present must be a non-negative
4754
+ * integer, since it is written into the generated code.
4755
+ *
4756
+ * @param {Object} node the AST node whose `depth` is validated
4757
+ * @returns {number} the validated depth, or 0 if the node has none
4758
+ * @throws {Exception} if the depth is present but not a non-negative integer
4759
+ */
4760
+ function validateDepth(node) {
4761
+ var depth = node.depth;
4762
+ if (depth == null) {
4763
+ return 0;
4764
+ }
4765
+ if (typeof depth !== 'number' || !isFinite(depth) || Math.floor(depth) !== depth || depth < 0) {
4766
+ throw invalidAst(node, 'depth must be a non-negative integer');
4767
+ }
4768
+ return depth;
4769
+ }
4770
+
4771
+ /**
4772
+ * Validate the `value` of a StringLiteral, NumberLiteral or BooleanLiteral.
4773
+ *
4774
+ * Literal values are written into the generated code, so they must have
4775
+ * exactly the type their node promises. Nodes of any other type pass through
4776
+ * unchanged.
4777
+ *
4778
+ * Numbers need not be finite: the parser produces Infinity for very long
4779
+ * number literals, and Infinity and NaN are emitted as the read-only globals
4780
+ * of the same name.
4781
+ *
4782
+ * @param {Object} node the AST node to validate
4783
+ * @throws {Exception} if the value does not match the literal type
4784
+ */
4785
+ function validateLiteral(node) {
4786
+ var value = node.value;
4787
+ if (node.type === 'StringLiteral' && typeof value !== 'string') {
4788
+ throw invalidAst(node, 'value must be a string');
4789
+ } else if (node.type === 'NumberLiteral' && typeof value !== 'number') {
4790
+ throw invalidAst(node, 'value must be a number');
4791
+ } else if (node.type === 'BooleanLiteral' && typeof value !== 'boolean') {
4792
+ throw invalidAst(node, 'value must be a boolean');
4793
+ }
4794
+ }
4795
+
4796
+ /**
4797
+ * Validate that a node property is an array containing only strings.
4798
+ *
4799
+ * @param {Object} node the AST node the property belongs to, for the error
4800
+ * @param {*} value the property value to validate
4801
+ * @param {string} name the property name, used in the error message
4802
+ * @returns {string[]} the validated array
4803
+ * @throws {Exception} if the value is not an array or contains non-strings
4804
+ */
4805
+ function validateStringArray(node, value, name) {
4806
+ if (!_utils.isArray(value)) {
4807
+ throw invalidAst(node, name + ' must be an array');
4808
+ }
4809
+ for (var i = 0; i < value.length; i++) {
4810
+ if (typeof value[i] !== 'string') {
4811
+ throw invalidAst(node, name + ' must only contain strings');
4812
+ }
4813
+ }
4814
+ return value;
4815
+ }
4816
+
4817
+ /**
4818
+ * Validate the `parts` of a PathExpression node.
4819
+ *
4820
+ * @param {Object} path the PathExpression node
4821
+ * @returns {string[]} the validated parts
4822
+ * @throws {Exception} if `parts` is not an array of strings
4823
+ */
4824
+ function validateParts(path) {
4825
+ return validateStringArray(path, path.parts, 'parts');
4826
+ }
4827
+
4828
+ /**
4829
+ * Validate the optional `blockParams` of a Program node. Its length is
4830
+ * written into the generated code, so it must be an array of strings when
4831
+ * present.
4832
+ *
4833
+ * @param {Object} program the Program node
4834
+ * @throws {Exception} if `blockParams` is present and not an array of strings
4835
+ */
4836
+ function validateBlockParams(program) {
4837
+ if (program.blockParams != null) {
4838
+ validateStringArray(program, program.blockParams, 'blockParams');
4839
+ }
4840
+ }
4841
+
4601
4842
  function argEquals(a, b) {
4602
4843
  if (a === b) {
4603
4844
  return true;
@@ -4666,7 +4907,7 @@ return /******/ (function(modules) { // webpackBootstrap
4666
4907
  return this.internalNameLookup(parent, name);
4667
4908
  },
4668
4909
  depthedLookup: function depthedLookup(name) {
4669
- return [this.aliasable('container.lookup'), '(depths, ', JSON.stringify(name), ')'];
4910
+ return [this.aliasable('container.lookup'), '(depths, ', this.quotedString(name), ')'];
4670
4911
  },
4671
4912
 
4672
4913
  compilerInfo: function compilerInfo() {
@@ -4701,7 +4942,7 @@ return /******/ (function(modules) { // webpackBootstrap
4701
4942
  // END PUBLIC API
4702
4943
  internalNameLookup: function internalNameLookup(parent, name) {
4703
4944
  this.lookupPropertyFunctionIsUsed = true;
4704
- return ['lookupProperty(', parent, ',', JSON.stringify(name), ')'];
4945
+ return ['lookupProperty(', parent, ',', this.quotedString(name), ')'];
4705
4946
  },
4706
4947
 
4707
4948
  lookupPropertyFunctionIsUsed: false,
@@ -4794,12 +5035,10 @@ return /******/ (function(modules) { // webpackBootstrap
4794
5035
  var decorators = _context.decorators;
4795
5036
 
4796
5037
  for (i = 0, l = programs.length; i < l; i++) {
4797
- if (programs[i]) {
4798
- ret[i] = programs[i];
4799
- if (decorators[i]) {
4800
- ret[i + '_d'] = decorators[i];
4801
- ret.useDecorators = true;
4802
- }
5038
+ ret[i] = programs[i];
5039
+ if (decorators[i]) {
5040
+ ret[i + '_d'] = decorators[i];
5041
+ ret.useDecorators = true;
4803
5042
  }
4804
5043
  }
4805
5044
 
@@ -5125,20 +5364,21 @@ return /******/ (function(modules) { // webpackBootstrap
5125
5364
  this.resolvePath('data', parts, 0, true, strict);
5126
5365
  },
5127
5366
 
5128
- resolvePath: function resolvePath(type, parts, i, falsy, strict) {
5367
+ resolvePath: function resolvePath(type, parts, startPartIndex, falsy, strict) {
5129
5368
  // istanbul ignore next
5130
5369
 
5131
5370
  var _this2 = this;
5132
5371
 
5133
5372
  if (this.options.strict || this.options.assumeObjects) {
5134
- this.push(strictLookup(this.options.strict && strict, this, parts, i, type));
5373
+ this.push(strictLookup(this.options.strict && strict, this, parts, startPartIndex, type));
5135
5374
  return;
5136
5375
  }
5137
5376
 
5138
5377
  var len = parts.length;
5139
- for (; i < len; i++) {
5378
+
5379
+ var _loop = function (i) {
5140
5380
  /* eslint-disable no-loop-func */
5141
- this.replaceStack(function (current) {
5381
+ _this2.replaceStack(function (current) {
5142
5382
  var lookup = _this2.nameLookup(current, parts[i], type);
5143
5383
  // We want to ensure that zero and false are handled properly if the context (falsy flag)
5144
5384
  // needs to have the special handling for these values.
@@ -5150,6 +5390,10 @@ return /******/ (function(modules) { // webpackBootstrap
5150
5390
  }
5151
5391
  });
5152
5392
  /* eslint-enable no-loop-func */
5393
+ };
5394
+
5395
+ for (var i = startPartIndex; i < len; i++) {
5396
+ _loop(i);
5153
5397
  }
5154
5398
  },
5155
5399
 
@@ -5267,7 +5511,12 @@ return /******/ (function(modules) { // webpackBootstrap
5267
5511
  var foundDecorator = this.nameLookup('decorators', name, 'decorator'),
5268
5512
  options = this.setupHelperArgs(name, paramSize);
5269
5513
 
5270
- this.decorators.push(['fn = ', this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]), ' || fn;']);
5514
+ // Store the resolved decorator in a variable and verify it is a function before
5515
+ // calling it. Without this, unregistered decorators can cause an unhandled TypeError
5516
+ // (calling undefined), which crashes the process — enabling Denial of Service.
5517
+ this.decorators.push(['var decorator = ', foundDecorator, ';']);
5518
+ this.decorators.push(['if (typeof decorator !== "function") { throw new Error(', this.quotedString('Missing decorator: "' + name + '"'), '); }']);
5519
+ this.decorators.push(['fn = ', this.decorators.functionCall('decorator', '', ['fn', 'props', 'container', options]), ' || fn;']);
5271
5520
  },
5272
5521
 
5273
5522
  // [invokeHelper]
@@ -5368,7 +5617,7 @@ return /******/ (function(modules) { // webpackBootstrap
5368
5617
  }
5369
5618
 
5370
5619
  if (indent) {
5371
- options.indent = JSON.stringify(indent);
5620
+ options.indent = this.quotedString(indent);
5372
5621
  }
5373
5622
  options.helpers = 'helpers';
5374
5623
  options.partials = 'partials';
@@ -5424,7 +5673,7 @@ return /******/ (function(modules) { // webpackBootstrap
5424
5673
 
5425
5674
  pushId: function pushId(type, name, child) {
5426
5675
  if (type === 'BlockParam') {
5427
- this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + JSON.stringify('.' + child) : ''));
5676
+ this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + this.quotedString('.' + child) : ''));
5428
5677
  } else if (type === 'PathExpression') {
5429
5678
  this.pushString(name);
5430
5679
  } else if (type === 'SubExpression') {
@@ -5450,8 +5699,8 @@ return /******/ (function(modules) { // webpackBootstrap
5450
5699
  var existing = this.matchExistingProgram(child);
5451
5700
 
5452
5701
  if (existing == null) {
5453
- this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
5454
- var index = this.context.programs.length;
5702
+ // Placeholder to prevent name conflicts for nested children
5703
+ var index = this.context.programs.push('') - 1;
5455
5704
  child.index = index;
5456
5705
  child.name = 'program' + index;
5457
5706
  this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile);
@@ -5739,7 +5988,7 @@ return /******/ (function(modules) { // webpackBootstrap
5739
5988
 
5740
5989
  setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) {
5741
5990
  var options = this.setupParams(helper, paramSize, params);
5742
- options.loc = JSON.stringify(this.source.currentLocation);
5991
+ options.loc = locationLiteral(this);
5743
5992
  options = this.objectLiteral(options);
5744
5993
  if (useRegister) {
5745
5994
  this.useRegister('options');
@@ -5771,19 +6020,35 @@ return /******/ (function(modules) { // webpackBootstrap
5771
6020
  return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name);
5772
6021
  };
5773
6022
 
5774
- function strictLookup(requireTerminal, compiler, parts, i, type) {
6023
+ /**
6024
+ * The compiler's current source location as a JavaScript literal.
6025
+ *
6026
+ * Hand-built ASTs may omit `loc`; emitting the literal `undefined` then
6027
+ * keeps the generated code valid and drops the key from object literals.
6028
+ * The location's `source` is the `srcName` option, so it is escaped like
6029
+ * every other string in the generated code.
6030
+ *
6031
+ * @param {JavaScriptCompiler} compiler the compiler whose location is emitted
6032
+ * @returns {string} a JSON object literal, or the string "undefined"
6033
+ */
6034
+ function locationLiteral(compiler) {
6035
+ var loc = compiler.source.currentLocation;
6036
+ return loc ? _codeGen.safeJsonLiteral(loc) : 'undefined';
6037
+ }
6038
+
6039
+ function strictLookup(requireTerminal, compiler, parts, startPartIndex, type) {
5775
6040
  var stack = compiler.popStack(),
5776
6041
  len = parts.length;
5777
6042
  if (requireTerminal) {
5778
6043
  len--;
5779
6044
  }
5780
6045
 
5781
- for (; i < len; i++) {
6046
+ for (var i = startPartIndex; i < len; i++) {
5782
6047
  stack = compiler.nameLookup(stack, parts[i], type);
5783
6048
  }
5784
6049
 
5785
6050
  if (requireTerminal) {
5786
- return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ', ', JSON.stringify(compiler.source.currentLocation), ' )'];
6051
+ return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[len]), ', ', locationLiteral(compiler), ' )'];
5787
6052
  } else {
5788
6053
  return stack;
5789
6054
  }
@@ -5802,9 +6067,62 @@ return /******/ (function(modules) { // webpackBootstrap
5802
6067
  var _Object$keys = __webpack_require__(60)['default'];
5803
6068
 
5804
6069
  exports.__esModule = true;
6070
+ exports.safeJsonLiteral = safeJsonLiteral;
5805
6071
 
5806
6072
  var _utils = __webpack_require__(5);
5807
6073
 
6074
+ // Matches any character that quotedString may have to escape: quotes,
6075
+ // backslashes, control characters, line separators, surrogates and "<". Most
6076
+ // strings, e.g. lookup names, contain none of them and can skip the escaping.
6077
+ // eslint-disable-next-line no-control-regex
6078
+ var NEEDS_ESCAPING = /[\x00-\x1f\\"\u2028\u2029<\ud800-\udfff]/;
6079
+
6080
+ // Per HTML "Restrictions for contents of script elements": "<!--", "<script"
6081
+ // and "</script" can end or change the parsing of an enclosing <script>
6082
+ // element.
6083
+ var SCRIPT_DELIMITERS = /<(?=!--|\/?script)/gi;
6084
+
6085
+ /**
6086
+ * Escape the "<" of every sequence that could end or change the parsing of
6087
+ * an enclosing <script> element.
6088
+ *
6089
+ * Only pass JavaScript or JSON code in which "<" can only occur inside string
6090
+ * literals, where "\u003C" has the same value.
6091
+ *
6092
+ * @param {string} code the code to escape
6093
+ * @returns {string} the escaped code
6094
+ */
6095
+ function escapeScriptDelimiters(code) {
6096
+ return code.replace(SCRIPT_DELIMITERS, '\\u003C');
6097
+ }
6098
+
6099
+ /**
6100
+ * Escape a UTF-16 surrogate code unit as a "\uXXXX" escape sequence.
6101
+ *
6102
+ * @param {string} surrogate a single surrogate code unit
6103
+ * @returns {string} the escape sequence
6104
+ */
6105
+ function escapeSurrogate(surrogate) {
6106
+ return '\\u' + surrogate.charCodeAt(0).toString(16);
6107
+ }
6108
+
6109
+ /**
6110
+ * Convert a value to a JSON literal that is safe to write into generated
6111
+ * code: valid in ES5, where line and paragraph separators end string literals,
6112
+ * encodable as UTF-8 on every engine, and safe inside an enclosing <script>
6113
+ * element.
6114
+ *
6115
+ * @param {*} value the value to convert
6116
+ * @returns {string} the JavaScript literal
6117
+ */
6118
+
6119
+ function safeJsonLiteral(value) {
6120
+ return escapeScriptDelimiters(JSON.stringify(value).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029')
6121
+ // Engines before ES2019 leave lone surrogates unescaped, and those cannot
6122
+ // be encoded as UTF-8. Escaping paired ones as well keeps the value.
6123
+ .replace(/[\ud800-\udfff]/g, escapeSurrogate));
6124
+ }
6125
+
5808
6126
  var SourceNode = undefined;
5809
6127
 
5810
6128
  try {
@@ -5916,8 +6234,16 @@ return /******/ (function(modules) { // webpackBootstrap
5916
6234
  },
5917
6235
 
5918
6236
  quotedString: function quotedString(str) {
5919
- return '"' + (str + '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
5920
- .replace(/\u2029/g, '\\u2029') + '"';
6237
+ str = str + '';
6238
+ // Fast path: quotedString runs for every lookup name and content string,
6239
+ // and the chain of replacements below is much slower than a single test.
6240
+ if (!NEEDS_ESCAPING.test(str)) {
6241
+ return '"' + str + '"';
6242
+ }
6243
+
6244
+ // JSON.stringify escapes quotes, backslashes and control characters;
6245
+ // safeJsonLiteral adds line separators, surrogates and script delimiters.
6246
+ return safeJsonLiteral(str);
5921
6247
  },
5922
6248
 
5923
6249
  objectLiteral: function objectLiteral(obj) {
@@ -5964,7 +6290,6 @@ return /******/ (function(modules) { // webpackBootstrap
5964
6290
  };
5965
6291
 
5966
6292
  exports['default'] = CodeGen;
5967
- module.exports = exports['default'];
5968
6293
 
5969
6294
  /***/ })
5970
6295
  /******/ ])