From 0495ce5b534c4550f25228821db8098293439f2f Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Tue, 21 May 2024 18:24:15 +0200 Subject: [PATCH] Merge pull request from GHSA-pj27-2xvp-4qxg * fix: Use original expiration date when evaluating restored session. * fix: Also serialize new field. --- lib/cookie.js | 5 ++++ lib/fastifySession.js | 4 ++- test/expiration.test.js | 63 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 test/expiration.test.js diff --git a/lib/cookie.js b/lib/cookie.js index 3de8c73..ebfa1c5 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -11,6 +11,10 @@ module.exports = class Cookie { this.partitioned = cookie.partitioned this._expires = null + if(cookie.expires) { + this.originalExpires = new Date(cookie.expires) + } + if (originalMaxAge) { this.maxAge = originalMaxAge } else if (cookie.expires) { @@ -56,6 +60,7 @@ module.exports = class Cookie { return { expires: this._expires, originalMaxAge: this.originalMaxAge, + originalExpires: this.originalExpires, sameSite: this.sameSite, secure: this.secure, path: this.path, diff --git a/lib/fastifySession.js b/lib/fastifySession.js index 9e43429..bc8fe20 100644 --- a/lib/fastifySession.js +++ b/lib/fastifySession.js @@ -92,7 +92,9 @@ function fastifySession (fastify, options, next) { decryptedSessionId ) - if (restoredSession.cookie.expires && restoredSession.cookie.expires.getTime() <= Date.now()) { + const expiration = restoredSession.cookie.originalExpires || restoredSession.cookie.expires + + if (expiration && expiration.getTime() <= Date.now()) { restoredSession.destroy(err => { if (err) { done(err) diff --git a/test/expiration.test.js b/test/expiration.test.js new file mode 100644 index 0000000..e7eb321 --- /dev/null +++ b/test/expiration.test.js @@ -0,0 +1,63 @@ +"use strict"; + +const test = require("tap").test; +const { buildFastify, DEFAULT_SECRET } = require("./util"); +const { setTimeout } = require("node:timers/promises"); + +test("sessions should be deleted if expired", async (t) => { + t.plan(5); + + const sessions = {}; + const options = { + secret: DEFAULT_SECRET, + store: { + get(id, cb) { + t.pass("session was restored"); + cb(null, sessions[id]); + }, + set(id, session, cb) { + sessions[id] = session; + cb(); + }, + destroy(id, cb) { + t.pass("expired session is destroyed"); + cb(); + }, + }, + cookie: { maxAge: 1000, secure: false }, + }; + + const fastify = await buildFastify((request, reply) => { + reply.send(200); + }, options); + t.teardown(() => { + fastify.close(); + }); + + let response; + response = await fastify.inject({ + url: "/", + }); + + const initialSession = response.headers["set-cookie"] + .split(" ")[0] + .replace(";", ""); + t.ok(initialSession.startsWith("sessionId=")); + + // Wait for the cookie to expire + await setTimeout(2000); + + response = await fastify.inject({ + url: "/", + headers: { + Cookie: initialSession, + }, + }); + + const endingSession = response.headers["set-cookie"] + .split(" ")[0] + .replace(";", ""); + t.ok(endingSession.startsWith("sessionId=")); + + t.not(initialSession, endingSession); +});