mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
fix(csp): fix csp auto-detection and stylesheet injection
When we refactored , we broke the csp mode because the previous implementation relied on the fact that it was ok to lazy initialize the .csp property, this is not the case any more. Besides, we need to know about csp mode during bootstrap and avoid injecting the stylesheet when csp is active, so I refactored the code to fix both issues. PR #4411 will follow up on this commit and add more improvements. Closes #917 Closes #2963 Closes #4394 Closes #4444 BREAKING CHANGE: triggering ngCsp directive via `ng:csp` attribute is not supported any more. Please use data-ng-csp instead.
This commit is contained in:
@@ -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>');";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -76,7 +76,7 @@ function $SnifferProvider() {
|
||||
|
||||
return eventSupport[event];
|
||||
},
|
||||
csp: document.securityPolicy ? document.securityPolicy.isActive : false,
|
||||
csp: csp(),
|
||||
vendorPrefix: vendorPrefix,
|
||||
transitions : transitions,
|
||||
animations : animations
|
||||
|
||||
@@ -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({});
|
||||
|
||||
@@ -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);
|
||||
}));
|
||||
});
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user