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

Require at least 2 pins #5

Merged
merged 1 commit into from Jul 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 5 additions & 6 deletions index.js
@@ -1,6 +1,6 @@
var arrayWrap = require('arraywrap');

var badArgumentsError = new Error('hpkp must be called with a maxAge and at least one sha256.');
var badArgumentsError = new Error('hpkp must be called with a maxAge and at least two sha256 (one actually used and another kept as a backup).');

module.exports = function hpkp(passedOptions) {

Expand All @@ -17,20 +17,19 @@ module.exports = function hpkp(passedOptions) {

function parseOptions(options) {
if ((!options) ||
(options.maxage && options.maxAge) ||
(options.sha256 && options.sha256s)) {
(options.maxage && options.maxAge) ) {
throw badArgumentsError;
}

var maxAge = options.maxAge || options.maxage;
var sha256s = options.sha256 || options.sha256s;
if (!maxAge || !sha256s || (maxAge <= 0)) {
var sha256s = arrayWrap(options.sha256s);
if (!maxAge || (sha256s.length < 2) || (maxAge <= 0)) {
throw badArgumentsError;
}

return {
maxAge: maxAge,
sha256s: arrayWrap(sha256s),
sha256s: sha256s,
includeSubdomains: options.includeSubdomains,
reportUri: options.reportUri
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -3,7 +3,8 @@
"author": "Adam Baldwin <baldwin@andyet.net> (http://andyet.net/team/baldwin)",
"license": "MIT",
"contributors": [
"Evan Hahn <me@evanhahn.com> (http://evanhahn.com)"
"Evan Hahn <me@evanhahn.com> (http://evanhahn.com)",
"Tom Delmas <tdelmas@gmail.com> (https://tdelmas.ovh)"
],
"description": "HTTP Public Key Pinning (HPKP) middleware",
"version": "0.1.0",
Expand Down
80 changes: 25 additions & 55 deletions test/index.js
Expand Up @@ -17,71 +17,46 @@ describe('hpkp', function () {
return request(app).get('/');
}

it('sets header with one string key called "sha256"', function (done) {
test({ maxAge: 10000, sha256: 'abc123' })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=10', done);
});

it('sets header with one string key called "sha256s"', function (done) {
test({ maxAge: 10000, sha256s: 'abc123' })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=10', done);
});

it('sets header with a single-value array key called "sha256"', function (done) {
test({ maxAge: 10000, sha256: ['abc123'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=10', done);
});

it('sets header with a single-value array key called "sha256s"', function (done) {
test({ maxAge: 10000, sha256s: ['abc123'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=10', done);
});

it('sets header with a multi-value array key called "sha256"', function (done) {
test({ maxAge: 10000, sha256: ['abc123', 'xyz456'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=10', done);
});

it('sets header with a multi-value array key called "sha256s"', function (done) {
test({ maxAge: 10000, sha256s: ['abc123', 'xyz456'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=10', done);
});

it('allows lowercase "maxage"', function (done) {
test({ maxage: 10000, sha256: 'abc123' })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=10', done);
test({ maxage: 10000, sha256s: ['abc123', 'xyz456'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=10', done);
});

it('can include subdomains', function (done) {
test({ maxage: 10000, sha256: 'abc123', includeSubdomains: true })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=10; includeSubdomains', done);
test({ maxage: 10000, sha256s: ['abc123', 'xyz456'], includeSubdomains: true })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=10; includeSubdomains', done);
});

it('changes the header when using a report URI', function (done) {
test({ maxage: 10000, sha256: 'abc123', reportUri: 'http://example.com' })
.expect('Public-Key-Pins-Report-Only', 'pin-sha256="abc123"; max-age=10; report-uri="http://example.com"', done);
test({ maxage: 10000, sha256s: ['abc123', 'xyz456'], reportUri: 'http://example.com' })
.expect('Public-Key-Pins-Report-Only', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=10; report-uri="http://example.com"', done);
});

it('changes the header when using a report URI and includes subdomains', function (done) {
test({ maxage: 10000, sha256: 'abc123', reportUri: 'http://example.com', includeSubdomains: true })
.expect('Public-Key-Pins-Report-Only', 'pin-sha256="abc123"; max-age=10; includeSubdomains; report-uri="http://example.com"', done);
test({ maxage: 10000, sha256s: ['abc123', 'xyz456'], reportUri: 'http://example.com', includeSubdomains: true })
.expect('Public-Key-Pins-Report-Only', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=10; includeSubdomains; report-uri="http://example.com"', done);
});

it('rounds down to the nearest second', function (done) {
test({ maxAge: 1234, sha256: 'abc123' })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=1', done);
test({ maxAge: 1234, sha256s: ['abc123', 'xyz456'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=1', done);
});

it('rounds up to the nearest second', function (done) {
test({ maxAge: 1567, sha256: 'abc123' })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; max-age=2', done);
test({ maxAge: 1567, sha256s: ['abc123', 'xyz456'] })
.expect('Public-Key-Pins', 'pin-sha256="abc123"; pin-sha256="xyz456"; max-age=2', done);
});

});

it('names its function and middleware', function () {
assert.equal(hpkp.name, 'hpkp');
assert.equal(hpkp.name, hpkp({ maxAge: 10000, sha256: 'abc123' }).name);
assert.equal(hpkp.name, hpkp({ maxAge: 10000, sha256s: ['abc123', 'xyz456'] }).name);
});

describe('with improper input', function () {
Expand All @@ -102,39 +77,34 @@ describe('hpkp', function () {
});

it('fails if called without a max-age', function () {
[
{ sha256: 'abc123' },
{ sha256: ['abc123'] },
{ sha256s: 'abc123' },
{ sha256s: ['abc123'] }
].forEach(function (value) {
assert.throws(callWith(value));
});
assert.throws(callWith({ sha256s: ['abc123', 'xyz456'] }));
});

it('fails if called without SHAs', function () {
it('fails if called with less than 2 SHAs', function () {
[
{ maxAge: 10000 },
{ maxage: 10000 }
{ maxAge: 10000 , sha256s: 'abc123'},
{ maxAge: 10000 , sha256s: []},
{ maxAge: 10000 , sha256s: ['abc123']},
{ maxage: 10000 },
{ maxage: 10000 , sha256s: 'abc123'},
{ maxage: 10000 , sha256s: []},
{ maxage: 10000 , sha256s: ['abc123']},
].forEach(function (value) {
assert.throws(callWith(value));
});
});

it('fails if called with a zero maxAge', function () {
assert.throws(callWith({ maxAge: 0, sha256: 'abc123' }));
assert.throws(callWith({ maxAge: 0, sha256s: ['abc123', 'xyz456'] }));
});

it('fails if called with a negative maxAge', function () {
assert.throws(callWith({ maxAge: -1000, sha256: 'abc123' }));
assert.throws(callWith({ maxAge: -1000, sha256s: ['abc123', 'xyz456'] }));
});

it('fails if called with both types of maxAge argument', function () {
assert.throws(callWith({ maxAge: 1000, maxage: 1000, sha256: 'abc123' }));
});

it('fails if called with both types of SHA argument', function () {
assert.throws(callWith({ maxAge: 1000, sha256: 'abc123', sha256s: 'xyz456' }));
assert.throws(callWith({ maxAge: 1000, maxage: 1000, sha256s: ['abc123', 'xyz456'] }));
});

});
Expand Down