handlebars-source 4.7.9 → 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 +396 -125
  3. data/handlebars.runtime.js +94 -23
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79b760036a203b609cbbc1d0bc2e0b19804fe3d45a0c90ce2e19092009822912
4
- data.tar.gz: 3a039c765182c00cc4cada63cae3b39b22b23ef77fb02f3e54198f4e7b7942a8
3
+ metadata.gz: e468b3e179e7f5bd9af2b141167fd890a0e8a54e626d94f22b34f5749ac37496
4
+ data.tar.gz: 5adb7d8a935bd975df2ac2bd238e824932f52902aa12730a3fb1e2be06cfea4f
5
5
  SHA512:
6
- metadata.gz: 5d5c3c92272132af5235ff5bb76dc6a36b93464a5ecaf385113191328c3bab2e7326148b92fab075e3bc9ac2cc998600cc86aa1254e3145fdc72296d4334b3f1
7
- data.tar.gz: 4520b66ebcfb3a958ac2a9e743afb38c8164a43b65217a04497ae641d7d4121989ab64ec5aa487128fe171935bbda78c6040356e9fb1bff3594f146edae174c9
6
+ metadata.gz: 7d2ae7c9fe7b55127605691418d72853139a96a6ea895c4f5e9d031bf00a91bbdec6d6a2a2be7abccd654df27e4b529b446eb36399a18113168540426a2a13a2
7
+ data.tar.gz: 6ee67cec7d885fd3fa3f25300cde22fb8e53b3a5e81c2ed85553a51ae79050ab69a263225e35eb25ea212993a8c0a6e218944a9d013b90e8f4904c8bad57cd2f
data/handlebars.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**!
2
2
 
3
3
  @license
4
- handlebars v4.7.9
4
+ handlebars v4.7.10
5
5
 
6
6
  Copyright (C) 2011-2019 by Yehuda Katz
7
7
 
@@ -98,9 +98,9 @@ return /******/ (function(modules) { // webpackBootstrap
98
98
 
99
99
  var _handlebarsCompilerBase = __webpack_require__(84);
100
100
 
101
- var _handlebarsCompilerCompiler = __webpack_require__(89);
101
+ var _handlebarsCompilerCompiler = __webpack_require__(90);
102
102
 
103
- var _handlebarsCompilerJavascriptCompiler = __webpack_require__(90);
103
+ var _handlebarsCompilerJavascriptCompiler = __webpack_require__(91);
104
104
 
105
105
  var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler);
106
106
 
@@ -278,7 +278,7 @@ return /******/ (function(modules) { // webpackBootstrap
278
278
 
279
279
  var _internalProtoAccess = __webpack_require__(73);
280
280
 
281
- var VERSION = '4.7.9';
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
  }
@@ -728,6 +733,26 @@ return /******/ (function(modules) { // webpackBootstrap
728
733
 
729
734
  var _exception2 = _interopRequireDefault(_exception);
730
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
+
731
756
  exports['default'] = function (instance) {
732
757
  instance.registerHelper('each', function (context, options) {
733
758
  if (!options) {
@@ -753,7 +778,7 @@ return /******/ (function(modules) { // webpackBootstrap
753
778
  data = _utils.createFrame(options.data);
754
779
  }
755
780
 
756
- function execIteration(field, index, last) {
781
+ function execIteration(field, index, last, value) {
757
782
  if (data) {
758
783
  data.key = field;
759
784
  data.index = index;
@@ -765,9 +790,9 @@ return /******/ (function(modules) { // webpackBootstrap
765
790
  }
766
791
  }
767
792
 
768
- ret = ret + fn(context[field], {
793
+ ret = ret + fn(value, {
769
794
  data: data,
770
- blockParams: _utils.blockParams([context[field], field], [contextPath + field, null])
795
+ blockParams: _utils.blockParams([value, field], [contextPath + field, null])
771
796
  });
772
797
  }
773
798
 
@@ -775,18 +800,24 @@ return /******/ (function(modules) { // webpackBootstrap
775
800
  if (_utils.isArray(context)) {
776
801
  for (var j = context.length; i < j; i++) {
777
802
  if (i in context) {
778
- execIteration(i, i, i === context.length - 1);
803
+ execIteration(i, i, i === context.length - 1, context[i]);
779
804
  }
780
805
  }
781
806
  } else if (typeof _Symbol === 'function' && context[_Symbol$iterator]) {
782
- var newContext = [];
783
807
  var iterator = _getIterator(context);
784
- for (var it = iterator.next(); !it.done; it = iterator.next()) {
785
- newContext.push(it.value);
786
- }
787
- context = newContext;
788
- for (var j = context.length; i < j; i++) {
789
- 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++;
790
821
  }
791
822
  } else {
792
823
  (function () {
@@ -797,13 +828,13 @@ return /******/ (function(modules) { // webpackBootstrap
797
828
  // the last iteration without have to scan the object twice and create
798
829
  // an itermediate keys array.
799
830
  if (priorKey !== undefined) {
800
- execIteration(priorKey, i - 1);
831
+ execIteration(priorKey, i - 1, false, context[priorKey]);
801
832
  }
802
833
  priorKey = key;
803
834
  i++;
804
835
  });
805
836
  if (priorKey !== undefined) {
806
- execIteration(priorKey, i - 1, true);
837
+ execIteration(priorKey, i - 1, true, context[priorKey]);
807
838
  }
808
839
  })();
809
840
  }
@@ -2040,6 +2071,7 @@ return /******/ (function(modules) { // webpackBootstrap
2040
2071
  exports.__esModule = true;
2041
2072
  exports.createProtoAccessControl = createProtoAccessControl;
2042
2073
  exports.resultIsAllowed = resultIsAllowed;
2074
+ exports.isPrototypeConstructor = isPrototypeConstructor;
2043
2075
  exports.resetLoggedProperties = resetLoggedProperties;
2044
2076
 
2045
2077
  var _utils = __webpack_require__(5);
@@ -2086,6 +2118,25 @@ return /******/ (function(modules) { // webpackBootstrap
2086
2118
  }
2087
2119
  }
2088
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
+
2089
2140
  function checkWhiteList(protoAccessControlForType, propertyName) {
2090
2141
  if (protoAccessControlForType.whitelist[propertyName] !== undefined) {
2091
2142
  return protoAccessControlForType.whitelist[propertyName] === true;
@@ -2232,7 +2283,19 @@ return /******/ (function(modules) { // webpackBootstrap
2232
2283
 
2233
2284
  var result = env.VM.invokePartial.call(this, partial, context, options);
2234
2285
 
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
+ }
2292
+
2235
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
+ }
2236
2299
  options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
2237
2300
  result = options.partials[options.name](context, options);
2238
2301
  }
@@ -2269,7 +2332,12 @@ return /******/ (function(modules) { // webpackBootstrap
2269
2332
  if (result == null) {
2270
2333
  return result;
2271
2334
  }
2272
- 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)) {
2273
2341
  return result;
2274
2342
  }
2275
2343
 
@@ -2437,8 +2505,11 @@ return /******/ (function(modules) { // webpackBootstrap
2437
2505
  } else {
2438
2506
  partial = lookupOwnProperty(options.partials, options.name);
2439
2507
  }
2440
- } else if (!partial.call && !options.name) {
2441
- // 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.
2442
2513
  options.name = partial;
2443
2514
  partial = lookupOwnProperty(options.partials, partial);
2444
2515
  }
@@ -2652,8 +2723,6 @@ return /******/ (function(modules) { // webpackBootstrap
2652
2723
 
2653
2724
  'use strict';
2654
2725
 
2655
- var _Object$keys = __webpack_require__(60)['default'];
2656
-
2657
2726
  var _interopRequireDefault = __webpack_require__(1)['default'];
2658
2727
 
2659
2728
  var _interopRequireWildcard = __webpack_require__(3)['default'];
@@ -2670,14 +2739,10 @@ return /******/ (function(modules) { // webpackBootstrap
2670
2739
 
2671
2740
  var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl);
2672
2741
 
2673
- var _helpers = __webpack_require__(88);
2742
+ var _helpers = __webpack_require__(89);
2674
2743
 
2675
2744
  var Helpers = _interopRequireWildcard(_helpers);
2676
2745
 
2677
- var _exception = __webpack_require__(6);
2678
-
2679
- var _exception2 = _interopRequireDefault(_exception);
2680
-
2681
2746
  var _utils = __webpack_require__(5);
2682
2747
 
2683
2748
  exports.parser = _parser2['default'];
@@ -2688,9 +2753,6 @@ return /******/ (function(modules) { // webpackBootstrap
2688
2753
  function parseWithoutProcessing(input, options) {
2689
2754
  // Just return if an already-compiled AST was passed in.
2690
2755
  if (input.type === 'Program') {
2691
- // When a pre-parsed AST is passed in, validate all node values to prevent
2692
- // code injection via type-confused literals.
2693
- validateInputAst(input);
2694
2756
  return input;
2695
2757
  }
2696
2758
 
@@ -2713,58 +2775,6 @@ return /******/ (function(modules) { // webpackBootstrap
2713
2775
  return strip.accept(ast);
2714
2776
  }
2715
2777
 
2716
- function validateInputAst(ast) {
2717
- validateAstNode(ast);
2718
- }
2719
-
2720
- function validateAstNode(node) {
2721
- if (node == null) {
2722
- return;
2723
- }
2724
-
2725
- if (Array.isArray(node)) {
2726
- node.forEach(validateAstNode);
2727
- return;
2728
- }
2729
-
2730
- if (typeof node !== 'object') {
2731
- return;
2732
- }
2733
-
2734
- if (node.type === 'PathExpression') {
2735
- if (!isValidDepth(node.depth)) {
2736
- throw new _exception2['default']('Invalid AST: PathExpression.depth must be an integer');
2737
- }
2738
- if (!Array.isArray(node.parts)) {
2739
- throw new _exception2['default']('Invalid AST: PathExpression.parts must be an array');
2740
- }
2741
- for (var i = 0; i < node.parts.length; i++) {
2742
- if (typeof node.parts[i] !== 'string') {
2743
- throw new _exception2['default']('Invalid AST: PathExpression.parts must only contain strings');
2744
- }
2745
- }
2746
- } else if (node.type === 'NumberLiteral') {
2747
- if (typeof node.value !== 'number' || !isFinite(node.value)) {
2748
- throw new _exception2['default']('Invalid AST: NumberLiteral.value must be a number');
2749
- }
2750
- } else if (node.type === 'BooleanLiteral') {
2751
- if (typeof node.value !== 'boolean') {
2752
- throw new _exception2['default']('Invalid AST: BooleanLiteral.value must be a boolean');
2753
- }
2754
- }
2755
-
2756
- _Object$keys(node).forEach(function (propertyName) {
2757
- if (propertyName === 'loc') {
2758
- return;
2759
- }
2760
- validateAstNode(node[propertyName]);
2761
- });
2762
- }
2763
-
2764
- function isValidDepth(depth) {
2765
- return typeof depth === 'number' && isFinite(depth) && Math.floor(depth) === depth && depth >= 0;
2766
- }
2767
-
2768
2778
  /***/ }),
2769
2779
  /* 85 */
2770
2780
  /***/ (function(module, exports) {
@@ -3562,7 +3572,7 @@ return /******/ (function(modules) { // webpackBootstrap
3562
3572
  // If we are on a standalone node, save the indent info for partials
3563
3573
  if (current.type === 'PartialStatement') {
3564
3574
  // Pull out the whitespace from the final line
3565
- current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
3575
+ current.indent = /(?:^|[^ \t])([ \t]+)$/.exec(body[i - 1].original)[1];
3566
3576
  }
3567
3577
  }
3568
3578
  }
@@ -3670,7 +3680,9 @@ return /******/ (function(modules) { // webpackBootstrap
3670
3680
  }
3671
3681
 
3672
3682
  if (prev.type === 'ContentStatement') {
3673
- 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);
3674
3686
  }
3675
3687
  }
3676
3688
  function isNextWhitespace(body, i, isRoot) {
@@ -3722,7 +3734,10 @@ return /******/ (function(modules) { // webpackBootstrap
3722
3734
 
3723
3735
  // We omit the last node if it's whitespace only and not preceded by a non-content node.
3724
3736
  var original = current.value;
3725
- 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');
3726
3741
  current.leftStripped = current.value !== original;
3727
3742
  return current.leftStripped;
3728
3743
  }
@@ -3744,10 +3759,34 @@ return /******/ (function(modules) { // webpackBootstrap
3744
3759
 
3745
3760
  var _exception2 = _interopRequireDefault(_exception);
3746
3761
 
3762
+ var _utils = __webpack_require__(5);
3763
+
3764
+ var _nodeTypes = __webpack_require__(88);
3765
+
3747
3766
  function Visitor() {
3748
3767
  this.parents = [];
3749
3768
  }
3750
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
+
3751
3790
  Visitor.prototype = {
3752
3791
  constructor: Visitor,
3753
3792
  mutating: false,
@@ -3756,9 +3795,7 @@ return /******/ (function(modules) { // webpackBootstrap
3756
3795
  acceptKey: function acceptKey(node, name) {
3757
3796
  var value = this.accept(node[name]);
3758
3797
  if (this.mutating) {
3759
- // Hacky sanity check: This may have a few false positives for type for the helper
3760
- // methods but will generally do the right thing without a lot of overhead.
3761
- if (value && !Visitor.prototype[value.type]) {
3798
+ if (value && !isDispatchable(this, value.type)) {
3762
3799
  throw new _exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
3763
3800
  }
3764
3801
  node[name] = value;
@@ -3794,8 +3831,9 @@ return /******/ (function(modules) { // webpackBootstrap
3794
3831
  return;
3795
3832
  }
3796
3833
 
3797
- /* istanbul ignore next: Sanity code */
3798
- 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]) {
3799
3837
  throw new _exception2['default']('Unknown type: ' + object.type, object);
3800
3838
  }
3801
3839
 
@@ -3875,6 +3913,20 @@ return /******/ (function(modules) { // webpackBootstrap
3875
3913
 
3876
3914
  /***/ }),
3877
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
+
3928
+ /***/ }),
3929
+ /* 89 */
3878
3930
  /***/ (function(module, exports, __webpack_require__) {
3879
3931
 
3880
3932
  'use strict';
@@ -4105,7 +4157,7 @@ return /******/ (function(modules) { // webpackBootstrap
4105
4157
  }
4106
4158
 
4107
4159
  /***/ }),
4108
- /* 89 */
4160
+ /* 90 */
4109
4161
  /***/ (function(module, exports, __webpack_require__) {
4110
4162
 
4111
4163
  /* eslint-disable new-cap */
@@ -4131,6 +4183,8 @@ return /******/ (function(modules) { // webpackBootstrap
4131
4183
 
4132
4184
  var _ast2 = _interopRequireDefault(_ast);
4133
4185
 
4186
+ var _nodeTypes = __webpack_require__(88);
4187
+
4134
4188
  var slice = [].slice;
4135
4189
 
4136
4190
  function Compiler() {}
@@ -4210,8 +4264,8 @@ return /******/ (function(modules) { // webpackBootstrap
4210
4264
  },
4211
4265
 
4212
4266
  accept: function accept(node) {
4213
- /* istanbul ignore next: Sanity code */
4214
- 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]) {
4215
4269
  throw new _exception2['default']('Unknown type: ' + node.type, node);
4216
4270
  }
4217
4271
 
@@ -4222,6 +4276,7 @@ return /******/ (function(modules) { // webpackBootstrap
4222
4276
  },
4223
4277
 
4224
4278
  Program: function Program(program) {
4279
+ validateBlockParams(program);
4225
4280
  this.options.blockParams.unshift(program.blockParams);
4226
4281
 
4227
4282
  var body = program.body,
@@ -4337,6 +4392,9 @@ return /******/ (function(modules) { // webpackBootstrap
4337
4392
  },
4338
4393
 
4339
4394
  ContentStatement: function ContentStatement(content) {
4395
+ if (content.value != null && typeof content.value !== 'string') {
4396
+ throw invalidAst(content, 'value must be a string');
4397
+ }
4340
4398
  if (content.value) {
4341
4399
  this.opcode('appendContent', content.value);
4342
4400
  }
@@ -4361,7 +4419,7 @@ return /******/ (function(modules) { // webpackBootstrap
4361
4419
  name = path.parts[0],
4362
4420
  isBlock = program != null || inverse != null;
4363
4421
 
4364
- this.opcode('getContext', path.depth);
4422
+ this.opcode('getContext', validateDepth(path));
4365
4423
 
4366
4424
  this.opcode('pushProgram', program);
4367
4425
  this.opcode('pushProgram', inverse);
@@ -4398,35 +4456,43 @@ return /******/ (function(modules) { // webpackBootstrap
4398
4456
  },
4399
4457
 
4400
4458
  PathExpression: function PathExpression(path) {
4401
- this.addDepth(path.depth);
4402
- 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);
4403
4463
 
4404
- var name = path.parts[0],
4464
+ this.addDepth(depth);
4465
+ this.opcode('getContext', depth);
4466
+
4467
+ var name = parts[0],
4405
4468
  scoped = _ast2['default'].helpers.scopedId(path),
4406
- blockParamId = !path.depth && !scoped && this.blockParamIndex(name);
4469
+ blockParamId = !depth && !scoped && this.blockParamIndex(name);
4407
4470
 
4408
4471
  if (blockParamId) {
4409
- this.opcode('lookupBlockParam', blockParamId, path.parts);
4472
+ this.opcode('lookupBlockParam', blockParamId, parts);
4410
4473
  } else if (!name) {
4411
4474
  // Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
4412
4475
  this.opcode('pushContext');
4413
4476
  } else if (path.data) {
4414
4477
  this.options.data = true;
4415
- this.opcode('lookupData', path.depth, path.parts, path.strict);
4478
+ this.opcode('lookupData', depth, parts, path.strict);
4416
4479
  } else {
4417
- this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped);
4480
+ this.opcode('lookupOnContext', parts, path.falsy, path.strict, scoped);
4418
4481
  }
4419
4482
  },
4420
4483
 
4421
4484
  StringLiteral: function StringLiteral(string) {
4485
+ validateLiteral(string);
4422
4486
  this.opcode('pushString', string.value);
4423
4487
  },
4424
4488
 
4425
4489
  NumberLiteral: function NumberLiteral(number) {
4490
+ validateLiteral(number);
4426
4491
  this.opcode('pushLiteral', number.value);
4427
4492
  },
4428
4493
 
4429
4494
  BooleanLiteral: function BooleanLiteral(bool) {
4495
+ validateLiteral(bool);
4430
4496
  this.opcode('pushLiteral', bool.value);
4431
4497
  },
4432
4498
 
@@ -4441,7 +4507,17 @@ return /******/ (function(modules) { // webpackBootstrap
4441
4507
  Hash: function Hash(hash) {
4442
4508
  var pairs = hash.pairs,
4443
4509
  i = 0,
4444
- 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
+ }
4445
4521
 
4446
4522
  this.opcode('pushHash');
4447
4523
 
@@ -4472,6 +4548,9 @@ return /******/ (function(modules) { // webpackBootstrap
4472
4548
  },
4473
4549
 
4474
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);
4475
4554
  var isSimple = _ast2['default'].helpers.simpleId(sexpr.path);
4476
4555
 
4477
4556
  var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);
@@ -4513,17 +4592,26 @@ return /******/ (function(modules) { // webpackBootstrap
4513
4592
  },
4514
4593
 
4515
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);
4516
4598
  var value = val.value != null ? val.value : val.original || '';
4599
+ var depth = validateDepth(val);
4517
4600
 
4518
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
+ }
4519
4608
  if (value.replace) {
4520
4609
  value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.');
4521
4610
  }
4522
-
4523
- if (val.depth) {
4524
- this.addDepth(val.depth);
4611
+ if (depth) {
4612
+ this.addDepth(depth);
4525
4613
  }
4526
- this.opcode('getContext', val.depth || 0);
4614
+ this.opcode('getContext', depth);
4527
4615
  this.opcode('pushStringParam', value, val.type);
4528
4616
 
4529
4617
  if (val.type === 'SubExpression') {
@@ -4534,8 +4622,10 @@ return /******/ (function(modules) { // webpackBootstrap
4534
4622
  } else {
4535
4623
  if (this.trackIds) {
4536
4624
  var blockParamIndex = undefined;
4537
- if (val.parts && !_ast2['default'].helpers.scopedId(val) && !val.depth) {
4538
- 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]);
4539
4629
  }
4540
4630
  if (blockParamIndex) {
4541
4631
  var blockParamChild = val.parts.slice(1).join('.');
@@ -4644,6 +4734,111 @@ return /******/ (function(modules) { // webpackBootstrap
4644
4734
  return ret;
4645
4735
  }
4646
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
+
4647
4842
  function argEquals(a, b) {
4648
4843
  if (a === b) {
4649
4844
  return true;
@@ -4676,7 +4871,7 @@ return /******/ (function(modules) { // webpackBootstrap
4676
4871
  }
4677
4872
 
4678
4873
  /***/ }),
4679
- /* 90 */
4874
+ /* 91 */
4680
4875
  /***/ (function(module, exports, __webpack_require__) {
4681
4876
 
4682
4877
  'use strict';
@@ -4695,7 +4890,7 @@ return /******/ (function(modules) { // webpackBootstrap
4695
4890
 
4696
4891
  var _utils = __webpack_require__(5);
4697
4892
 
4698
- var _codeGen = __webpack_require__(91);
4893
+ var _codeGen = __webpack_require__(92);
4699
4894
 
4700
4895
  var _codeGen2 = _interopRequireDefault(_codeGen);
4701
4896
 
@@ -4712,7 +4907,7 @@ return /******/ (function(modules) { // webpackBootstrap
4712
4907
  return this.internalNameLookup(parent, name);
4713
4908
  },
4714
4909
  depthedLookup: function depthedLookup(name) {
4715
- return [this.aliasable('container.lookup'), '(depths, ', JSON.stringify(name), ')'];
4910
+ return [this.aliasable('container.lookup'), '(depths, ', this.quotedString(name), ')'];
4716
4911
  },
4717
4912
 
4718
4913
  compilerInfo: function compilerInfo() {
@@ -4747,7 +4942,7 @@ return /******/ (function(modules) { // webpackBootstrap
4747
4942
  // END PUBLIC API
4748
4943
  internalNameLookup: function internalNameLookup(parent, name) {
4749
4944
  this.lookupPropertyFunctionIsUsed = true;
4750
- return ['lookupProperty(', parent, ',', JSON.stringify(name), ')'];
4945
+ return ['lookupProperty(', parent, ',', this.quotedString(name), ')'];
4751
4946
  },
4752
4947
 
4753
4948
  lookupPropertyFunctionIsUsed: false,
@@ -5422,7 +5617,7 @@ return /******/ (function(modules) { // webpackBootstrap
5422
5617
  }
5423
5618
 
5424
5619
  if (indent) {
5425
- options.indent = JSON.stringify(indent);
5620
+ options.indent = this.quotedString(indent);
5426
5621
  }
5427
5622
  options.helpers = 'helpers';
5428
5623
  options.partials = 'partials';
@@ -5478,7 +5673,7 @@ return /******/ (function(modules) { // webpackBootstrap
5478
5673
 
5479
5674
  pushId: function pushId(type, name, child) {
5480
5675
  if (type === 'BlockParam') {
5481
- 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) : ''));
5482
5677
  } else if (type === 'PathExpression') {
5483
5678
  this.pushString(name);
5484
5679
  } else if (type === 'SubExpression') {
@@ -5793,7 +5988,7 @@ return /******/ (function(modules) { // webpackBootstrap
5793
5988
 
5794
5989
  setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) {
5795
5990
  var options = this.setupParams(helper, paramSize, params);
5796
- options.loc = JSON.stringify(this.source.currentLocation);
5991
+ options.loc = locationLiteral(this);
5797
5992
  options = this.objectLiteral(options);
5798
5993
  if (useRegister) {
5799
5994
  this.useRegister('options');
@@ -5825,6 +6020,22 @@ return /******/ (function(modules) { // webpackBootstrap
5825
6020
  return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name);
5826
6021
  };
5827
6022
 
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
+
5828
6039
  function strictLookup(requireTerminal, compiler, parts, startPartIndex, type) {
5829
6040
  var stack = compiler.popStack(),
5830
6041
  len = parts.length;
@@ -5837,7 +6048,7 @@ return /******/ (function(modules) { // webpackBootstrap
5837
6048
  }
5838
6049
 
5839
6050
  if (requireTerminal) {
5840
- return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[len]), ', ', JSON.stringify(compiler.source.currentLocation), ' )'];
6051
+ return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[len]), ', ', locationLiteral(compiler), ' )'];
5841
6052
  } else {
5842
6053
  return stack;
5843
6054
  }
@@ -5847,7 +6058,7 @@ return /******/ (function(modules) { // webpackBootstrap
5847
6058
  module.exports = exports['default'];
5848
6059
 
5849
6060
  /***/ }),
5850
- /* 91 */
6061
+ /* 92 */
5851
6062
  /***/ (function(module, exports, __webpack_require__) {
5852
6063
 
5853
6064
  /* global define, require */
@@ -5856,9 +6067,62 @@ return /******/ (function(modules) { // webpackBootstrap
5856
6067
  var _Object$keys = __webpack_require__(60)['default'];
5857
6068
 
5858
6069
  exports.__esModule = true;
6070
+ exports.safeJsonLiteral = safeJsonLiteral;
5859
6071
 
5860
6072
  var _utils = __webpack_require__(5);
5861
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
+
5862
6126
  var SourceNode = undefined;
5863
6127
 
5864
6128
  try {
@@ -5970,8 +6234,16 @@ return /******/ (function(modules) { // webpackBootstrap
5970
6234
  },
5971
6235
 
5972
6236
  quotedString: function quotedString(str) {
5973
- 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
5974
- .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);
5975
6247
  },
5976
6248
 
5977
6249
  objectLiteral: function objectLiteral(obj) {
@@ -6018,7 +6290,6 @@ return /******/ (function(modules) { // webpackBootstrap
6018
6290
  };
6019
6291
 
6020
6292
  exports['default'] = CodeGen;
6021
- module.exports = exports['default'];
6022
6293
 
6023
6294
  /***/ })
6024
6295
  /******/ ])
@@ -1,7 +1,7 @@
1
1
  /**!
2
2
 
3
3
  @license
4
- handlebars v4.7.9
4
+ handlebars v4.7.10
5
5
 
6
6
  Copyright (C) 2011-2019 by Yehuda Katz
7
7
 
@@ -209,7 +209,7 @@ return /******/ (function(modules) { // webpackBootstrap
209
209
 
210
210
  var _internalProtoAccess = __webpack_require__(72);
211
211
 
212
- var VERSION = '4.7.9';
212
+ var VERSION = '4.7.10';
213
213
  exports.VERSION = VERSION;
214
214
  var COMPILER_REVISION = 8;
215
215
  exports.COMPILER_REVISION = COMPILER_REVISION;
@@ -383,8 +383,9 @@ return /******/ (function(modules) { // webpackBootstrap
383
383
 
384
384
  function escapeExpression(string) {
385
385
  if (typeof string !== 'string') {
386
- // don't escape SafeStrings, since they're already safe
387
- if (string && string.toHTML) {
386
+ // don't escape SafeStrings, since they're already safe. Other values with a
387
+ // "toHTML" key, e.g. from JSON context data, are escaped like any object.
388
+ if (string && typeof string.toHTML === 'function') {
388
389
  return string.toHTML();
389
390
  } else if (string == null) {
390
391
  return '';
@@ -447,11 +448,15 @@ return /******/ (function(modules) { // webpackBootstrap
447
448
  column = undefined,
448
449
  endColumn = undefined;
449
450
 
450
- if (loc) {
451
+ // Hand-built ASTs may carry a partial loc; never let the location
452
+ // bookkeeping mask the actual error message.
453
+ if (loc && loc.start) {
451
454
  line = loc.start.line;
452
- endLineNumber = loc.end.line;
453
455
  column = loc.start.column;
454
- endColumn = loc.end.column;
456
+ if (loc.end) {
457
+ endLineNumber = loc.end.line;
458
+ endColumn = loc.end.column;
459
+ }
455
460
 
456
461
  message += ' - ' + line + ':' + column;
457
462
  }
@@ -659,6 +664,26 @@ return /******/ (function(modules) { // webpackBootstrap
659
664
 
660
665
  var _exception2 = _interopRequireDefault(_exception);
661
666
 
667
+ /**
668
+ * Close an unfinished iterator after the block body threw, like for...of does,
669
+ * so that generators can run their cleanup code.
670
+ *
671
+ * Errors thrown while closing, including by a `return` getter, are ignored:
672
+ * the block's error is the cause and is the one the caller must see.
673
+ *
674
+ * @param {Iterator} iterator the iterator that was being consumed
675
+ */
676
+ function closeIterator(iterator) {
677
+ try {
678
+ var _close = iterator['return'];
679
+ if (_utils.isFunction(_close)) {
680
+ _close.call(iterator);
681
+ }
682
+ } catch (closeError) {
683
+ // NOP
684
+ }
685
+ }
686
+
662
687
  exports['default'] = function (instance) {
663
688
  instance.registerHelper('each', function (context, options) {
664
689
  if (!options) {
@@ -684,7 +709,7 @@ return /******/ (function(modules) { // webpackBootstrap
684
709
  data = _utils.createFrame(options.data);
685
710
  }
686
711
 
687
- function execIteration(field, index, last) {
712
+ function execIteration(field, index, last, value) {
688
713
  if (data) {
689
714
  data.key = field;
690
715
  data.index = index;
@@ -696,9 +721,9 @@ return /******/ (function(modules) { // webpackBootstrap
696
721
  }
697
722
  }
698
723
 
699
- ret = ret + fn(context[field], {
724
+ ret = ret + fn(value, {
700
725
  data: data,
701
- blockParams: _utils.blockParams([context[field], field], [contextPath + field, null])
726
+ blockParams: _utils.blockParams([value, field], [contextPath + field, null])
702
727
  });
703
728
  }
704
729
 
@@ -706,18 +731,24 @@ return /******/ (function(modules) { // webpackBootstrap
706
731
  if (_utils.isArray(context)) {
707
732
  for (var j = context.length; i < j; i++) {
708
733
  if (i in context) {
709
- execIteration(i, i, i === context.length - 1);
734
+ execIteration(i, i, i === context.length - 1, context[i]);
710
735
  }
711
736
  }
712
737
  } else if (typeof _Symbol === 'function' && context[_Symbol$iterator]) {
713
- var newContext = [];
714
738
  var iterator = _getIterator(context);
715
- for (var it = iterator.next(); !it.done; it = iterator.next()) {
716
- newContext.push(it.value);
717
- }
718
- context = newContext;
719
- for (var j = context.length; i < j; i++) {
720
- execIteration(i, i, i === context.length - 1);
739
+ var current = iterator.next();
740
+ while (!current.done) {
741
+ var next = iterator.next();
742
+ try {
743
+ execIteration(i, i, next.done, current.value);
744
+ } catch (e) {
745
+ if (!next.done) {
746
+ closeIterator(iterator);
747
+ }
748
+ throw e;
749
+ }
750
+ current = next;
751
+ i++;
721
752
  }
722
753
  } else {
723
754
  (function () {
@@ -728,13 +759,13 @@ return /******/ (function(modules) { // webpackBootstrap
728
759
  // the last iteration without have to scan the object twice and create
729
760
  // an itermediate keys array.
730
761
  if (priorKey !== undefined) {
731
- execIteration(priorKey, i - 1);
762
+ execIteration(priorKey, i - 1, false, context[priorKey]);
732
763
  }
733
764
  priorKey = key;
734
765
  i++;
735
766
  });
736
767
  if (priorKey !== undefined) {
737
- execIteration(priorKey, i - 1, true);
768
+ execIteration(priorKey, i - 1, true, context[priorKey]);
738
769
  }
739
770
  })();
740
771
  }
@@ -1971,6 +2002,7 @@ return /******/ (function(modules) { // webpackBootstrap
1971
2002
  exports.__esModule = true;
1972
2003
  exports.createProtoAccessControl = createProtoAccessControl;
1973
2004
  exports.resultIsAllowed = resultIsAllowed;
2005
+ exports.isPrototypeConstructor = isPrototypeConstructor;
1974
2006
  exports.resetLoggedProperties = resetLoggedProperties;
1975
2007
 
1976
2008
  var _utils = __webpack_require__(4);
@@ -2017,6 +2049,25 @@ return /******/ (function(modules) { // webpackBootstrap
2017
2049
  }
2018
2050
  }
2019
2051
 
2052
+ /**
2053
+ * Detect a prototype object's own "constructor" back-reference, e.g.
2054
+ * `Function.prototype.constructor === Function`. Because "constructor" is an
2055
+ * "own" property of such objects, it would otherwise bypass the prototype-access
2056
+ * checks and expose dangerous constructors (allowing arbitrary code execution).
2057
+ *
2058
+ * Only functions can be constructors, so other values are never treated as one
2059
+ * and their `prototype` is never read.
2060
+ *
2061
+ * @param {*} parent the object the property is being read from
2062
+ * @param {string} propertyName the property being looked up
2063
+ * @param {*} result the already-resolved value of `parent[propertyName]`
2064
+ * @returns {boolean} true if the lookup resolves `parent`'s own constructor
2065
+ */
2066
+
2067
+ function isPrototypeConstructor(parent, propertyName, result) {
2068
+ return propertyName === 'constructor' && typeof result === 'function' && parent === result.prototype;
2069
+ }
2070
+
2020
2071
  function checkWhiteList(protoAccessControlForType, propertyName) {
2021
2072
  if (protoAccessControlForType.whitelist[propertyName] !== undefined) {
2022
2073
  return protoAccessControlForType.whitelist[propertyName] === true;
@@ -2163,7 +2214,19 @@ return /******/ (function(modules) { // webpackBootstrap
2163
2214
 
2164
2215
  var result = env.VM.invokePartial.call(this, partial, context, options);
2165
2216
 
2217
+ // An empty result normally means the partial is template source that still
2218
+ // has to be compiled. A function partial has already been invoked, so an
2219
+ // empty result from it is a bug in that partial.
2220
+ if (result == null && Utils.isFunction(partial)) {
2221
+ throw new _exception2['default']('The partial ' + options.name + ' returned no output: partials must return a string');
2222
+ }
2223
+
2166
2224
  if (result == null && env.compile) {
2225
+ // Only template source registered as a partial may be compiled here.
2226
+ // Anything else (e.g. an AST-shaped object) must not become code.
2227
+ if (typeof partial !== 'string') {
2228
+ throw new _exception2['default']('The partial ' + options.name + ' could not be compiled: partials must be strings or functions returning strings');
2229
+ }
2167
2230
  options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
2168
2231
  result = options.partials[options.name](context, options);
2169
2232
  }
@@ -2200,7 +2263,12 @@ return /******/ (function(modules) { // webpackBootstrap
2200
2263
  if (result == null) {
2201
2264
  return result;
2202
2265
  }
2203
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
2266
+ // Own properties are trusted context data and returned as-is, except for
2267
+ // a prototype's own "constructor" back-reference (e.g.
2268
+ // Function.prototype.constructor === Function): that must still pass the
2269
+ // prototype-access deny list, otherwise the Function constructor becomes
2270
+ // reachable and arbitrary code can be executed.
2271
+ if (Object.prototype.hasOwnProperty.call(parent, propertyName) && !_internalProtoAccess.isPrototypeConstructor(parent, propertyName, result)) {
2204
2272
  return result;
2205
2273
  }
2206
2274
 
@@ -2368,8 +2436,11 @@ return /******/ (function(modules) { // webpackBootstrap
2368
2436
  } else {
2369
2437
  partial = lookupOwnProperty(options.partials, options.name);
2370
2438
  }
2371
- } else if (!partial.call && !options.name) {
2372
- // This is a dynamic partial that returned a string
2439
+ } else if (typeof partial !== 'function' && !options.name) {
2440
+ // This is a dynamic partial that returned a name rather than a compiled
2441
+ // partial. Only functions are accepted as partials here; any other value
2442
+ // (including an object with a "call" property) is treated as a name, so
2443
+ // that context data can never be compiled as a template.
2373
2444
  options.name = partial;
2374
2445
  partial = lookupOwnProperty(options.partials, partial);
2375
2446
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: handlebars-source
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.7.9
4
+ version: 4.7.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yehuda Katz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-26 00:00:00.000000000 Z
11
+ date: 2026-10-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Handlebars.js source code wrapper for (pre)compilation gems.
14
14
  email: