Skip to content

Commit e1fd333

Browse files
fix(ngCsp): allow CSP to be configurable
There are two different features in Angular that can break CSP rules: use of `eval` to execute a string as JavaScript and dynamic injection of CSS style rules into the DOM. This change allows us to configure which of these features should be turned off to allow a more fine grained set of CSP rules to be supported. Closes angular#11933 Closes angular#8459
1 parent 3d4df22 commit e1fd333

File tree

3 files changed

+22
-11
lines changed

3 files changed

+22
-11
lines changed

src/ng/directive/ngCsp.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@
5353
*
5454
*
5555
* * No declaration means that Angular will assume that you can do inline styles, but it will do
56-
* a runtime check for unsafe-eval. E.g. `<body>`.
56+
* a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
57+
* of Angular.
5758
*
5859
* * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
59-
* styles and unsafe eval. E.g. `<body ng-csp>`.
60+
* styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
61+
* of Angular.
6062
*
6163
* * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
6264
* inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
@@ -65,8 +67,8 @@
6567
* run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
6668
*
6769
* * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
68-
* styles nor use eval, which is the same as an empty: ng-csp, except that no runtime check for
69-
* unsafe eval will occur. E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
70+
* styles nor use eval, which is the same as an empty: ng-csp.
71+
* E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
7072
*
7173
* @example
7274
* This example shows how to apply the `ngCsp` directive to the `html` tag.

src/ng/parse.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1701,13 +1701,14 @@ function $ParseProvider() {
17011701
var cacheDefault = createMap();
17021702
var cacheExpensive = createMap();
17031703

1704-
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
1704+
this.$get = ['$filter', function($filter) {
1705+
var noUnsafeEval = csp().noUnsafeEval;
17051706
var $parseOptions = {
1706-
csp: $sniffer.csp && $sniffer.csp.noUnsafeEval,
1707+
csp: noUnsafeEval,
17071708
expensiveChecks: false
17081709
},
17091710
$parseOptionsExpensive = {
1710-
csp: $sniffer.csp && $sniffer.csp.noUnsafeEval,
1711+
csp: noUnsafeEval,
17111712
expensiveChecks: true
17121713
};
17131714

test/AngularSpec.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -799,11 +799,10 @@ describe('angular', function() {
799799
var originalFunction;
800800

801801
beforeEach(function() {
802-
originalFunction = window.Function;
802+
spyOn(window, 'Function');
803803
});
804804

805805
afterEach(function() {
806-
window.Function = originalFunction;
807806
delete csp.rules;
808807
});
809808

@@ -814,40 +813,49 @@ describe('angular', function() {
814813

815814

816815
it('should return true for noUnsafeEval if eval causes a CSP security policy error', function() {
817-
window.Function = function() { throw new Error('CSP test'); };
816+
window.Function.andCallFake(function() { throw new Error('CSP test'); });
818817
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: false });
818+
expect(window.Function).toHaveBeenCalledWith('');
819819
});
820820

821821

822822
it('should return true for all rules when CSP is enabled manually via empty `ng-csp` attribute', function() {
823823
var spy = mockCspElement('ng-csp');
824824
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: true });
825825
expect(spy).toHaveBeenCalledWith('[ng-csp]');
826+
expect(window.Function).not.toHaveBeenCalled();
826827
});
827828

828829

829830
it('should return true when CSP is enabled manually via [data-ng-csp]', function() {
830831
var spy = mockCspElement('data-ng-csp');
831832
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: true });
832-
expect(document.querySelector).toHaveBeenCalledWith('[data-ng-csp]');
833+
expect(spy).toHaveBeenCalledWith('[data-ng-csp]');
834+
expect(window.Function).not.toHaveBeenCalled();
833835
});
834836

835837

836838
it('should return true for noUnsafeEval if it is specified in the `ng-csp` attribute value', function() {
837839
var spy = mockCspElement('ng-csp', 'no-unsafe-eval');
838840
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: false });
841+
expect(spy).toHaveBeenCalledWith('[ng-csp]');
842+
expect(window.Function).not.toHaveBeenCalled();
839843
});
840844

841845

842846
it('should return true for noInlineStyle if it is specified in the `ng-csp` attribute value', function() {
843847
var spy = mockCspElement('ng-csp', 'no-inline-style');
844848
expect(csp()).toEqual({ noUnsafeEval: false, noInlineStyle: true });
849+
expect(spy).toHaveBeenCalledWith('[ng-csp]');
850+
expect(window.Function).not.toHaveBeenCalled();
845851
});
846852

847853

848854
it('should return true for all styles if they are all specified in the `ng-csp` attribute value', function() {
849855
var spy = mockCspElement('ng-csp', 'no-inline-style;no-unsafe-eval');
850856
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: true });
857+
expect(spy).toHaveBeenCalledWith('[ng-csp]');
858+
expect(window.Function).not.toHaveBeenCalled();
851859
});
852860
});
853861

0 commit comments

Comments
 (0)