Disallow calling "helperMissing" and "blockHelperMissing" directly
closes #1558
This commit is contained in:
committed by
Nils Knappmeier
parent
fff3e40402
commit
2078c727c6
@@ -311,7 +311,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// replace it on the stack with the result of properly
|
// replace it on the stack with the result of properly
|
||||||
// invoking blockHelperMissing.
|
// invoking blockHelperMissing.
|
||||||
blockValue: function(name) {
|
blockValue: function(name) {
|
||||||
let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
|
let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'),
|
||||||
params = [this.contextName(0)];
|
params = [this.contextName(0)];
|
||||||
this.setupHelperArgs(name, 0, params);
|
this.setupHelperArgs(name, 0, params);
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
// On stack, after, if lastHelper: value
|
// On stack, after, if lastHelper: value
|
||||||
ambiguousBlockValue: function() {
|
ambiguousBlockValue: function() {
|
||||||
// We're being a bit cheeky and reusing the options value from the prior exec
|
// We're being a bit cheeky and reusing the options value from the prior exec
|
||||||
let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
|
let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'),
|
||||||
params = [this.contextName(0)];
|
params = [this.contextName(0)];
|
||||||
this.setupHelperArgs('', 0, params, true);
|
this.setupHelperArgs('', 0, params, true);
|
||||||
|
|
||||||
@@ -622,18 +622,32 @@ JavaScriptCompiler.prototype = {
|
|||||||
// If the helper is not found, `helperMissing` is called.
|
// If the helper is not found, `helperMissing` is called.
|
||||||
invokeHelper: function(paramSize, name, isSimple) {
|
invokeHelper: function(paramSize, name, isSimple) {
|
||||||
let nonHelper = this.popStack(),
|
let nonHelper = this.popStack(),
|
||||||
helper = this.setupHelper(paramSize, name),
|
helper = this.setupHelper(paramSize, name);
|
||||||
simple = isSimple ? [helper.name, ' || '] : '';
|
|
||||||
|
|
||||||
let lookup = ['('].concat(simple, nonHelper);
|
let possibleFunctionCalls = [];
|
||||||
if (!this.options.strict) {
|
|
||||||
lookup.push(' || ', this.aliasable('helpers.helperMissing'));
|
if (isSimple) { // direct call to helper
|
||||||
|
possibleFunctionCalls.push(helper.name);
|
||||||
|
}
|
||||||
|
// call a function from the input object
|
||||||
|
possibleFunctionCalls.push(nonHelper);
|
||||||
|
if (!this.options.strict) {
|
||||||
|
possibleFunctionCalls.push(this.aliasable('container.hooks.helperMissing'));
|
||||||
}
|
}
|
||||||
lookup.push(')');
|
|
||||||
|
|
||||||
this.push(this.source.functionCall(lookup, 'call', helper.callParams));
|
let functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')'];
|
||||||
|
let functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams);
|
||||||
|
this.push(functionCall);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
itemsSeparatedBy: function(items, separator) {
|
||||||
|
let result = [];
|
||||||
|
result.push(items[0]);
|
||||||
|
for (let i = 1; i < items.length; i++) {
|
||||||
|
result.push(separator, items[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
// [invokeKnownHelper]
|
// [invokeKnownHelper]
|
||||||
//
|
//
|
||||||
// On stack, before: hash, inverse, program, params..., ...
|
// On stack, before: hash, inverse, program, params..., ...
|
||||||
@@ -673,7 +687,7 @@ JavaScriptCompiler.prototype = {
|
|||||||
lookup[0] = '(helper = ';
|
lookup[0] = '(helper = ';
|
||||||
lookup.push(
|
lookup.push(
|
||||||
' != null ? helper : ',
|
' != null ? helper : ',
|
||||||
this.aliasable('helpers.helperMissing')
|
this.aliasable('container.hooks.helperMissing')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,12 @@ export function registerDefaultHelpers(instance) {
|
|||||||
registerLookup(instance);
|
registerLookup(instance);
|
||||||
registerWith(instance);
|
registerWith(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function moveHelperToHooks(instance, helperName, keepHelper) {
|
||||||
|
if (instance.helpers[helperName]) {
|
||||||
|
instance.hooks[helperName] = instance.helpers[helperName];
|
||||||
|
if (!keepHelper) {
|
||||||
|
delete instance.helpers[helperName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+19
-16
@@ -1,6 +1,7 @@
|
|||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
import Exception from './exception';
|
import Exception from './exception';
|
||||||
import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from './base';
|
import {COMPILER_REVISION, createFrame, REVISION_CHANGES} from './base';
|
||||||
|
import {moveHelperToHooks} from './helpers';
|
||||||
|
|
||||||
export function checkRevision(compilerInfo) {
|
export function checkRevision(compilerInfo) {
|
||||||
const compilerRevision = compilerInfo && compilerInfo[0] || 1,
|
const compilerRevision = compilerInfo && compilerInfo[0] || 1,
|
||||||
@@ -21,6 +22,7 @@ export function checkRevision(compilerInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function template(templateSpec, env) {
|
export function template(templateSpec, env) {
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (!env) {
|
if (!env) {
|
||||||
throw new Exception('No environment passed to template');
|
throw new Exception('No environment passed to template');
|
||||||
@@ -42,13 +44,15 @@ export function template(templateSpec, env) {
|
|||||||
options.ids[0] = true;
|
options.ids[0] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
||||||
let result = env.VM.invokePartial.call(this, partial, context, options);
|
|
||||||
|
let optionsWithHooks = Utils.extend({}, options, {hooks: this.hooks});
|
||||||
|
|
||||||
|
let result = env.VM.invokePartial.call(this, partial, context, optionsWithHooks);
|
||||||
|
|
||||||
if (result == null && env.compile) {
|
if (result == null && env.compile) {
|
||||||
options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
|
options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
|
||||||
result = options.partials[options.name](context, options);
|
result = options.partials[options.name](context, optionsWithHooks);
|
||||||
}
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (options.indent) {
|
if (options.indent) {
|
||||||
@@ -115,15 +119,6 @@ export function template(templateSpec, env) {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
merge: function(param, common) {
|
|
||||||
let obj = param || common;
|
|
||||||
|
|
||||||
if (param && common && (param !== common)) {
|
|
||||||
obj = Utils.extend({}, common, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
// An empty object to use as replacement for null-contexts
|
// An empty object to use as replacement for null-contexts
|
||||||
nullContext: Object.seal({}),
|
nullContext: Object.seal({}),
|
||||||
|
|
||||||
@@ -158,19 +153,27 @@ export function template(templateSpec, env) {
|
|||||||
|
|
||||||
ret._setup = function(options) {
|
ret._setup = function(options) {
|
||||||
if (!options.partial) {
|
if (!options.partial) {
|
||||||
container.helpers = container.merge(options.helpers, env.helpers);
|
container.helpers = Utils.extend({}, env.helpers, options.helpers);
|
||||||
|
|
||||||
if (templateSpec.usePartial) {
|
if (templateSpec.usePartial) {
|
||||||
container.partials = container.merge(options.partials, env.partials);
|
container.partials = Utils.extend({}, env.partials, options.partials);
|
||||||
}
|
}
|
||||||
if (templateSpec.usePartial || templateSpec.useDecorators) {
|
if (templateSpec.usePartial || templateSpec.useDecorators) {
|
||||||
container.decorators = container.merge(options.decorators, env.decorators);
|
container.decorators = Utils.extend({}, env.decorators, options.decorators);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container.hooks = {};
|
||||||
|
let keepHelper = options.allowCallsToHelperMissing;
|
||||||
|
moveHelperToHooks(container, 'helperMissing', keepHelper);
|
||||||
|
moveHelperToHooks(container, 'blockHelperMissing', keepHelper);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
container.helpers = options.helpers;
|
container.helpers = options.helpers;
|
||||||
container.partials = options.partials;
|
container.partials = options.partials;
|
||||||
container.decorators = options.decorators;
|
container.decorators = options.decorators;
|
||||||
|
container.hooks = options.hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ret._child = function(i, data, blockParams, depths) {
|
ret._child = function(i, data, blockParams, depths) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
const escape = {
|
const escape = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
|
|||||||
@@ -32,4 +32,60 @@ describe('security issues', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('GH-xxxx: Prevent explicit call of helperMissing-helpers', function() {
|
||||||
|
if (!Handlebars.compile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('without the option "allowExplicitCallOfHelperMissing"', function() {
|
||||||
|
it('should throw an exception when calling "{{helperMissing}}" ', function() {
|
||||||
|
shouldThrow(function() {
|
||||||
|
var template = Handlebars.compile('{{helperMissing}}');
|
||||||
|
template({});
|
||||||
|
}, Error);
|
||||||
|
});
|
||||||
|
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||||
|
shouldThrow(function() {
|
||||||
|
var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}');
|
||||||
|
template({});
|
||||||
|
}, Error);
|
||||||
|
});
|
||||||
|
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||||
|
var functionCalls = [];
|
||||||
|
shouldThrow(function() {
|
||||||
|
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||||
|
template({ fn: function() { functionCalls.push('called'); }});
|
||||||
|
}, Error);
|
||||||
|
equals(functionCalls.length, 0);
|
||||||
|
});
|
||||||
|
it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||||
|
shouldThrow(function() {
|
||||||
|
var template = Handlebars.compile('{{#blockHelperMissing .}}{{/blockHelperMissing}}');
|
||||||
|
template({ fn: function() { return 'functionInData';}});
|
||||||
|
}, Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with the option "allowCallsToHelperMissing" set to true', function() {
|
||||||
|
it('should not throw an exception when calling "{{helperMissing}}" ', function() {
|
||||||
|
var template = Handlebars.compile('{{helperMissing}}');
|
||||||
|
template({}, {allowCallsToHelperMissing: true});
|
||||||
|
});
|
||||||
|
it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||||
|
var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}');
|
||||||
|
template({}, {allowCallsToHelperMissing: true});
|
||||||
|
});
|
||||||
|
it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||||
|
var functionCalls = [];
|
||||||
|
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||||
|
template({ fn: function() { functionCalls.push('called'); }}, {allowCallsToHelperMissing: true});
|
||||||
|
equals(functionCalls.length, 1);
|
||||||
|
});
|
||||||
|
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||||
|
var template = Handlebars.compile('{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}');
|
||||||
|
template({}, {allowCallsToHelperMissing: true});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+1
@@ -29,6 +29,7 @@ declare namespace Handlebars {
|
|||||||
decorators?: { [name: string]: Function };
|
decorators?: { [name: string]: Function };
|
||||||
data?: any;
|
data?: any;
|
||||||
blockParams?: any[];
|
blockParams?: any[];
|
||||||
|
allowCallsToHelperMissing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HelperOptions {
|
export interface HelperOptions {
|
||||||
|
|||||||
Reference in New Issue
Block a user