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

Commit 0e5d319

Browse files
committed
fix(csp): fix autodetection of CSP + better docs
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
1 parent f1b0d21 commit 0e5d319

File tree

3 files changed

+43
-15
lines changed

3 files changed

+43
-15
lines changed

src/Angular.js

+18-5
Original file line numberDiff line numberDiff line change
@@ -925,12 +925,25 @@ function equals(o1, o2) {
925925
return false;
926926
}
927927

928+
var csp = function() {
929+
if (isDefined(csp.isActive_)) return csp.isActive_;
930+
931+
var active = !!(document.querySelector('[ng-csp]') ||
932+
document.querySelector('[data-ng-csp]'));
933+
934+
if (!active) {
935+
try {
936+
/* jshint -W031, -W054 */
937+
new Function('');
938+
/* jshint +W031, +W054 */
939+
} catch (e) {
940+
active = true;
941+
}
942+
}
943+
944+
return (csp.isActive_ = active);
945+
};
928946

929-
function csp() {
930-
return (document.securityPolicy && document.securityPolicy.isActive) ||
931-
(document.querySelector &&
932-
!!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
933-
}
934947

935948

936949
function concat(array1, array2, index) {

src/ng/directive/ngCsp.js

+19-6
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
* This is necessary when developing things like Google Chrome Extensions.
1212
*
1313
* CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
14-
* For us to be compatible, we just need to implement the "getterFn" in $parse without violating
15-
* any of these restrictions.
14+
* For Angular to be CSP compatible there are only two things that we need to do differently:
15+
*
16+
* - don't use `Function` constructor to generate optimized value getters
17+
* - don't inject custom stylesheet into the document
1618
*
1719
* AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
1820
* directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
@@ -23,7 +25,18 @@
2325
* includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
2426
* To make those directives work in CSP mode, include the `angular-csp.css` manually.
2527
*
26-
* In order to use this feature put the `ngCsp` directive on the root element of the application.
28+
* Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
29+
* autodetection however triggers a CSP error to be logged in the console:
30+
*
31+
* ```
32+
* Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
33+
* script in the following Content Security Policy directive: "default-src 'self'". Note that
34+
* 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
35+
* ```
36+
*
37+
* This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
38+
* directive on the root element of the application or on the `angular.js` script tag, whichever
39+
* appears first in the html document.
2740
*
2841
* *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
2942
*
@@ -38,6 +51,6 @@
3851
```
3952
*/
4053

41-
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap
42-
// the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute
43-
// anywhere in the current doc
54+
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
55+
// bootstrap the system (before $parse is instantiated), for this reason we just have
56+
// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc

test/AngularSpec.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -404,14 +404,15 @@ describe('angular', function() {
404404

405405

406406
describe('csp', function() {
407-
var originalSecurityPolicy;
407+
var originalFunction;
408408

409409
beforeEach(function() {
410-
originalSecurityPolicy = document.securityPolicy;
410+
originalFunction = window.Function;
411411
});
412412

413413
afterEach(function() {
414-
document.securityPolicy = originalSecurityPolicy;
414+
window.Function = originalFunction;
415+
delete csp.isActive_;
415416
});
416417

417418

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

422423

423424
it('should return true if CSP is autodetected via CSP v1.1 securityPolicy.isActive property', function() {
424-
document.securityPolicy = {isActive: true};
425+
window.Function = function() { throw new Error('CSP test'); };
425426
expect(csp()).toBe(true);
426427
});
427428

429+
428430
it('should return the true when CSP is enabled manually via [ng-csp]', function() {
429431
spyOn(document, 'querySelector').andCallFake(function(selector) {
430432
if (selector == '[ng-csp]') return {};

0 commit comments

Comments
 (0)