diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 5c194378bad2..204db413ecf2 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -116,7 +116,7 @@ module.exports = { .replace(/\\/g, '\\\\') .replace(/'/g, "\\'") .replace(/\r?\n/g, '\\n'); - js = "!window.angular.$$csp() && window.angular.element(document.head).prepend('');"; + js = "!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('');"; state.js.push(js); return state; diff --git a/src/Angular.js b/src/Angular.js index e66b9938a9a3..9d351a869bb0 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -984,22 +984,39 @@ function equals(o1, o2) { } var csp = function() { - if (isDefined(csp.isActive_)) return csp.isActive_; + if (!isDefined(csp.rules)) { - var active = !!(document.querySelector('[ng-csp]') || - document.querySelector('[data-ng-csp]')); - if (!active) { + var ngCspElement = (document.querySelector('[ng-csp]') || + document.querySelector('[data-ng-csp]')); + + if (ngCspElement) { + var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || + ngCspElement.getAttribute('data-ng-csp'); + csp.rules = { + noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), + noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) + }; + } else { + csp.rules = { + noUnsafeEval: noUnsafeEval(), + noInlineStyle: false + }; + } + } + + return csp.rules; + + function noUnsafeEval() { try { /* jshint -W031, -W054 */ new Function(''); /* jshint +W031, +W054 */ + return false; } catch (e) { - active = true; + return true; } } - - return (csp.isActive_ = active); }; /** diff --git a/src/AngularPublic.js b/src/AngularPublic.js index dff1ce015ae1..9afb79c16d6a 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -20,7 +20,6 @@ ngClassDirective, ngClassEvenDirective, ngClassOddDirective, - ngCspDirective, ngCloakDirective, ngControllerDirective, ngFormDirective, diff --git a/src/ng/directive/ngCsp.js b/src/ng/directive/ngCsp.js index 974d2e9305fb..378333b4a854 100644 --- a/src/ng/directive/ngCsp.js +++ b/src/ng/directive/ngCsp.js @@ -6,27 +6,29 @@ * * @element html * @description - * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * + * Angular has some features that can break certain + * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules. + * + * If you intend to implement these rules then you must tell Angular not to use these features. * * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps. * - * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). - * For Angular to be CSP compatible there are only two things that we need to do differently: * - * - don't use `Function` constructor to generate optimized value getters - * - don't inject custom stylesheet into the document + * The following rules affect Angular: * - * 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. + * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions + * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30% + * increase in the speed of evaluating Angular expressions. * - * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically - * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). - * To make those directives work in CSP mode, include the `angular-csp.css` manually. + * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular + * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). + * To make these directives work when a CSP rule is blocking inline styles, you must link to the + * `angular-csp.css` in your HTML manually. * - * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This - * autodetection however triggers a CSP error to be logged in the console: + * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval + * and automatically deactivates this feature in the {@link $parse} service. This autodetection, + * however, triggers a CSP error to be logged in the console: * * ``` * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of @@ -35,11 +37,39 @@ * ``` * * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` - * directive on the root element of the application or on the `angular.js` script tag, whichever - * appears first in the html document. + * directive on an element of the HTML document that appears before the `