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

Commit 39841f2

Browse files
committed
fix($compile): disallow interpolations for DOM event handlers
BREAKING CHANGE: Interpolations inside DOM event handlers are disallowed. DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and likely leads to an XSS if you're not super careful. On the other hand, ng-click and such event handlers evaluate Angular expressions that are a lot safer (e.g. No direct access to global objects - only scope), cleaner and harder to exploit. To migrate the code follow the example below: Before: JS: scope.foo = 'alert(1)'; HTML: <div onclick="{{foo}}"> After: JS: scope.foo = function() { alert(1); } HTML: <div ng-click="foo()">
1 parent 1adf29a commit 39841f2

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

src/ng/compile.js

+10
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ function $CompileProvider($provide) {
155155
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
156156
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
157157

158+
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
159+
// The assumption is that future DOM event attribute names will begin with
160+
// 'on' and be composed of only English letters.
161+
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]*|formaction)$/;
158162

159163
/**
160164
* @ngdoc function
@@ -1165,6 +1169,12 @@ function $CompileProvider($provide) {
11651169
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
11661170
var $$observers = (attr.$$observers || (attr.$$observers = {}));
11671171

1172+
if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
1173+
throw $compileMinErr('nodomevents',
1174+
"Interpolations for HTML DOM event attributes are disallowed. Please use the ng- " +
1175+
"versions (such as ng-click instead of onclick) instead.");
1176+
}
1177+
11681178
// we need to interpolate again, in case the attribute value has been updated
11691179
// (e.g. by another directive's compile function)
11701180
interpolateFn = $interpolate(attr[name], true);

test/ng/compileSpec.js

+30
Original file line numberDiff line numberDiff line change
@@ -2831,6 +2831,36 @@ describe('$compile', function() {
28312831
});
28322832
});
28332833

2834+
describe('interpolation on HTML DOM event handler attributes onclick, onXYZ, formaction', function() {
2835+
it('should disallow interpolation on onclick', inject(function($compile, $rootScope) {
2836+
// All interpolations are disallowed.
2837+
$rootScope.onClickJs = "";
2838+
expect(function() {
2839+
$compile('<button onclick="{{onClickJs}}"></script>')($rootScope);
2840+
}).toThrow(
2841+
"[$compile:nodomevents] Interpolations for HTML DOM event attributes are disallowed. " +
2842+
"Please use the ng- versions (such as ng-click instead of onclick) instead.");
2843+
expect(function() {
2844+
$compile('<button ONCLICK="{{onClickJs}}"></script>')($rootScope);
2845+
}).toThrow(
2846+
"[$compile:nodomevents] Interpolations for HTML DOM event attributes are disallowed. " +
2847+
"Please use the ng- versions (such as ng-click instead of onclick) instead.");
2848+
expect(function() {
2849+
$compile('<button ng-attr-onclick="{{onClickJs}}"></script>')($rootScope);
2850+
}).toThrow(
2851+
"[$compile:nodomevents] Interpolations for HTML DOM event attributes are disallowed. " +
2852+
"Please use the ng- versions (such as ng-click instead of onclick) instead.");
2853+
}));
2854+
2855+
it('should pass through arbitrary values on onXYZ event attributes that contain a hyphen', inject(function($compile, $rootScope) {
2856+
element = $compile('<button on-click="{{onClickJs}}"></script>')($rootScope);
2857+
$rootScope.onClickJs = 'javascript:doSomething()';
2858+
$rootScope.$apply();
2859+
expect(element.attr('on-click')).toEqual('javascript:doSomething()');
2860+
}));
2861+
});
2862+
2863+
28342864
describe('ngAttr* attribute binding', function() {
28352865

28362866
it('should bind after digest but not before', inject(function($compile, $rootScope) {

0 commit comments

Comments
 (0)