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

chore(ngCsp): add e2e tests #9136

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ module.exports = function(grunt) {
keepalive: true,
middleware: function(connect, options){
return [
//uncomment to enable CSP
// util.csp(),
util.conditionalCsp(),
util.rewrite(),
connect.favicon('images/favicon.ico'),
connect.static(options.base),
Expand All @@ -74,6 +73,7 @@ module.exports = function(grunt) {

next();
},
util.conditionalCsp(),
connect.favicon('images/favicon.ico'),
connect.static(options.base)
];
Expand Down
12 changes: 8 additions & 4 deletions lib/grunt/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,15 @@ module.exports = {


//csp connect middleware
csp: function(){
conditionalCsp: function(){
return function(req, res, next){
res.setHeader("X-WebKit-CSP", "default-src 'self';");
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
res.setHeader("Content-Security-Policy", "default-src 'self'");
var CSP = /\.csp\W/;

if (CSP.test(req.url)) {
res.setHeader("X-WebKit-CSP", "default-src 'self';");
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
res.setHeader("Content-Security-Policy", "default-src 'self'");
}
next();
};
},
Expand Down
120 changes: 119 additions & 1 deletion src/ng/directive/ngCsp.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,125 @@
...
</html>
```
*/
* @example
// Note: the suffix `.csp` in the example name triggers
// csp mode in our http server!
<example name="example.csp" module="cspExample" ng-csp="true">
<file name="index.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>
</file>
<file name="script.js">
angular.module('cspExample', [])
.controller('MainController', function() {
this.counter = 0;
this.inc = function() {
this.counter++;
};
this.evil = function() {
// jshint evil:true
try {
eval('1+2');
} catch (e) {
this.evilError = e.message;
}
};
});
</file>
<file name="protractor.js" type="protractor">
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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how I feel about this; if I understand correctly, this clears all errors, but returns only the severe ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Ignores warnings about css problems, ...
Right now protractor just ignores the errors...

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('protractor/node_modules/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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do all the browsers we support respect CSP headers? I'm just not sure about safari 6

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Safari 6 does with a prefix: http://caniuse.com/#feat=contentsecuritypolicy

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like IE9 does not, which is a problem for travis. IE10/11 also use prefixed headers :( Just skip the test if ie is detected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need to do feature detection for IE9, but should keep testing IE10/11.

evilBtn.click();
expect(evilError.getText()).toMatch(/Content Security Policy/);
expectError(/Content Security Policy/);
});
</file>
</example>
*/

// 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
Expand Down