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

Commit

Permalink
fix(csp): fix autodetection of CSP + better docs
Browse files Browse the repository at this point in the history
CSP spec got changed and it is no longer possible to autodetect if a policy is
active without triggering a CSP error:

w3c/webappsec@1888295

Now we use `new Function('')` to detect if CSP is on. To prevent error from this
detection to show up in console developers have to use the ngCsp directive.

(This problem became more severe after our recent removal of `simpleGetterFn`
 which made us depend on function constructor for all expressions.)

Closes #8162
Closes #8191
  • Loading branch information
IgorMinar committed Jul 15, 2014
1 parent f1b0d21 commit 0e5d319
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 15 deletions.
23 changes: 18 additions & 5 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -925,12 +925,25 @@ function equals(o1, o2) {
return false;
}

var csp = function() {
if (isDefined(csp.isActive_)) return csp.isActive_;

var active = !!(document.querySelector('[ng-csp]') ||
document.querySelector('[data-ng-csp]'));

if (!active) {
try {
/* jshint -W031, -W054 */
new Function('');
/* jshint +W031, +W054 */
} catch (e) {
active = true;
}
}

return (csp.isActive_ = active);
};

function csp() {
return (document.securityPolicy && document.securityPolicy.isActive) ||
(document.querySelector &&
!!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
}


function concat(array1, array2, index) {
Expand Down
25 changes: 19 additions & 6 deletions src/ng/directive/ngCsp.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
* 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.
* 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
*
* 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
Expand All @@ -23,7 +25,18 @@
* 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.
*
* In order to use this feature put the `ngCsp` directive on the root element of the application.
* 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:
*
* ```
* Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
* script in the following Content Security Policy directive: "default-src 'self'". Note that
* 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
* ```
*
* 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.
*
* *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
*
Expand All @@ -38,6 +51,6 @@
```
*/

// 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
// 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
// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
10 changes: 6 additions & 4 deletions test/AngularSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,14 +404,15 @@ describe('angular', function() {


describe('csp', function() {
var originalSecurityPolicy;
var originalFunction;

beforeEach(function() {
originalSecurityPolicy = document.securityPolicy;
originalFunction = window.Function;
});

afterEach(function() {
document.securityPolicy = originalSecurityPolicy;
window.Function = originalFunction;
delete csp.isActive_;
});


Expand All @@ -421,10 +422,11 @@ describe('angular', function() {


it('should return true if CSP is autodetected via CSP v1.1 securityPolicy.isActive property', function() {
document.securityPolicy = {isActive: true};
window.Function = function() { throw new Error('CSP test'); };
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 {};
Expand Down

0 comments on commit 0e5d319

Please sign in to comment.