diff --git a/index.js b/index.js index dd6d479..1c934da 100644 --- a/index.js +++ b/index.js @@ -109,7 +109,11 @@ function JSONCookies (obj) { key = cookies[i] val = JSONCookie(obj[key]) - if (val) { + // Boolean false is reserved: signedCookies() uses it to mark a cookie whose + // signature did not verify, so writing a parsed "j:false" here would make an + // authentic value indistinguishable from a tampered one (#168). Left as the + // raw string until that representation is settled. + if (val !== undefined && val !== false) { obj[key] = val } } diff --git a/test/cookieParser.js b/test/cookieParser.js index 6031823..6226f59 100644 --- a/test/cookieParser.js +++ b/test/cookieParser.js @@ -81,6 +81,23 @@ describe('cookieParser()', function () { .expect(200, '{"foo":"foobarbaz"}', done) }) + it('should keep an authentic JSON false distinct from a tampered cookie', function (done) { + var signed = signature.sign('j:false', 'keyboard cat') + request(createServer('keyboard cat')) + .get('/signed') + .set('Cookie', 'flag=s:' + signed) + .expect(200, '{"flag":"j:false"}', done) + }) + + it('should report a tampered cookie as false', function (done) { + var signed = signature.sign('j:false', 'keyboard cat') + var tampered = signed.slice(0, -1) + (signed.slice(-1) === 'a' ? 'b' : 'a') + request(createServer('keyboard cat')) + .get('/signed') + .set('Cookie', 'flag=s:' + tampered) + .expect(200, '{"flag":false}', done) + }) + it('should remove the signed value from req.cookies', function (done) { request(createServer('keyboard cat')) .get('/') @@ -162,6 +179,24 @@ describe('cookieParser.JSONCookie(str)', function () { }) }) +describe('cookieParser.JSONCookies(obj)', function () { + it('should parse falsy JSON cookie values', function () { + assert.deepEqual(cookieParser.JSONCookies({ + z: 'j:0', + e: 'j:""', + n: 'j:null' + }), { z: 0, e: '', n: null }) + }) + + it('should leave a JSON false as its raw string, so it stays distinct from the signature-failure marker', function () { + assert.deepEqual(cookieParser.JSONCookies({ f: 'j:false' }), { f: 'j:false' }) + }) + + it('should leave non-JSON cookie values untouched', function () { + assert.deepEqual(cookieParser.JSONCookies({ a: 'bar', b: 'j:{foo:"bar"}' }), { a: 'bar', b: 'j:{foo:"bar"}' }) + }) +}) + describe('cookieParser.signedCookie(str, secret)', function () { it('should return undefined for non-string arguments', function () { assert.strictEqual(cookieParser.signedCookie(undefined, 'keyboard cat'), undefined)