diff options
Diffstat (limited to 'test')
| -rwxr-xr-x | test/ng/compileSpec.js | 90 | ||||
| -rw-r--r-- | test/ng/directive/booleanAttrsSpec.js | 104 | ||||
| -rw-r--r-- | test/ng/directive/ngBindSpec.js | 48 | ||||
| -rw-r--r-- | test/ng/directive/ngIncludeSpec.js | 50 | ||||
| -rw-r--r-- | test/ng/directive/ngSrcSpec.js | 44 | ||||
| -rw-r--r-- | test/ng/interpolateSpec.js | 59 | ||||
| -rw-r--r-- | test/ng/sceSpecs.js | 347 | ||||
| -rw-r--r-- | test/ng/urlUtilsSpec.js | 18 | ||||
| -rw-r--r-- | test/ngRoute/routeSpec.js | 30 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 7 | 
10 files changed, 739 insertions, 58 deletions
| diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index c7821878..1f5aae95 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -681,9 +681,17 @@ describe('$compile', function() {                restrict: 'CAM', templateUrl: 'hello.html', transclude: true              }));              directive('cau', valueFn({ -              restrict: 'CAM', templateUrl:'cau.html' +              restrict: 'CAM', templateUrl: 'cau.html'              })); - +            directive('crossDomainTemplate', valueFn({ +              restrict: 'CAM', templateUrl: 'http://example.com/should-not-load.html' +            })); +            directive('trustedTemplate', function($sce) { return { +              restrict: 'CAM', +              templateUrl: function() { +                return $sce.trustAsResourceUrl('http://example.com/trusted-template.html'); +              }}; +            });              directive('cError', valueFn({                restrict: 'CAM',                templateUrl:'error.html', @@ -735,6 +743,24 @@ describe('$compile', function() {            }          )); +        it('should not load cross domain templates by default', inject( +            function($compile, $rootScope, $templateCache, $sce) { +              expect(function() { +                $templateCache.put('http://example.com/should-not-load.html', 'Should not load even if in cache.'); +                $compile('<div class="crossDomainTemplate"></div>')($rootScope); +              }).toThrow('[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: http://example.com/should-not-load.html'); +        })); + +        it('should load cross domain templates when trusted', inject( +            function($compile, $httpBackend, $rootScope, $sce) { +              $httpBackend.expect('GET', 'http://example.com/trusted-template.html').respond('<span>example.com/trusted_template_contents</span>'); +              element = $compile('<div class="trustedTemplate"></div>')($rootScope); +              expect(sortedHtml(element)). +                  toEqual('<div class="trustedTemplate"></div>'); +              $httpBackend.flush(); +              expect(sortedHtml(element)). +                  toEqual('<div class="trustedTemplate"><span>example.com/trusted_template_contents</span></div>'); +        }));          it('should append template via $http and cache it in $templateCache', inject(              function($compile, $httpBackend, $templateCache, $rootScope, $browser) { @@ -1521,6 +1547,16 @@ describe('$compile', function() {            expect(element.attr('name')).toEqual('attr: angular');          })); +    describe('SCE values', function() { +      it('should resolve compile and link both attribute and text bindings', inject( +          function($rootScope, $compile, $sce) { +            $rootScope.name = $sce.trustAsHtml('angular'); +            element = $compile('<div name="attr: {{name}}">text: {{name}}</div>')($rootScope); +            $rootScope.$digest(); +            expect(element.text()).toEqual('text: angular'); +            expect(element.attr('name')).toEqual('attr: angular'); +          })); +    });      it('should decorate the binding with ng-binding and interpolation function', inject(          function($compile, $rootScope) { @@ -2625,12 +2661,16 @@ describe('$compile', function() {    }); -  describe('img[src] sanitization', function() { -    it('should NOT require trusted values for img src', inject(function($rootScope, $compile) { +  describe('img[src] sanitization', function($sce) { +    it('should NOT require trusted values for img src', inject(function($rootScope, $compile, $sce) {        element = $compile('<img src="{{testUrl}}"></img>')($rootScope);        $rootScope.testUrl = 'http://example.com/image.png';        $rootScope.$digest();        expect(element.attr('src')).toEqual('http://example.com/image.png'); +      // But it should accept trusted values anyway. +      $rootScope.testUrl = $sce.trustAsUrl('http://example.com/image2.png'); +      $rootScope.$digest(); +      expect(element.attr('src')).toEqual('http://example.com/image2.png');      }));      it('should sanitize javascript: urls', inject(function($compile, $rootScope) { @@ -2965,6 +3005,48 @@ describe('$compile', function() {      }));    }); +  describe('iframe[src]', function() { +    it('should pass through src attributes for the same domain', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = "different_page"; +      $rootScope.$apply(); +      expect(element.attr('src')).toEqual('different_page'); +    })); + +    it('should clear out src attributes for a different domain', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = "http://a.different.domain.example.com"; +      expect(function() { $rootScope.$apply() }).toThrow( +          "[$interpolate:interr] Can't interpolate: {{testUrl}}\nError: [$sce:isecrurl] Blocked " + +          "loading resource from url not allowed by $sceDelegate policy.  URL: " + +          "http://a.different.domain.example.com"); +    })); + +    it('should clear out JS src attributes', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = "javascript:alert(1);"; +      expect(function() { $rootScope.$apply() }).toThrow( +          "[$interpolate:interr] Can't interpolate: {{testUrl}}\nError: [$sce:isecrurl] Blocked " + +          "loading resource from url not allowed by $sceDelegate policy.  URL: " + +          "javascript:alert(1);"); +    })); + +    it('should clear out non-resource_url src attributes', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()"); +      expect($rootScope.$apply).toThrow( +          "[$interpolate:interr] Can't interpolate: {{testUrl}}\nError: [$sce:isecrurl] Blocked " + +          "loading resource from url not allowed by $sceDelegate policy.  URL: javascript:doTrustedStuff()"); +    })); + +    it('should pass through $sce.trustAs() values in src attributes', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = $sce.trustAsResourceUrl("javascript:doTrustedStuff()"); +      $rootScope.$apply(); + +      expect(element.attr('src')).toEqual('javascript:doTrustedStuff()'); +    })); +  });    describe('ngAttr* attribute binding', function() { diff --git a/test/ng/directive/booleanAttrsSpec.js b/test/ng/directive/booleanAttrsSpec.js index be2dfb60..93e8cc20 100644 --- a/test/ng/directive/booleanAttrsSpec.js +++ b/test/ng/directive/booleanAttrsSpec.js @@ -102,61 +102,99 @@ describe('boolean attr directives', function() {  describe('ngSrc', function() { -  it('should interpolate the expression and bind to src', inject(function($compile, $rootScope) { +  it('should interpolate the expression and bind to src with raw same-domain value', +      inject(function($compile, $rootScope) { +        var element = $compile('<div ng-src="{{id}}"></div>')($rootScope); + +        $rootScope.$digest(); +        expect(element.attr('src')).toBeUndefined(); + +        $rootScope.$apply(function() { +          $rootScope.id = '/somewhere/here'; +        }); +        expect(element.attr('src')).toEqual('/somewhere/here'); + +        dealoc(element); +      })); + + +  it('should interpolate the expression and bind to src with a trusted value', inject(function($compile, $rootScope, $sce) {      var element = $compile('<div ng-src="{{id}}"></div>')($rootScope);      $rootScope.$digest();      expect(element.attr('src')).toBeUndefined();      $rootScope.$apply(function() { -      $rootScope.id = 1; +      $rootScope.id = $sce.trustAsResourceUrl('http://somewhere');      }); -    expect(element.attr('src')).toEqual('1'); +    expect(element.attr('src')).toEqual('http://somewhere');      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/'); +  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}}\nStrict " + +          "Contextual Escaping disallows interpolations that concatenate multiple expressions " + +          "when a trusted value is required.  See http://docs.angularjs.org/api/ng.$sce"); +  })); + + +  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'); +  })); + + +  it('should NOT interpolate a wrongly typed expression', inject(function($compile, $rootScope, $sce) { +    expect(function() { +      var element = $compile('<div ng-src="{{id}}"></div>')($rootScope);        $rootScope.$apply(function() { -        $rootScope.id = 1; +        $rootScope.id = $sce.trustAsUrl('http://somewhere');        }); -      expect(element.attr('foo')).toEqual('some/1'); -    })); +      element.attr('src'); +    }).toThrow( +            "[$interpolate:interr] Can't interpolate: {{id}}\nError: [$sce:isecrurl] Blocked " + +                "loading resource from url not allowed by $sceDelegate policy.  URL: http://somewhere"); +  })); -  });    if (msie) {      it('should update the element property as well as the attribute', inject( -        function($compile, $rootScope) { -      // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist -      // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need -      // to set the property as well to achieve the desired effect +        function($compile, $rootScope, $sce) { +          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist +          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need +          // to set the property as well to achieve the desired effect -      var element = $compile('<div ng-src="{{id}}"></div>')($rootScope); +          var element = $compile('<div ng-src="{{id}}"></div>')($rootScope); -      $rootScope.$digest(); -      expect(element.prop('src')).toBeUndefined(); +          $rootScope.$digest(); +          expect(element.prop('src')).toBeUndefined(); +          dealoc(element); -      $rootScope.$apply(function() { -        $rootScope.id = 1; -      }); -      expect(element.prop('src')).toEqual('1'); +          element = $compile('<div ng-src="some/"></div>')($rootScope); -      dealoc(element); -    })); +          $rootScope.$digest(); +          expect(element.prop('src')).toEqual('some/'); +          dealoc(element); + +          element = $compile('<div ng-src="{{id}}"></div>')($rootScope); +          $rootScope.$apply(function() { +            $rootScope.id = $sce.trustAsResourceUrl('http://somewhere'); +          }); +          expect(element.prop('src')).toEqual('http://somewhere'); + +          dealoc(element); +        }));    }  }); diff --git a/test/ng/directive/ngBindSpec.js b/test/ng/directive/ngBindSpec.js index da291fa4..1d8f8ef4 100644 --- a/test/ng/directive/ngBindSpec.js +++ b/test/ng/directive/ngBindSpec.js @@ -69,11 +69,47 @@ describe('ngBind*', function() {    describe('ngBindHtmlUnsafe', function() { -    it('should set unsafe html', inject(function($rootScope, $compile) { -      element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope); -      $rootScope.html = '<div onclick="">hello</div>'; -      $rootScope.$digest(); -      expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>'); -    })); +    function configureSce(enabled) { +      module(function($provide, $sceProvider) { +        $sceProvider.enabled(enabled); +      }); +    }; + +    describe('SCE disabled', function() { +      beforeEach(function() {configureSce(false)}); + +      it('should set unsafe html', inject(function($rootScope, $compile) { +        element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope); +        $rootScope.html = '<div onclick="">hello</div>'; +        $rootScope.$digest(); +        expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>'); +      })); +    }); + + +    describe('SCE enabled', function() { +      beforeEach(function() {configureSce(true)}); + +      it('should NOT set unsafe html for untrusted values', inject(function($rootScope, $compile) { +        element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope); +        $rootScope.html = '<div onclick="">hello</div>'; +        expect($rootScope.$digest).toThrow(); +      })); + +      it('should NOT set unsafe html for wrongly typed values', inject(function($rootScope, $compile, $sce) { +        element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope); +        $rootScope.html = $sce.trustAsCss('<div onclick="">hello</div>'); +        expect($rootScope.$digest).toThrow(); +      })); + +      it('should set unsafe html for trusted values', inject(function($rootScope, $compile, $sce) { +        element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope); +        $rootScope.html = $sce.trustAsHtml('<div onclick="">hello</div>'); +        $rootScope.$digest(); +        expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>'); +      })); + +    }); +    });  }); diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js index 93709431..6cb78755 100644 --- a/test/ng/directive/ngIncludeSpec.js +++ b/test/ng/directive/ngIncludeSpec.js @@ -3,7 +3,6 @@  describe('ngInclude', function() {    var element; -    afterEach(function(){      dealoc(element);    }); @@ -16,7 +15,29 @@ describe('ngInclude', function() {    } -  it('should include on external file', inject(putIntoCache('myUrl', '{{name}}'), +  it('should trust and use literal urls', inject(function( +      $rootScope, $httpBackend, $compile) { +    element = $compile('<div ng-include="\'url\'"></div>')($rootScope); +    $httpBackend.expect('GET', 'url').respond('template text'); +    $rootScope.$digest(); +    $httpBackend.flush(); +    expect(element.text()).toEqual('template text'); +    dealoc($rootScope); +  })); + + +  it('should trust and use trusted urls', inject(function($rootScope, $httpBackend, $compile, $sce) { +    element = $compile('<div ng-include="fooUrl"></div>')($rootScope); +    $httpBackend.expect('GET', 'http://foo.bar/url').respond('template text'); +    $rootScope.fooUrl = $sce.trustAsResourceUrl('http://foo.bar/url'); +    $rootScope.$digest(); +    $httpBackend.flush(); +    expect(element.text()).toEqual('template text'); +    dealoc($rootScope); +  })); + + +  it('should include an external file', inject(putIntoCache('myUrl', '{{name}}'),        function($rootScope, $compile) {      element = jqLite('<ng:include src="url"></ng:include>');      jqLite(document.body).append(element); @@ -42,6 +63,29 @@ describe('ngInclude', function() {    })); +  it('should NOT use untrusted expressions ', inject(putIntoCache('myUrl', '{{name}} text'), +      function($rootScope, $compile, $sce) { +    element = jqLite('<ng:include src="url"></ng:include>'); +    jqLite(document.body).append(element); +    element = $compile(element)($rootScope); +    $rootScope.name = 'chirayu'; +    $rootScope.url = 'myUrl'; +    expect($rootScope.$digest).toThrow(); +    jqLite(document.body).html(''); +  })); + + +  it('should NOT use mistyped expressions ', inject(putIntoCache('myUrl', '{{name}} text'), +      function($rootScope, $compile, $sce) { +    element = jqLite('<ng:include src="url"></ng:include>'); +    jqLite(document.body).append(element); +    element = $compile(element)($rootScope); +    $rootScope.name = 'chirayu'; +    $rootScope.url = $sce.trustAsUrl('myUrl'); +    expect($rootScope.$digest).toThrow(); +    jqLite(document.body).html(''); +  })); +    it('should remove previously included text if a falsy value is bound to src', inject(          putIntoCache('myUrl', '{{name}}'),          function($rootScope, $compile) { @@ -308,7 +352,7 @@ describe('ngInclude ngAnimate', function() {    }    function applyCSS(element, cssProp, cssValue) { -    element.css(cssProp, cssValue);     +    element.css(cssProp, cssValue);      element.css(vendorPrefix + cssProp, cssValue);    } diff --git a/test/ng/directive/ngSrcSpec.js b/test/ng/directive/ngSrcSpec.js index a917c511..23ace7ee 100644 --- a/test/ng/directive/ngSrcSpec.js +++ b/test/ng/directive/ngSrcSpec.js @@ -14,4 +14,48 @@ describe('ngSrc', function() {      expect(element.attr('src')).not.toBe('');      expect(element.attr('src')).toBe(undefined);    })); + +  describe('iframe[ng-src]', function() { +    it('should pass through src attributes for the same domain', inject(function($compile, $rootScope) { +      element = $compile('<iframe ng-src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = "different_page"; +      $rootScope.$apply(); +      expect(element.attr('src')).toEqual('different_page'); +    })); + +    it('should error on src attributes for a different domain', inject(function($compile, $rootScope) { +      element = $compile('<iframe ng-src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = "http://a.different.domain.example.com"; +      expect(function() { $rootScope.$apply() }).toThrow( +          "[$interpolate:interr] Can't interpolate: {{testUrl}}\nError: [$sce:isecrurl] Blocked " + +          "loading resource from url not allowed by $sceDelegate policy.  URL: " + +          "http://a.different.domain.example.com"); +    })); + +    it('should error on JS src attributes', inject(function($compile, $rootScope) { +      element = $compile('<iframe ng-src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = "javascript:alert(1);"; +      expect(function() { $rootScope.$apply() }).toThrow( +          "[$interpolate:interr] Can't interpolate: {{testUrl}}\nError: [$sce:isecrurl] Blocked " + +          "loading resource from url not allowed by $sceDelegate policy.  URL: " + +          "javascript:alert(1);"); +    })); + +    it('should error on non-resource_url src attributes', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe ng-src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()"); +      expect($rootScope.$apply).toThrow( +          "[$interpolate:interr] Can't interpolate: {{testUrl}}\nError: [$sce:isecrurl] Blocked " + +          "loading resource from url not allowed by $sceDelegate policy.  URL: " + +          "javascript:doTrustedStuff()"); +    })); + +    it('should pass through $sce.trustAs() values in src attributes', inject(function($compile, $rootScope, $sce) { +      element = $compile('<iframe ng-src="{{testUrl}}"></iframe>')($rootScope); +      $rootScope.testUrl = $sce.trustAsResourceUrl("javascript:doTrustedStuff()"); +      $rootScope.$apply(); + +      expect(element.attr('src')).toEqual('javascript:doTrustedStuff()'); +    })); +  });  }); diff --git a/test/ng/interpolateSpec.js b/test/ng/interpolateSpec.js index 7569c0e2..d74b764a 100644 --- a/test/ng/interpolateSpec.js +++ b/test/ng/interpolateSpec.js @@ -67,6 +67,55 @@ describe('$interpolate', function() {    })); +  describe('interpolating in a trusted context', function() { +    var sce; +    beforeEach(function() { +      function log() {}; +      var fakeLog = {log: log, warn: log, info: log, error: log}; +      module(function($provide, $sceProvider) { +        $provide.value('$log', fakeLog); +        $sceProvider.enabled(true); +      }); +      inject(['$sce', function($sce) { sce = $sce; }]); +    }); + +    it('should NOT interpolate non-trusted expressions', inject(function($interpolate) { +      var foo = "foo"; +      expect($interpolate('{{foo}}', true, sce.CSS)({}, {foo: foo})).toEqual(''); +    })); + +    it('should NOT interpolate mistyped expressions', inject(function($interpolate) { +      var foo = sce.trustAsCss("foo"); +      expect($interpolate('{{foo}}', true, sce.HTML)({}, {foo: foo})).toEqual(''); +    })); + +    it('should interpolate trusted expressions in a regular context', inject(function($interpolate) { +      var foo = sce.trustAsCss("foo"); +      expect($interpolate('{{foo}}', true)({foo: foo})).toEqual('foo'); +    })); + +    it('should interpolate trusted expressions in a specific trustedContext', inject(function($interpolate) { +      var foo = sce.trustAsCss("foo"); +      expect($interpolate('{{foo}}', true, sce.CSS)({foo: foo})).toEqual('foo'); +    })); + +    // The concatenation of trusted values does not necessarily result in a trusted value.  (For +    // instance, you can construct evil JS code by putting together pieces of JS strings that are by +    // themselves safe to execute in isolation.) +    it('should NOT interpolate trusted expressions with multiple parts', inject(function($interpolate) { +      var foo = sce.trustAsCss("foo"); +      var bar = sce.trustAsCss("bar"); +      expect(function() { +        return $interpolate('{{foo}}{{bar}}', true, sce.CSS)( +             {foo: foo, bar: bar}); }).toThrow( +                "[$interpolate:noconcat] Error while interpolating: {{foo}}{{bar}}\n" + +                "Strict Contextual Escaping disallows interpolations that concatenate multiple " + +                "expressions when a trusted value is required.  See " + +                "http://docs.angularjs.org/api/ng.$sce"); +    })); +  }); + +    describe('provider', function() {      beforeEach(module(function($interpolateProvider) {        $interpolateProvider.startSymbol('--'); @@ -155,13 +204,15 @@ describe('$interpolate', function() {        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."); +            "[$interpolate:noconcat] Error while interpolating: constant/{{var}}\nStrict " + +            "Contextual Escaping disallows interpolations that concatenate multiple expressions " + +            "when a trusted value is required.  See http://docs.angularjs.org/api/ng.$sce");        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."); +            "[$interpolate:noconcat] Error while interpolating: {{foo}}{{bar}}\nStrict " + +            "Contextual Escaping disallows interpolations that concatenate multiple expressions " + +            "when a trusted value is required.  See http://docs.angularjs.org/api/ng.$sce");      }));      it('should interpolate a multi-part expression when isTrustedContext is false', inject(function($interpolate) { diff --git a/test/ng/sceSpecs.js b/test/ng/sceSpecs.js new file mode 100644 index 00000000..16525b8d --- /dev/null +++ b/test/ng/sceSpecs.js @@ -0,0 +1,347 @@ +'use strict'; + +describe('SCE', function() { + +  describe('when disabled', function() { +    beforeEach(function() { +      module(function($sceProvider) { +        $sceProvider.enabled(false); +      }); +    }); + +    it('should provide the getter for enabled', inject(function($sce) { +      expect($sce.isEnabled()).toBe(false); +    })); + +    it('should not wrap/unwrap any value or throw exception on non-string values', inject(function($sce) { +      var originalValue = { foo: "bar" }; +      expect($sce.trustAs($sce.JS, originalValue)).toBe(originalValue); +      expect($sce.getTrusted($sce.JS, originalValue)).toBe(originalValue); +    })); +  }); + +  describe('IE8 quirks mode', function() { +    function runTest(enabled, documentMode, expectException) { +      module(function($provide) { +        $provide.value('$document', [{ +          documentMode: documentMode, +          createElement: function() {} +        }]); +        $provide.value('$sceDelegate', {trustAs: null, valueOf: null, getTrusted: null}); +      }); + +      inject(function($window, $injector) { +        function constructSce() { +          var sceProvider = new $SceProvider(); +          sceProvider.enabled(enabled); +          return $injector.invoke(sceProvider.$get, sceProvider); +        } + +        var origMsie = $window.msie; +        try { +          $window.msie = true; +          if (expectException) { +            expect(constructSce).toThrow( +                '[$sce:iequirks] Strict Contextual Escaping does not support Internet Explorer ' + +                'version < 9 in quirks mode.  You can fix this by adding the text <!doctype html> to ' + +                'the top of your HTML document.  See http://docs.angularjs.org/api/ng.$sce for more ' + +                'information.'); +          } else { +            // no exception. +            constructSce(); +          } +        } +        finally { +          $window.msie = origMsie; +        } +      }); +    } + +    it('should throw an exception when sce is enabled in quirks mode', function() { +      runTest(true, 7, true); +    }); + +    it('should NOT throw an exception when sce is enabled and in standards mode', function() { +      runTest(true, 8, false); +    }); + +    it('should NOT throw an exception when sce is enabled and documentMode is undefined', function() { +      runTest(true, undefined, false); +    }); + +    it('should NOT throw an exception when sce is disabled even when in quirks mode', function() { +      runTest(false, 7, false); +    }); + +    it('should NOT throw an exception when sce is disabled and in standards mode', function() { +      runTest(false, 8, false); +    }); + +    it('should NOT throw an exception when sce is disabled and documentMode is undefined', function() { +      runTest(false, undefined, false); +    }); +  }); + +  describe('when enabled', function() { +    it('should wrap string values with TrustedValueHolder', inject(function($sce) { +      var originalValue = 'original_value'; +      var wrappedValue = $sce.trustAs($sce.HTML, originalValue); +      expect(typeof wrappedValue).toBe('object'); +      expect($sce.getTrusted($sce.HTML, wrappedValue)).toBe('original_value'); +      expect(function() { $sce.getTrusted($sce.CSS, wrappedValue); }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +      wrappedValue = $sce.trustAs($sce.CSS, originalValue); +      expect(typeof wrappedValue).toBe('object'); +      expect($sce.getTrusted($sce.CSS, wrappedValue)).toBe('original_value'); +      expect(function() { $sce.getTrusted($sce.HTML, wrappedValue); }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +      wrappedValue = $sce.trustAs($sce.URL, originalValue); +      expect(typeof wrappedValue).toBe('object'); +      expect($sce.getTrusted($sce.URL, wrappedValue)).toBe('original_value'); +      wrappedValue = $sce.trustAs($sce.JS, originalValue); +      expect(typeof wrappedValue).toBe('object'); +      expect($sce.getTrusted($sce.JS, wrappedValue)).toBe('original_value'); +    })); + +    it('should NOT wrap non-string values', inject(function($sce) { +      expect(function() { $sce.trustAsCss(123); }).toThrow( +          '[$sce:itype] Attempted to trust a non-string value in a content requiring a string: ' + +          'Context: css'); +    })); + +    it('should NOT wrap unknown contexts', inject(function($sce) { +      expect(function() { $sce.trustAs('unknown1' , '123'); }).toThrow( +          '[$sce:icontext] Attempted to trust a value in invalid context. Context: unknown1; Value: 123'); +    })); + +    it('should NOT wrap undefined context', inject(function($sce) { +      expect(function() { $sce.trustAs(undefined, '123'); }).toThrow( +          '[$sce:icontext] Attempted to trust a value in invalid context. Context: undefined; Value: 123'); +    })); + +    it('should wrap undefined into undefined', inject(function($sce) { +      expect($sce.trustAsHtml(undefined)).toBe(undefined); +    })); + +    it('should unwrap undefined into undefined', inject(function($sce) { +      expect($sce.getTrusted($sce.HTML, undefined)).toBe(undefined); +    })); + +    it('should wrap null into null', inject(function($sce) { +      expect($sce.trustAsHtml(null)).toBe(null); +    })); + +    it('should unwrap null into null', inject(function($sce) { +      expect($sce.getTrusted($sce.HTML, null)).toBe(null); +    })); + +    it('should wrap "" into ""', inject(function($sce) { +      expect($sce.trustAsHtml("")).toBe(""); +    })); + +    it('should unwrap null into null', inject(function($sce) { +      expect($sce.getTrusted($sce.HTML, null)).toBe(null); +    })); + +    it('should unwrap "" into ""', inject(function($sce) { +      expect($sce.getTrusted($sce.HTML, "")).toBe(""); +    })); + +    it('should unwrap values and return the original', inject(function($sce) { +      var originalValue = "originalValue"; +      var wrappedValue = $sce.trustAs($sce.HTML, originalValue); +      expect($sce.getTrusted($sce.HTML, wrappedValue)).toBe(originalValue); +    })); + +    it('should NOT unwrap values when the type is different', inject(function($sce) { +      var originalValue = "originalValue"; +      var wrappedValue = $sce.trustAs($sce.HTML, originalValue); +      expect(function () { $sce.getTrusted($sce.CSS, wrappedValue); }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +    })); + +    it('should NOT unwrap values that had not been wrapped', inject(function($sce) { +      function TrustedValueHolder(trustedValue) { +        this.$unwrapTrustedValue = function() { +          return trustedValue; +        }; +      } +      var wrappedValue = new TrustedValueHolder("originalValue"); +      expect(function() { return $sce.getTrusted($sce.HTML, wrappedValue) }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +    })); + +    it('should implement toString on trusted values', inject(function($sce) { +      var originalValue = '123', +          wrappedValue = $sce.trustAsHtml(originalValue); +      expect($sce.getTrustedHtml(wrappedValue)).toBe(originalValue); +      expect(wrappedValue.toString()).toBe(originalValue.toString()); +    })); +  }); + + +  describe('replace $sceDelegate', function() { +    it('should override the default $sce.trustAs/valueOf/etc.', function() { +      module(function($provide) { +        $provide.value('$sceDelegate', { +            trustAs: function(type, value) { return "wrapped:"   + value; }, +            getTrusted: function(type, value) { return "unwrapped:" + value; }, +            valueOf: function(value) { return "valueOf:" + value; } +        }); +      }); + +      inject(function($sce) { +        expect($sce.trustAsJs("value")).toBe("wrapped:value"); +        expect($sce.valueOf("value")).toBe("valueOf:value"); +        expect($sce.getTrustedJs("value")).toBe("unwrapped:value"); +        expect($sce.parseAsJs("name")({name: "chirayu"})).toBe("unwrapped:chirayu"); +      }); +    }); +  }); + + +  describe('$sce.parseAs', function($sce) { +   it('should parse constant literals as trusted', inject(function($sce) { +      expect($sce.parseAsJs('1')()).toBe(1); +      expect($sce.parseAsJs('1', $sce.ANY)()).toBe(1); +      expect($sce.parseAsJs('1', $sce.HTML)()).toBe(1); +      expect($sce.parseAsJs('1', 'UNDEFINED')()).toBe(1); +      expect($sce.parseAsJs('true')()).toBe(true); +      expect($sce.parseAsJs('false')()).toBe(false); +      expect($sce.parseAsJs('null')()).toBe(null); +      expect($sce.parseAsJs('undefined')()).toBe(undefined); +      expect($sce.parseAsJs('"string"')()).toBe("string"); +    })); + +    it('should NOT parse constant non-literals', inject(function($sce) { +      // Until there's a real world use case for this, we're disallowing +      // constant non-literals.  See $SceParseProvider. +      var exprFn = $sce.parseAsJs('1+1'); +      expect(exprFn).toThrow(); +    })); + +    it('should NOT return untrusted values from expression function', inject(function($sce) { +      var exprFn = $sce.parseAs($sce.HTML, 'foo'); +      expect(function() { +        return exprFn({}, {'foo': true}) +      }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +    })); + +    it('should NOT return trusted values of the wrong type from expression function', inject(function($sce) { +      var exprFn = $sce.parseAs($sce.HTML, 'foo'); +      expect(function() { +        return exprFn({}, {'foo': $sce.trustAs($sce.JS, '123')}) +      }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +    })); + +    it('should return trusted values from expression function', inject(function($sce) { +      var exprFn = $sce.parseAs($sce.HTML, 'foo'); +      expect(exprFn({}, {'foo': $sce.trustAs($sce.HTML, 'trustedValue')})).toBe('trustedValue'); +    })); + +    it('should support shorthand methods', inject(function($sce) { +      // Test shorthand parse methods. +      expect($sce.parseAsHtml('1')()).toBe(1); +      // Test short trustAs methods. +      expect($sce.trustAsAny).toBeUndefined(); +      expect(function() { +        // mismatched types. +        $sce.parseAsCss('foo')({}, {'foo': $sce.trustAsHtml('1')}); +      }).toThrow( +          '[$sce:unsafe] Attempting to use an unsafe value in a safe context.'); +    })); + +  }); + +  describe('$sceDelegate resource url policies', function() { +    function runTest(cfg, testFn) { +      return function() { +        module(function($sceDelegateProvider) { +          if (cfg.whiteList !== undefined) { +            $sceDelegateProvider.resourceUrlWhitelist(cfg.whiteList); +          } +          if (cfg.blackList !== undefined) { +            $sceDelegateProvider.resourceUrlBlacklist(cfg.blackList); +          } +        }); +        inject(testFn); +      } +    } + +    it('should default to "self" which allows relative urls', runTest({}, function($sce, $document) { +        expect($sce.getTrustedResourceUrl('foo/bar')).toEqual('foo/bar'); +    })); + +    it('should reject everything when whitelist is empty', runTest( +      { +        whiteList: [], +        blackList: [] +      }, function($sce) { +        expect(function() { $sce.getTrustedResourceUrl('#'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: #'); +    })); + +    it('should match against normalized urls', runTest( +      { +        whiteList: [/^foo$/], +        blackList: [] +      }, function($sce) { +        expect(function() { $sce.getTrustedResourceUrl('foo'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: foo'); +    })); + +    it('should support custom regex', runTest( +      { +        whiteList: [/^http:\/\/example\.com.*/], +        blackList: [] +      }, function($sce) { +        expect($sce.getTrustedResourceUrl('http://example.com/foo')).toEqual('http://example.com/foo'); +        expect(function() { $sce.getTrustedResourceUrl('https://example.com/foo'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: https://example.com/foo'); +    })); + +    it('should support the special string "self" in whitelist', runTest( +      { +        whiteList: ['self'], +        blackList: [] +      }, function($sce) { +        expect($sce.getTrustedResourceUrl('foo')).toEqual('foo'); +    })); + +    it('should support the special string "self" in blacklist', runTest( +      { +        whiteList: [/.*/], +        blackList: ['self'] +      }, function($sce) { +        expect(function() { $sce.getTrustedResourceUrl('foo'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: foo'); +    })); + +    it('should have blacklist override the whitelist', runTest( +      { +        whiteList: ['self'], +        blackList: ['self'] +      }, function($sce) { +        expect(function() { $sce.getTrustedResourceUrl('foo'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: foo'); +    })); + +    it('should support multiple items in both lists', runTest( +      { +        whiteList: [/^http:\/\/example.com\/1$/, /^http:\/\/example.com\/2$/, /^http:\/\/example.com\/3$/, 'self'], +        blackList: [/^http:\/\/example.com\/3$/, /open_redirect/], +      }, function($sce) { +        expect($sce.getTrustedResourceUrl('same_domain')).toEqual('same_domain'); +        expect($sce.getTrustedResourceUrl('http://example.com/1')).toEqual('http://example.com/1'); +        expect($sce.getTrustedResourceUrl('http://example.com/2')).toEqual('http://example.com/2'); +        expect(function() { $sce.getTrustedResourceUrl('http://example.com/3'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: http://example.com/3'); +        expect(function() { $sce.getTrustedResourceUrl('open_redirect'); }).toThrow( +          '[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: open_redirect'); +    })); + +  }); +}); + diff --git a/test/ng/urlUtilsSpec.js b/test/ng/urlUtilsSpec.js index 57043a5a..3c9bf847 100644 --- a/test/ng/urlUtilsSpec.js +++ b/test/ng/urlUtilsSpec.js @@ -11,21 +11,27 @@ describe('$$urlUtils', function() {        expect(parsed.href).toMatch(/https?:\/\//);        expect(parsed.protocol).toMatch(/^https?:/);        expect(parsed.host).not.toBe(""); +      expect(parsed.hostname).not.toBe(""); +      expect(parsed.pathname).not.toBe("");      }));    });    describe('isSameOrigin', function() { -    it('should support various combinations of urls', inject(function($$urlUtils, $document) { -      expect($$urlUtils.isSameOrigin('path')).toBe(true); +    it('should support various combinations of urls - both string and parsed', inject(function($$urlUtils, $document) { +      function expectIsSameOrigin(url, expectedValue) { +        expect($$urlUtils.isSameOrigin(url)).toBe(expectedValue); +        expect($$urlUtils.isSameOrigin($$urlUtils.resolve(url, true))).toBe(expectedValue); +      } +      expectIsSameOrigin('path', true);        var origin = $$urlUtils.resolve($document[0].location.href, true); -      expect($$urlUtils.isSameOrigin('//' + origin.host + '/path')).toBe(true); +      expectIsSameOrigin('//' + origin.host + '/path', true);        // Different domain. -      expect($$urlUtils.isSameOrigin('http://example.com/path')).toBe(false); +      expectIsSameOrigin('http://example.com/path', false);        // Auto fill protocol. -      expect($$urlUtils.isSameOrigin('//example.com/path')).toBe(false); +      expectIsSameOrigin('//example.com/path', false);        // Should not match when the ports are different.        // This assumes that the test is *not* running on port 22 (very unlikely). -      expect($$urlUtils.isSameOrigin('//' + origin.hostname + ':22/path')).toBe(false); +      expectIsSameOrigin('//' + origin.hostname + ':22/path', false);      }));    });  }); diff --git a/test/ngRoute/routeSpec.js b/test/ngRoute/routeSpec.js index 300ca2d7..13d149a1 100644 --- a/test/ngRoute/routeSpec.js +++ b/test/ngRoute/routeSpec.js @@ -13,6 +13,7 @@ describe('$route', function() {        $httpBackend.when('GET', 'foo.html').respond('foo');        $httpBackend.when('GET', 'baz.html').respond('baz');        $httpBackend.when('GET', 'bar.html').respond('bar'); +      $httpBackend.when('GET', 'http://example.com/trusted-template.html').respond('cross domain trusted template');        $httpBackend.when('GET', '404.html').respond('not found');      };    })); @@ -510,6 +511,33 @@ describe('$route', function() {        });      }); +    it('should NOT load cross domain templates by default', function() { +        module(function($routeProvider) { +          $routeProvider.when('/foo', { templateUrl: 'http://example.com/foo.html' }); +        }); + +      inject(function ($route, $location, $rootScope) { +        $location.path('/foo'); +        expect(function() { +          $rootScope.$digest(); +        }).toThrow('[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy.  URL: http://example.com/foo.html'); +      }); +    }); + +    it('should load cross domain templates that are trusted', function() { +      module(function($routeProvider, $sceDelegateProvider) { +        $routeProvider.when('/foo', { templateUrl: 'http://example.com/foo.html' }); +        $sceDelegateProvider.resourceUrlWhitelist([/^http:\/\/example\.com\/foo\.html$/]); +      }); + +      inject(function ($route, $location, $rootScope) { +        $httpBackend.whenGET('http://example.com/foo.html').respond('FOO BODY'); +        $location.path('/foo'); +        $rootScope.$digest(); +        $httpBackend.flush(); +        expect($route.current.locals.$template).toEqual('FOO BODY'); +      }); +    });      it('should not update $routeParams until $routeChangeSuccess', function() {        module(function($routeProvider) { @@ -904,7 +932,7 @@ describe('$route', function() {          return '<h1>' + routePathParams.id + '</h1>';        } -      module(function($routeProvider){ +      module(function($routeProvider) {          $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});          $routeProvider.when('/foo/:id', {template: customTemplateFn});        }); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 5fae2817..d97d88a1 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -107,7 +107,12 @@ function dealoc(obj) {    function cleanup(element) {      element.off().removeData(); -    for ( var i = 0, children = element.contents() || []; i < children.length; i++) { +    // Note:  We aren't using element.contents() here.  Under jQuery, element.contents() can fail +    // for IFRAME elements.  jQuery explicitly uses (element.contentDocument || +    // element.contentWindow.document) and both properties are null for IFRAMES that aren't attached +    // to a document. +    var children = element[0].childNodes || []; +    for ( var i = 0; i < children.length; i++) {        cleanup(angular.element(children[i]));      }    } | 
