Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support koa style middleware #26

Closed
wants to merge 3 commits 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: 3 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@
// Whether only function scope should be used for scope tests.
"funcscope": false,
// Whether es.next specific syntax should be allowed.
"esnext": false,
"esnext": true,
// This option suppresses warnings about generator functions with no yield statement in them.
"noyield": true,
// Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"sub": true,

Expand Down
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ language: node_js

node_js:
- "0.11"
- "0.10"

before_script:
- npm install -g grunt-cli
- npm install -g grunt-cli
4 changes: 2 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-mocha-test');

grunt.registerTask('test', ['jshint', 'mochaTest']);

};
grunt.registerTask('test-koa', ['jshint', 'mochaTest']);
};
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@
[![Build Status](https://travis-ci.org/krakenjs/lusca.svg?branch=master)](https://travis-ci.org/krakenjs/lusca)
[![NPM version](https://badge.fury.io/js/lusca.svg)](http://badge.fury.io/js/lusca)

Web application security middleware.
Web application security middleware. Support express and koa.


## Usage

### For express

```js
var express = require('express'),
app = express(),
lusca = require('lusca');
app = express(),
lusca = require('lusca');

app.use(lusca({
csrf: true,
csp: { /* ... */},
xframe: 'SAMEORIGIN',
p3p: 'ABCDEF',
hsts: {maxAge: 31536000, includeSubDomains: true},
hsts: { maxAge: 31536000, includeSubDomains: true },
xssProtection: true
}));
```
Expand All @@ -34,6 +36,35 @@ app.use(lusca.hsts({ maxAge: 31536000 });
app.use(lusca.xssProtection(true);
```

### For koa

```js
var koa = require('koa'),
app = koa(),
lusca = require('lusca');

app.use(lusca({
koa: true, // make sure use koa style middleware

csrf: true,
csp: { /* ... */},
xframe: 'SAMEORIGIN',
p3p: 'ABCDEF',
hsts: { maxAge: 31536000, includeSubDomains: true },
xssProtection: true
}));
```

Setting any value to `false` will disable it. Alternately, you can opt into methods one by one:

```js
app.use(lusca.csrf({ koa: true }));
app.use(lusca.csp({ koa: true, /* ... */}));
app.use(lusca.xframe({ koa: true, value: 'SAMEORIGIN' }));
app.use(lusca.p3p({ koa: true, value: 'ABCDEF' }));
app.use(lusca.hsts({ koa: true, maxAge: 31536000 });
app.use(lusca.xssProtection({ koa: true });
```


## API
Expand Down
28 changes: 26 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,41 @@
*/
var lusca = module.exports = function (options) {
var headers = [];

var koa;
if (options) {
koa = !!options.koa;
Object.keys(lusca).forEach(function (key) {
var config = options[key];

if (config) {
if (koa) {
if ((key === 'csrf' || key === 'xssProtection') &&
config === true) {
// { csrf: true }
// { xssProtection: true }
config = {};
} else if ((key === 'xframe' || key === 'p3p') &&
typeof config === 'string') {
// { xframe: 'DENY' }
// { p3p: 'MY_P3P_VALUE' }
config = {
value: config
};
}
config.koa = koa;
}
headers.push(lusca[key](config));
}
});
}

if (koa) {
var compose = require('koa-compose');
var mw = compose(headers);
mw._name = 'lusca';
return mw;
}

return function lusca(req, res, next) {
var chain = next;

Expand All @@ -60,4 +84,4 @@ lusca.csp = require('./lib/csp');
lusca.hsts = require('./lib/hsts');
lusca.p3p = require('./lib/p3p');
lusca.xframe = require('./lib/xframes');
lusca.xssProtection = require('./lib/xssprotection');
lusca.xssProtection = require('./lib/xssprotection');
9 changes: 8 additions & 1 deletion lib/csp.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ module.exports = function (options) {
value += 'report-uri ' + reportUri;
}

if (options && options.koa) {
return function* csp(next) {
this.set(name, value);
yield* next;
};
}

return function csp(req, res, next) {
res.header(name, value);
next();
};
};
};
32 changes: 31 additions & 1 deletion lib/csrf.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,36 @@ module.exports = function (options) {
key = options.key || '_csrf';
impl = options.impl || token;

if (options.koa) {
// koa style middleware
return function* csrf(next) {
var method, token;

// Set the token
if (!this.locals) {
this.locals = {};
}
this.locals[key] = impl.create(this);

// Move along for safe verbs
method = this.method;

if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {
return yield* next;
}

// Validate token
token = this.request.body && this.request.body[key];

if (impl.validate(this, token)) {
yield* next;
} else {
this.throw(403, new Error('CSRF token mismatch'));
}
};
}

// express style middleware
return function csrf(req, res, next) {
var method, token;

Expand All @@ -42,4 +72,4 @@ module.exports = function (options) {
next(new Error('CSRF token mismatch'));
}
};
};
};
14 changes: 12 additions & 2 deletions lib/hsts.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* https://www.owasp.org/index.php/HTTP_Strict_Transport_Security
* @param {Object} options
* maxAge {Number} The max age of the header. Required.
* includeSubDomains {Boolean}
* includeSubDomains {Boolean}
*/
module.exports = function (options) {
var value;
Expand All @@ -16,11 +16,21 @@ module.exports = function (options) {
value = (options.maxAge !== undefined) ? 'max-age=' + options.maxAge : '';
value += (value && options.includeSubDomains) ? '; includeSubDomains' : '';

if (options.koa) {
return function* hsts(next) {
if (value) {
this.set('Strict-Transport-Security', value);
}

yield* next;
};
}

return function hsts(req, res, next) {
if (value) {
res.header('Strict-Transport-Security', value);
}

next();
};
};
19 changes: 17 additions & 2 deletions lib/p3p.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@
* @param {String} value The P3P header value.
*/
module.exports = function (value) {
var options = value;
if (typeof options === 'object') {
// { value: 'MY_P3P_VALUE' }
value = options.value;
}

if (options.koa) {
return function* p3p(next) {
if (value) {
this.set('P3P', value);
}
yield* next;
};
}

return function p3p(req, res, next) {
if (value) {
res.header('P3P', value);
}

next();
};
};
};
2 changes: 1 addition & 1 deletion lib/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ function validate(req, token) {
module.exports = {
create: create,
validate: validate
};
};
14 changes: 13 additions & 1 deletion lib/xframes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,20 @@
* @param {String} value The XFRAME header value, e.g. DENY, SAMEORIGIN.
*/
module.exports = function (value) {
var options = value;
if (typeof options === 'object') {
value = options.value;
}

if (options.koa) {
return function* xframe(next) {
this.set('X-FRAME-OPTIONS', value);
yield* next;
};
}

return function xframe(req, res, next) {
res.header('X-FRAME-OPTIONS', value);
next();
};
};
};
11 changes: 10 additions & 1 deletion lib/xssprotection.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ module.exports = function xssProtection(options) {
var enabled = (options.enabled !== undefined) ? +options.enabled : 1;
var mode = options.mode || 'block';

var value = enabled + '; mode=' + mode;

if (options.koa) {
return function* xssProtection(next) {
this.set('X-XSS-Protection', value);
yield* next;
};
}

return function xssProtection(req, res, next) {
res.header('X-XSS-Protection', enabled + '; mode=' + mode);
res.header('X-XSS-Protection', value);
next();
};
};
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"name": "lusca",
"version": "1.0.0",
"description": "Application security for express.",
"description": "Application security for express and koa.",
"main": "index",
"scripts": {
"test": "grunt test"
"test": "npm run test-express && npm run test-koa",
"test-express": "node --harmony ./node_modules/.bin/grunt test",
"test-koa": "APP_MODE=koa node --harmony ./node_modules/.bin/grunt test-koa"
},
"repository": {
"type": "git",
Expand All @@ -29,10 +31,14 @@
"engineStrict": true,
"devDependencies": {
"jshint": "*",
"supertest": "~0.5.1",
"supertest": "~0.13.0",
"express": "~3.4.8",
"koa": "~0.6.1",
"koa-middlewares": "~0.1.3",
"koa-compose": "~2.3.0",
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.7.0",
"grunt-cli": "~0.1.13",
"grunt-contrib-jshint": "~0.10.0",
"grunt-mocha-test": "~0.7.0"
}
}
1 change: 0 additions & 1 deletion test/csp.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,4 @@ describe('CSP', function () {
.expect('Content-Security-Policy', 'default-src *; ')
.expect(200, done);
});

});
Loading