Improve this Doc  View Source

ngCsp

  1. - directive in module ng

Overview

AngularJS has some features that can conflict with certain restrictions that are applied when using CSP (Content Security Policy) rules.

If you intend to implement CSP with these rules then you must tell AngularJS not to use these features.

This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.

The following default rules in CSP affect AngularJS:

If you do not provide ngCsp then AngularJS tries to autodetect if CSP is blocking dynamic code creation from strings (e.g., unsafe-eval not specified in CSP header) and automatically deactivates this feature in the $parse service. This autodetection, however, triggers a CSP error to be logged in the console:

Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
script in the following Content Security Policy directive: "default-src 'self'". Note that
'script-src' was not explicitly set, so 'default-src' is used as a fallback.

This error is harmless but annoying. To prevent the error from showing up, put the ngCsp directive on an element of the HTML document that appears before the <script> tag that loads the angular.js file.

Note: This directive is only available in the ng-csp and data-ng-csp attribute form.

You can specify which of the CSP related AngularJS features should be deactivated by providing a value for the ng-csp attribute. The options are as follows:

You can use these values in the following combinations:

Directive Info

Usage

Example

This example shows how to apply the ngCsp directive to the html tag.

<!doctype html>
<html ng-app ng-csp>
...
...
</html>

<div ng-controller="MainController as ctrl">
  <div>
    <button ng-click="ctrl.inc()" id="inc">Increment</button>
    <span id="counter">
      {{ctrl.counter}}
    </span>
  </div>

  <div>
    <button ng-click="ctrl.evil()" id="evil">Evil</button>
    <span id="evilError">
      {{ctrl.evilError}}
    </span>
  </div>
</div>
angular.module('cspExample', [])
.controller('MainController', function MainController() {
   this.counter = 0;
   this.inc = function() {
     this.counter++;
   };
   this.evil = function() {
     try {
       eval('1+2'); // eslint-disable-line no-eval
     } catch (e) {
       this.evilError = e.message;
     }
   };
 });
var util, webdriver;

var incBtn = element(by.id('inc'));
var counter = element(by.id('counter'));
var evilBtn = element(by.id('evil'));
var evilError = element(by.id('evilError'));

function getAndClearSevereErrors() {
  return browser.manage().logs().get('browser').then(function(browserLog) {
    return browserLog.filter(function(logEntry) {
      return logEntry.level.value > webdriver.logging.Level.WARNING.value;
    });
  });
}

function clearErrors() {
  getAndClearSevereErrors();
}

function expectNoErrors() {
  getAndClearSevereErrors().then(function(filteredLog) {
    expect(filteredLog.length).toEqual(0);
    if (filteredLog.length) {
      console.log('browser console errors: ' + util.inspect(filteredLog));
    }
  });
}

function expectError(regex) {
  getAndClearSevereErrors().then(function(filteredLog) {
    var found = false;
    filteredLog.forEach(function(log) {
      if (log.message.match(regex)) {
        found = true;
      }
    });
    if (!found) {
      throw new Error('expected an error that matches ' + regex);
    }
  });
}

beforeEach(function() {
  util = require('util');
  webdriver = require('selenium-webdriver');
});

// For now, we only test on Chrome,
// as Safari does not load the page with Protractor's injected scripts,
// and Firefox webdriver always disables content security policy (#6358)
if (browser.params.browser !== 'chrome') {
  return;
}

it('should not report errors when the page is loaded', function() {
  // clear errors so we are not dependent on previous tests
  clearErrors();
  // Need to reload the page as the page is already loaded when
  // we come here
  browser.driver.getCurrentUrl().then(function(url) {
    browser.get(url);
  });
  expectNoErrors();
});

it('should evaluate expressions', function() {
  expect(counter.getText()).toEqual('0');
  incBtn.click();
  expect(counter.getText()).toEqual('1');
  expectNoErrors();
});

it('should throw and report an error when using "eval"', function() {
  evilBtn.click();
  expect(evilError.getText()).toMatch(/Content Security Policy/);
  expectError(/Content Security Policy/);
});