diff options
| -rw-r--r-- | lib/grunt/utils.js | 2 | ||||
| -rw-r--r-- | src/Angular.js | 7 | ||||
| -rwxr-xr-x | src/AngularPublic.js | 6 | ||||
| -rw-r--r-- | src/ng/directive/ngCsp.js | 24 | ||||
| -rw-r--r-- | src/ng/sniffer.js | 2 | ||||
| -rw-r--r-- | test/AngularSpec.js | 40 | ||||
| -rw-r--r-- | test/ng/directive/ngCspSpec.js | 10 | ||||
| -rw-r--r-- | test/ng/snifferSpec.js | 13 | 
8 files changed, 64 insertions, 40 deletions
| diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 764f481f..c7d4431b 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -91,7 +91,7 @@ module.exports = {          .replace(/\\/g, '\\\\')          .replace(/'/g, "\\'")          .replace(/\r?\n/g, '\\n'); -      return "angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>');"; +      return "!angular.$$csp() && angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>');";      }    }, diff --git a/src/Angular.js b/src/Angular.js index 305f1321..085e062d 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -760,6 +760,13 @@ function equals(o1, o2) {  } +function csp() { +  return (document.securityPolicy && document.securityPolicy.isActive) || +      (document.querySelector && +      !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]'))); +} + +  function concat(array1, array2, index) {    return array1.concat(slice.call(array2, index));  } diff --git a/src/AngularPublic.js b/src/AngularPublic.js index f7f78ae5..09361079 100755 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -44,12 +44,13 @@ function publishExternalAPI(angular){      'isNumber': isNumber,      'isElement': isElement,      'isArray': isArray, -    '$$minErr': minErr,      'version': version,      'isDate': isDate,      'lowercase': lowercase,      'uppercase': uppercase, -    'callbacks': {counter: 0} +    'callbacks': {counter: 0}, +    '$$minErr': minErr, +    '$$csp': csp    });    angularModule = setupModuleLoader(window); @@ -77,7 +78,6 @@ function publishExternalAPI(angular){              ngClass: ngClassDirective,              ngClassEven: ngClassEvenDirective,              ngClassOdd: ngClassOddDirective, -            ngCsp: ngCspDirective,              ngCloak: ngCloakDirective,              ngController: ngControllerDirective,              ngForm: ngFormDirective, diff --git a/src/ng/directive/ngCsp.js b/src/ng/directive/ngCsp.js index 174e4c58..1a099f59 100644 --- a/src/ng/directive/ngCsp.js +++ b/src/ng/directive/ngCsp.js @@ -3,25 +3,26 @@  /**   * @ngdoc directive   * @name ng.directive:ngCsp - * @priority 1000   *   * @element html   * @description   * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. - *  + *   * This is necessary when developing things like Google Chrome Extensions. - *  + *   * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).   * For us to be compatible, we just need to implement the "getterFn" in $parse without violating   * any of these restrictions. - *  + *   * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`   * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will   * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will   * be raised. - *  + *   * In order to use this feature put the `ngCsp` directive on the root element of the application. - *  + * + * *Note: This directive is only available in the ng-csp and data-ng-csp attribute form.* + *   * @example   * This example shows how to apply the `ngCsp` directive to the `html` tag.     <pre> @@ -33,11 +34,6 @@     </pre>   */ -var ngCspDirective = ['$sniffer', function($sniffer) { -  return { -    priority: 1000, -    compile: function() { -      $sniffer.csp = true; -    } -  }; -}]; +// ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap +// the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute +// anywhere in the current doc diff --git a/src/ng/sniffer.js b/src/ng/sniffer.js index 7f26e312..9a7447cb 100644 --- a/src/ng/sniffer.js +++ b/src/ng/sniffer.js @@ -76,7 +76,7 @@ function $SnifferProvider() {          return eventSupport[event];        }, -      csp: document.securityPolicy ? document.securityPolicy.isActive : false, +      csp: csp(),        vendorPrefix: vendorPrefix,        transitions : transitions,        animations : animations diff --git a/test/AngularSpec.js b/test/AngularSpec.js index c1914947..1b08a18e 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -348,6 +348,46 @@ describe('angular', function() {    }); +  describe('csp', function() { +    var originalSecurityPolicy; + +    beforeEach(function() { +      originalSecurityPolicy = document.securityPolicy; +    }); + +    afterEach(function() { +      document.securityPolicy = originalSecurityPolicy; +    }); + + +    it('should return the false when CSP is not enabled (the default)', function() { +      expect(csp()).toBe(false); +    }); + + +    it('should return true if CSP is autodetected via CSP v1.1 securityPolicy.isActive property', function() { +      document.securityPolicy = {isActive: true}; +      expect(csp()).toBe(true); +    }); + +    it('should return the true when CSP is enabled manually via [ng-csp]', function() { +      spyOn(document, 'querySelector').andCallFake(function(selector) { +        if (selector == '[ng-csp]') return {}; +      }); +      expect(csp()).toBe(true); +    }); + + +    it('should return the true when CSP is enabled manually via [data-ng-csp]', function() { +      spyOn(document, 'querySelector').andCallFake(function(selector) { +        if (selector == '[data-ng-csp]') return {}; +      }); +      expect(csp()).toBe(true); +      expect(document.querySelector).toHaveBeenCalledWith('[data-ng-csp]'); +    }); +  }); + +    describe('parseKeyValue', function() {      it('should parse a string into key-value pairs', function() {        expect(parseKeyValue('')).toEqual({}); diff --git a/test/ng/directive/ngCspSpec.js b/test/ng/directive/ngCspSpec.js deleted file mode 100644 index 7a21b587..00000000 --- a/test/ng/directive/ngCspSpec.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -describe('ngCsp', function() { - -  it('it should turn on CSP mode in $sniffer', inject(function($sniffer, $compile) { -    expect($sniffer.csp).toBe(false); -    $compile('<div ng-csp></div>'); -    expect($sniffer.csp).toBe(true); -  })); -}); diff --git a/test/ng/snifferSpec.js b/test/ng/snifferSpec.js index 6edf9f61..6e9dc830 100644 --- a/test/ng/snifferSpec.js +++ b/test/ng/snifferSpec.js @@ -85,21 +85,12 @@ describe('$sniffer', function() {    describe('csp', function() { -    it('should be false if document.securityPolicy.isActive not available', function() { +    it('should be false by default', function() {        expect(sniffer({}).csp).toBe(false);      }); - - -    it('should use document.securityPolicy.isActive if available', function() { -      var createDocumentWithCSP = function(csp) { -        return {securityPolicy: {isActive: csp}}; -      }; - -      expect(sniffer({}, createDocumentWithCSP(false)).csp).toBe(false); -      expect(sniffer({}, createDocumentWithCSP(true)).csp).toBe(true); -    });    }); +    describe('vendorPrefix', function() {      it('should return the correct vendor prefix based on the browser', function() { | 
