Skip to content
Permalink
Browse files

Merge branch '4.x'

# Conflicts:
#	components/bower.json
#	components/handlebars.js.nuspec
#	components/package.json
#	package.json
  • Loading branch information
nknapp committed Jan 13, 2020
2 parents 0c8230c + 586e672 commit edfe6b899c750f2c06a7607a0afc9c35fcd07bff
@@ -215,7 +215,7 @@ module.exports = function(grunt) {

grunt.registerTask('bench', ['metrics']);

if (process.env.SAUCE_USERNAME) {
if (process.env.SAUCE_ACCESS_KEY) {
grunt.registerTask('sauce', ['concat:tests', 'connect', 'saucelabs-mocha']);
} else {
grunt.registerTask('sauce', []);
@@ -3,8 +3,9 @@ import Exception from './exception';
import { registerDefaultHelpers } from './helpers';
import { registerDefaultDecorators } from './decorators';
import logger from './logger';
import { resetLoggedProperties } from './internal/proto-access';

export const VERSION = '4.6.0';
export const VERSION = '4.7.2';
export const COMPILER_REVISION = 8;
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;

@@ -78,6 +79,13 @@ HandlebarsEnvironment.prototype = {
},
unregisterDecorator: function(name) {
delete this.decorators[name];
},
/**
* Reset the memory of illegal property accesses that have already been logged.
* @deprecated should only be used in handlebars test-cases
*/
resetLoggedPropertyAccesses() {
resetLoggedProperties();
}
};

@@ -0,0 +1,70 @@
import { createNewLookupObject } from './create-new-lookup-object';
import * as logger from '../logger';

const loggedProperties = Object.create(null);

export function createProtoAccessControl(runtimeOptions) {
let defaultMethodWhiteList = Object.create(null);
defaultMethodWhiteList['constructor'] = false;
defaultMethodWhiteList['__defineGetter__'] = false;
defaultMethodWhiteList['__defineSetter__'] = false;
defaultMethodWhiteList['__lookupGetter__'] = false;

let defaultPropertyWhiteList = Object.create(null);
// eslint-disable-next-line no-proto
defaultPropertyWhiteList['__proto__'] = false;

return {
properties: {
whitelist: createNewLookupObject(
defaultPropertyWhiteList,
runtimeOptions.allowedProtoProperties
),
defaultValue: runtimeOptions.allowProtoPropertiesByDefault
},
methods: {
whitelist: createNewLookupObject(
defaultMethodWhiteList,
runtimeOptions.allowedProtoMethods
),
defaultValue: runtimeOptions.allowProtoMethodsByDefault
}
};
}

export function resultIsAllowed(result, protoAccessControl, propertyName) {
if (typeof result === 'function') {
return checkWhiteList(protoAccessControl.methods, propertyName);
} else {
return checkWhiteList(protoAccessControl.properties, propertyName);
}
}

function checkWhiteList(protoAccessControlForType, propertyName) {
if (protoAccessControlForType.whitelist[propertyName] !== undefined) {
return protoAccessControlForType.whitelist[propertyName] === true;
}
if (protoAccessControlForType.defaultValue !== undefined) {
return protoAccessControlForType.defaultValue;
}
logUnexpecedPropertyAccessOnce(propertyName);
return false;
}

function logUnexpecedPropertyAccessOnce(propertyName) {
if (loggedProperties[propertyName] !== true) {
loggedProperties[propertyName] = true;
logger.log(
'error',
`Handlebars: Access has been denied to resolve the property "${propertyName}" because it is not an "own property" of its parent.\n` +
`You can add a runtime option to disable the check or this warning:\n` +
`See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details`
);
}
}

export function resetLoggedProperties() {
Object.keys(loggedProperties).forEach(propertyName => {
delete loggedProperties[propertyName];
});
}
@@ -1,4 +1,9 @@
export function wrapHelper(helper, transformOptionsFn) {
if (typeof helper !== 'function') {
// This should not happen, but apparently it does in https://github.com/wycats/handlebars.js/issues/1639
// We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function.
return helper;
}
let wrapper = function(/* dynamic arguments */) {
const options = arguments[arguments.length - 1];
arguments[arguments.length - 1] = transformOptionsFn(options);
@@ -8,7 +8,10 @@ import {
} from './base';
import { moveHelperToHooks } from './helpers';
import { wrapHelper } from './internal/wrapHelper';
import { createNewLookupObject } from './internal/createNewLookupObject';
import {
createProtoAccessControl,
resultIsAllowed
} from './internal/proto-access';

export function checkRevision(compilerInfo) {
const compilerRevision = (compilerInfo && compilerInfo[0]) || 1,
@@ -70,8 +73,7 @@ export function template(templateSpec, env) {

let extendedOptions = Utils.extend({}, options, {
hooks: this.hooks,
allowedProtoMethods: this.allowedProtoMethods,
allowedProtoProperties: this.allowedProtoProperties
protoAccessControl: this.protoAccessControl
});

let result = env.VM.invokePartial.call(
@@ -123,15 +125,14 @@ export function template(templateSpec, env) {
},
lookupProperty: function(parent, propertyName) {
let result = parent[propertyName];
if (result == null) {
return result;
}
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
return result;
}
const whitelist =
typeof result === 'function'
? container.allowedProtoMethods
: container.allowedProtoProperties;

if (whitelist[propertyName] === true) {
if (resultIsAllowed(result, container.protoAccessControl, propertyName)) {
return result;
}
return undefined;
@@ -234,6 +235,7 @@ export function template(templateSpec, env) {
)
);
}

main = executeDecorators(
templateSpec.main,
main,
@@ -244,6 +246,7 @@ export function template(templateSpec, env) {
);
return main(context, options);
}

ret.isTop = true;

function _setup(options) {
@@ -268,21 +271,15 @@ export function template(templateSpec, env) {
}

container.hooks = {};
container.allowedProtoProperties = createNewLookupObject(
options.allowedProtoProperties
);
container.allowedProtoMethods = createNewLookupObject(
options.allowedProtoMethods
);
container.protoAccessControl = createProtoAccessControl(options);

let keepHelperInHelpers =
options.allowCallsToHelperMissing ||
templateWasPrecompiledWithCompilerV7;
moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
} else {
container.allowedProtoProperties = options.allowedProtoProperties;
container.allowedProtoMethods = options.allowedProtoMethods;
container.protoAccessControl = options.protoAccessControl; // internal option
container.helpers = options.helpers;
container.partials = options.partials;
container.decorators = options.decorators;
@@ -2,7 +2,51 @@

## Development

[Commits](https://github.com/wycats/handlebars.js/compare/v4.6.0...master)
[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.2...master)

## v4.7.2 - January 13th, 2020

Bugfixes:

- fix: don't wrap helpers that are not functions - 9d5aa36, #1639

Chore/Build:

- chore: execute saucelabs-task only if access-key exists - a4fd391

Compatibility notes:

- No breaking changes are to be expected

[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.1...v4.7.2)

## v4.7.1 - January 12th, 2020

Bugfixes:

- fix: fix log output in case of illegal property access - f152dfc
- fix: log error for illegal property access only once per property - 3c1e252

Compatibility notes:

- no incompatibilities are to be expected.

[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.0...v4.7.1)

## v4.7.0 - January 10th, 2020

Features:

- feat: default options for controlling proto access - 7af1c12, #1635
- This makes it possible to disable the prototype access restrictions added in 4.6.0
- an error is logged in the console, if access to prototype properties is attempted and denied
and no explicit configuration has taken place.

Compatibility notes:

- no compatibilities are expected

[Commits](https://github.com/wycats/handlebars.js/compare/v4.6.0...v4.7.0)

## v4.6.0 - January 8th, 2020

@@ -529,4 +529,13 @@ describe('Regressions', function() {
sinon.restore();
});
});

describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", function() {
it('should treat undefined helpers like non-existing helpers', function() {
expectTemplate('{{foo}}')
.withHelper('foo', undefined)
.withInput({ foo: 'bar' })
.toCompileTo('bar');
});
});
});

0 comments on commit edfe6b8

Please sign in to comment.
You can’t perform that action at this time.