aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorChirayu Krishnappa2013-06-21 19:00:45 -0700
committerChirayu Krishnappa2013-06-24 14:17:18 -0700
commit38deedd6e3d806eb8262bb43f26d47245f6c2739 (patch)
tree2307e8a84b040b32acf4fe8d325af0eb021469a1 /test
parent39841f2ec9b17b3b2920fd1eb548d444251f4f56 (diff)
downloadangular.js-38deedd6e3d806eb8262bb43f26d47245f6c2739.tar.bz2
fix($compile): reject multi-expression interpolations for src attribute
BREAKING CHANGE: Concatenating expressions makes it hard to reason about whether some combination of concatenated values are unsafe to use and could easily lead to XSS. By requiring that a single expression be used for *[src/ng-src] such as iframe[src], object[src], etc. (but not img[src/ng-src] since that value is sanitized), we ensure that the value that's used is assigned or constructed by some JS code somewhere that is more testable or make it obvious that you bound the value to some user controlled value. This helps reduce the load when auditing for XSS issues. To migrate your code, follow the example below: Before: JS: scope.baseUrl = 'page'; scope.a = 1; scope.b = 2; HTML: <!-- Are a and b properly escaped here? Is baseUrl controlled by user? --> <iframe src="{{baseUrl}}?a={{a}&b={{b}}"> After: JS: var baseUrl = "page"; scope.getIframeSrc = function() { // There are obviously better ways to do this. The // key point is that one will think about this and do // it the right way. var qs = ["a", "b"].map(function(value, name) { return encodeURIComponent(name) + "=" + encodeURIComponent(value); }).join("&"); // baseUrl isn't on scope so it isn't bound to a user // controlled value. return baseUrl + "?" + qs; } HTML: <iframe src="{{getIframeSrc()}}">
Diffstat (limited to 'test')
-rw-r--r--test/ng/directive/booleanAttrsSpec.js28
-rw-r--r--test/ng/interpolateSpec.js24
2 files changed, 49 insertions, 3 deletions
diff --git a/test/ng/directive/booleanAttrsSpec.js b/test/ng/directive/booleanAttrsSpec.js
index aa48afff..f2ea003c 100644
--- a/test/ng/directive/booleanAttrsSpec.js
+++ b/test/ng/directive/booleanAttrsSpec.js
@@ -89,19 +89,41 @@ describe('boolean attr directives', function() {
describe('ngSrc', function() {
it('should interpolate the expression and bind to src', inject(function($compile, $rootScope) {
- var element = $compile('<div ng-src="some/{{id}}"></div>')($rootScope);
+ var element = $compile('<div ng-src="{{id}}"></div>')($rootScope);
$rootScope.$digest();
- expect(element.attr('src')).toEqual('some/');
+ expect(element.attr('src')).toBeUndefined();
$rootScope.$apply(function() {
$rootScope.id = 1;
});
- expect(element.attr('src')).toEqual('some/1');
+ expect(element.attr('src')).toEqual('1');
dealoc(element);
}));
+ describe('isTrustedContext', function() {
+ it('should NOT interpolate a multi-part expression for non-img src attribute', inject(function($compile, $rootScope) {
+ expect(function() {
+ var element = $compile('<div ng-src="some/{{id}}"></div>')($rootScope);
+ dealoc(element);
+ }).toThrow(
+ "[$interpolate:noconcat] Error while interpolating: some/{{id}}\nYou may not use " +
+ "multiple expressions when interpolating this expression.");
+ }));
+
+ it('should interpolate a multi-part expression for regular attributes', inject(function($compile, $rootScope) {
+ var element = $compile('<div foo="some/{{id}}"></div>')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('foo')).toBe('some/');
+ $rootScope.$apply(function() {
+ $rootScope.id = 1;
+ });
+ expect(element.attr('foo')).toEqual('some/1');
+ }));
+
+ });
+
if (msie) {
it('should update the element property as well as the attribute', inject(
function($compile, $rootScope) {
diff --git a/test/ng/interpolateSpec.js b/test/ng/interpolateSpec.js
index 454d81aa..7569c0e2 100644
--- a/test/ng/interpolateSpec.js
+++ b/test/ng/interpolateSpec.js
@@ -149,6 +149,29 @@ describe('$interpolate', function() {
});
+ describe('isTrustedContext', function() {
+ it('should NOT interpolate a multi-part expression when isTrustedContext is true', inject(function($interpolate) {
+ var isTrustedContext = true;
+ expect(function() {
+ $interpolate('constant/{{var}}', true, isTrustedContext);
+ }).toThrow(
+ "[$interpolate:noconcat] Error while interpolating: constant/{{var}}\nYou may not use " +
+ "multiple expressions when interpolating this expression.");
+ expect(function() {
+ $interpolate('{{foo}}{{bar}}', true, isTrustedContext);
+ }).toThrow(
+ "[$interpolate:noconcat] Error while interpolating: {{foo}}{{bar}}\nYou may not use " +
+ "multiple expressions when interpolating this expression.");
+ }));
+
+ it('should interpolate a multi-part expression when isTrustedContext is false', inject(function($interpolate) {
+ expect($interpolate('some/{{id}}')()).toEqual('some/');
+ expect($interpolate('some/{{id}}')({id: 1})).toEqual('some/1');
+ expect($interpolate('{{foo}}{{bar}}')({foo: 1, bar: 2})).toEqual('12');
+ }));
+ });
+
+
describe('startSymbol', function() {
beforeEach(module(function($interpolateProvider) {
@@ -199,4 +222,5 @@ describe('$interpolate', function() {
expect($interpolate.endSymbol()).toBe('))');
}));
});
+
});