diff --git a/components/bower.json b/components/bower.json index 5bb095bc1..ccebdef1b 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.7.0", + "version": "4.7.1", "main": "handlebars.js", "license": "MIT", "dependencies": {} diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index 91f7baf80..44747f7f4 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ handlebars.js - 4.7.0 + 4.7.1 handlebars.js Authors https://github.com/wycats/handlebars.js/blob/master/LICENSE https://github.com/wycats/handlebars.js/ diff --git a/components/package.json b/components/package.json index d040cda4f..c296a32c2 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.7.0", + "version": "4.7.1", "license": "MIT", "jspm": { "main": "handlebars", diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index a80e0dde4..2aa3a62f5 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -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.7.0'; +export const VERSION = '4.7.1'; 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(); } }; diff --git a/lib/handlebars/internal/proto-access.js b/lib/handlebars/internal/proto-access.js index a10f5124c..a8c5394d5 100644 --- a/lib/handlebars/internal/proto-access.js +++ b/lib/handlebars/internal/proto-access.js @@ -1,6 +1,8 @@ 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; @@ -45,12 +47,24 @@ function checkWhiteList(protoAccessControlForType, propertyName) { if (protoAccessControlForType.defaultValue !== undefined) { return protoAccessControlForType.defaultValue; } - // eslint-disable-next-line no-console - 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 http://localhost:8080/api-reference/runtime-options.html#options-to-control-prototype-access for details` - ); + 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]; + }); +} diff --git a/package.json b/package.json index 62e640504..5c289a2c2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "4.7.0", + "version": "4.7.1", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [ diff --git a/release-notes.md b/release-notes.md index ac4fc5041..8e391a775 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,20 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.0...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.1...master) + +## 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 diff --git a/spec/security.js b/spec/security.js index bf0be1485..1b345f0cc 100644 --- a/spec/security.js +++ b/spec/security.js @@ -190,6 +190,10 @@ describe('security issues', function() { return 'returnValue'; }; + beforeEach(function() { + handlebarsEnv.resetLoggedPropertyAccesses(); + }); + afterEach(function() { sinon.restore(); }); @@ -214,6 +218,23 @@ describe('security issues', function() { expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/); }); + it('should only log the warning once', function() { + var spy = sinon.spy(console, 'error'); + + expectTemplate('{{aMethod}}') + .withInput(new TestClass()) + .withCompileOptions(compileOptions) + .toCompileTo(''); + + expectTemplate('{{aMethod}}') + .withInput(new TestClass()) + .withCompileOptions(compileOptions) + .toCompileTo(''); + + expect(spy.calledOnce).to.be.true(); + expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/); + }); + it('can be allowed, which disables the warning', function() { var spy = sinon.spy(console, 'error');