Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STRF-6929: Backport fixes to Handlebars 3.x #1

Merged
merged 2 commits into from Jun 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/bower.json
@@ -1,6 +1,6 @@
{
"name": "handlebars",
"version": "3.0.6",
"version": "3.1.0",
"main": "handlebars.js",
"dependencies": {}
}
2 changes: 1 addition & 1 deletion components/handlebars.js.nuspec
Expand Up @@ -2,7 +2,7 @@
<package>
<metadata>
<id>handlebars.js</id>
<version>3.0.6</version>
<version>3.1.0</version>
<authors>handlebars.js Authors</authors>
<licenseUrl>https://github.com/wycats/handlebars.js/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/wycats/handlebars.js/</projectUrl>
Expand Down
2 changes: 1 addition & 1 deletion components/package.json
@@ -1,6 +1,6 @@
{
"name": "handlebars",
"version": "3.0.6",
"version": "3.1.0",
"license": "MIT",
"jspm": {
"main": "handlebars",
Expand Down
8 changes: 7 additions & 1 deletion lib/handlebars/base.js
Expand Up @@ -212,7 +212,13 @@ function registerDefaultHelpers(instance) {
});

instance.registerHelper('lookup', function(obj, field) {
return obj && obj[field];
if (!obj) {
return obj;
}
if (field === 'constructor' && !obj.propertyIsEnumerable(field)) {
return undefined;
}
return obj[field];
});
}

Expand Down
3 changes: 3 additions & 0 deletions lib/handlebars/compiler/javascript-compiler.js
Expand Up @@ -13,6 +13,9 @@ JavaScriptCompiler.prototype = {
// PUBLIC API: You can override these methods in a subclass to provide
// alternative compiled forms for name lookup and buffering semantics
nameLookup: function(parent, name /* , type*/) {
if (name === 'constructor') {
return ['(', parent, '.propertyIsEnumerable(\'constructor\') ? ', parent, '.constructor : undefined', ')'];
}
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
return [parent, '.', name];
} else {
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,7 +1,7 @@
{
"name": "handlebars",
"barename": "handlebars",
"version": "3.0.6",
"version": "3.1.0",
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
"homepage": "http://www.handlebarsjs.com/",
"keywords": [
Expand Down
27 changes: 26 additions & 1 deletion release-notes.md
Expand Up @@ -2,7 +2,32 @@

## Development

[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.6...master)
[Commits](https://github.com/wycats/handlebars.js/compare/v3.1.0...master)

## v3.1.0 - June 28, 2019

Backport of security fixes from 4.1.0 and 4.1.2:

- disallow access to the constructor in templates to prevent RCE
- prevent RCE through the "lookup"-helper

Compatibility notes:

Access to class constructors (i.e. `({}).constructor`) is now prohibited to prevent
Remote Code Execution. This means that following construct will no work anymore:

```
class SomeClass {
}
SomeClass.staticProperty = 'static'
var template = Handlebars.compile('{{constructor.staticProperty}}');
document.getElementById('output').innerHTML = template(new SomeClass());
// expected: 'static', but now this is empty.
```

Similarly, access to the constructor of a class through {{lookup obj "constructor" }} is now prohibited.

This kind of access is not the intended use of Handlebars and leads to the vulnerability described in #1495. We will **not** increase the major version, because such use is not intended or documented, and because of the potential impact of the issue (we fear that most people won't use a new major version and the issue may not be resolved on many systems).

## v3.0.6 - January 2nd, 2019
Chore:
Expand Down
33 changes: 33 additions & 0 deletions spec/security.js
@@ -0,0 +1,33 @@
describe('security issues', function() {
describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
it('should not allow constructors to be accessed', function() {
shouldCompileTo('{{constructor.name}}', {}, '');
shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {}, '');
});

it('should allow the "constructor" property to be accessed if it is enumerable', function() {
shouldCompileTo('{{constructor.name}}', {'constructor': {
'name': 'here we go'
}}, 'here we go');
shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {'constructor': {
'name': 'here we go'
}}, 'here we go');
});

it('should allow prototype properties that are not constructors', function() {
function TestClass() {
}

Object.defineProperty(TestClass.prototype, 'abc', {
get: function() {
return 'xyz';
}
});

shouldCompileTo('{{#with this}}{{this.abc}}{{/with}}',
new TestClass(), 'xyz');
shouldCompileTo('{{#with this}}{{lookup this "abc"}}{{/with}}',
new TestClass(), 'xyz');
});
});
});
2 changes: 1 addition & 1 deletion tasks/util/git.js
Expand Up @@ -86,7 +86,7 @@ module.exports = {
});
},
tagName: function(callback) {
childProcess.exec('git describe --tags', {}, function(err, stdout) {
childProcess.exec('git describe --tags --always', {}, function(err, stdout) {
if (err) {
throw new Error('git.tagName: ' + err.message);
}
Expand Down