Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix($parse): forbid __{define,lookup}{Getter,Setter}__ properties
Browse files Browse the repository at this point in the history
It was possible to use `{}.__defineGetter__.call(null, 'alert', (0).valueOf.bind(0))` to set
`window.alert` to a false-ish value, thereby breaking the `isWindow` check, which might lead
to arbitrary code execution in browsers that let you obtain the window object using Array methods.
Prevent that by blacklisting the nasty __{define,lookup}{Getter,Setter}__ properties.

BREAKING CHANGE:
This prevents the use of __{define,lookup}{Getter,Setter}__ inside angular
expressions. If you really need them for some reason, please wrap/bind them to make them
less dangerous, then make them available through the scope object.
  • Loading branch information
thejh authored and IgorMinar committed Jun 30, 2014
1 parent 528be29 commit 48fa3aa
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ function ensureSafeMemberName(name, fullExpression) {
throw $parseMinErr('isecfld',
'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (name === "__defineGetter__" || name === "__defineSetter__"
|| name === "__lookupGetter__" || name === "__lookupSetter__") {
throw $parseMinErr('isecgetset',
'Defining and looking up getters and setters in Angular expressions is disallowed! '
+'Expression: {0}', fullExpression);
}
return name;
}
Expand All @@ -62,6 +67,11 @@ function ensureSafeObject(obj, fullExpression) {
throw $parseMinErr('isecobj',
'Referencing Object in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (obj === ({}).__defineGetter__ || obj === ({}).__defineSetter__
|| obj === ({}).__lookupGetter__ || obj === ({}).__lookupSetter__) {
throw $parseMinErr('isecgetset',
'Defining and looking up getters and setters in Angular expressions is disallowed! '
+'Expression: {0}', fullExpression);
}
}
return obj;
Expand Down
73 changes: 73 additions & 0 deletions test/ng/parseSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,79 @@ describe('parser', function() {
expect(function() { scope.$eval('array'); }).not.toThrow();
});
});

describe('getters and setters', function() {
it('should NOT allow invocation of __defineGetter__', function() {
expect(function() {
scope.$eval('{}.__defineGetter__("a", "".charAt)');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__defineGetter__("a", "".charAt)');

expect(function() {
scope.$eval('{}.__defineGetter__.call({}, "a", "".charAt)');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__defineGetter__.call({}, "a", "".charAt)');

expect(function() {
scope.$eval('{}["__defineGetter__"].call({}, "a", "".charAt)');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}["__defineGetter__"].call({}, "a", "".charAt)');
});

it('should NOT allow invocation of __defineSetter__', function() {
expect(function() {
scope.$eval('{}.__defineSetter__("a", "".charAt)');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__defineSetter__("a", "".charAt)');

expect(function() {
scope.$eval('{}.__defineSetter__.call({}, "a", "".charAt)');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__defineSetter__.call({}, "a", "".charAt)');
});

it('should NOT allow invocation of __lookupGetter__', function() {
expect(function() {
scope.$eval('{}.__lookupGetter__("a")');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__lookupGetter__("a")');

expect(function() {
scope.$eval('{}.__lookupGetter__.call({}, "a")');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__lookupGetter__.call({}, "a")');
});

it('should NOT allow invocation of __lookupSetter__', function() {
expect(function() {
scope.$eval('{}.__lookupSetter__("a")');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__lookupSetter__("a")');

expect(function() {
scope.$eval('{}.__lookupSetter__.call({}, "a")');
}).toThrowMinErr(
'$parse', 'isecgetset', 'Defining and looking up getters and setters in '+
'Angular expressions is disallowed! Expression: '+
'{}.__lookupSetter__.call({}, "a")');
});
});
});

describe('overriding constructor', function() {
Expand Down

0 comments on commit 48fa3aa

Please sign in to comment.