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

Commit

Permalink
fix(ngCsp): allow CSP to be configurable
Browse files Browse the repository at this point in the history
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 #11933
Closes #8459
  • Loading branch information
petebacondarwin committed Jul 15, 2015
1 parent 3d4df22 commit e1fd333
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 11 deletions.
10 changes: 6 additions & 4 deletions src/ng/directive/ngCsp.js
Expand Up @@ -53,10 +53,12 @@
*
*
* * No declaration means that Angular will assume that you can do inline styles, but it will do
* a runtime check for unsafe-eval. E.g. `<body>`.
* a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
* of Angular.
*
* * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
* styles and unsafe eval. E.g. `<body ng-csp>`.
* styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
* of Angular.
*
* * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
* inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
Expand All @@ -65,8 +67,8 @@
* run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
*
* * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
* styles nor use eval, which is the same as an empty: ng-csp, except that no runtime check for
* unsafe eval will occur. E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
* styles nor use eval, which is the same as an empty: ng-csp.
* E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
*
* @example
* This example shows how to apply the `ngCsp` directive to the `html` tag.
Expand Down
7 changes: 4 additions & 3 deletions src/ng/parse.js
Expand Up @@ -1701,13 +1701,14 @@ function $ParseProvider() {
var cacheDefault = createMap();
var cacheExpensive = createMap();

this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
this.$get = ['$filter', function($filter) {
var noUnsafeEval = csp().noUnsafeEval;
var $parseOptions = {
csp: $sniffer.csp && $sniffer.csp.noUnsafeEval,
csp: noUnsafeEval,
expensiveChecks: false
},
$parseOptionsExpensive = {
csp: $sniffer.csp && $sniffer.csp.noUnsafeEval,
csp: noUnsafeEval,
expensiveChecks: true
};

Expand Down
16 changes: 12 additions & 4 deletions test/AngularSpec.js
Expand Up @@ -799,11 +799,10 @@ describe('angular', function() {
var originalFunction;

beforeEach(function() {
originalFunction = window.Function;
spyOn(window, 'Function');
});

afterEach(function() {
window.Function = originalFunction;
delete csp.rules;
});

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


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


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


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


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


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


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

Expand Down

0 comments on commit e1fd333

Please sign in to comment.