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

Commit 0113f22

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 02c0ed2 commit 0113f22

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
@@ -921,12 +921,25 @@ function equals(o1, o2) {
921921
return false;
922922
}
923923

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

925-
function csp() {
926-
return (document.securityPolicy && document.securityPolicy.isActive) ||
927-
(document.querySelector &&
928-
!!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
929-
}
930943

931944

932945
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
@@ -413,14 +413,15 @@ describe('angular', function() {
413413

414414

415415
describe('csp', function() {
416-
var originalSecurityPolicy;
416+
var originalFunction;
417417

418418
beforeEach(function() {
419-
originalSecurityPolicy = document.securityPolicy;
419+
originalFunction = window.Function;
420420
});
421421

422422
afterEach(function() {
423-
document.securityPolicy = originalSecurityPolicy;
423+
window.Function = originalFunction;
424+
delete csp.isActive_;
424425
});
425426

426427

@@ -430,10 +431,11 @@ describe('angular', function() {
430431

431432

432433
it('should return true if CSP is autodetected via CSP v1.1 securityPolicy.isActive property', function() {
433-
document.securityPolicy = {isActive: true};
434+
window.Function = function() { throw new Error('CSP test'); };
434435
expect(csp()).toBe(true);
435436
});
436437

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

0 commit comments

Comments
 (0)