text changes and remove infra folder
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
// Set CSRF_SECRET before requiring the middleware
|
||||
process.env.CSRF_SECRET = 'test-csrf-secret-that-is-at-least-32-chars-long';
|
||||
process.env.CSRF_SECRET = "test-csrf-secret";
|
||||
|
||||
const mockTokensInstance = {
|
||||
secretSync: jest.fn().mockReturnValue(process.env.CSRF_SECRET),
|
||||
create: jest.fn().mockReturnValue('mock-token-123'),
|
||||
verify: jest.fn().mockReturnValue(true)
|
||||
create: jest.fn().mockReturnValue("mock-token-123"),
|
||||
verify: jest.fn().mockReturnValue(true),
|
||||
};
|
||||
|
||||
jest.mock('csrf', () => {
|
||||
jest.mock("csrf", () => {
|
||||
return jest.fn().mockImplementation(() => mockTokensInstance);
|
||||
});
|
||||
|
||||
jest.mock('cookie-parser', () => {
|
||||
jest.mock("cookie-parser", () => {
|
||||
return jest.fn().mockReturnValue((req, res, next) => next());
|
||||
});
|
||||
|
||||
jest.mock('../../../utils/logger', () => ({
|
||||
jest.mock("../../../utils/logger", () => ({
|
||||
error: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
@@ -26,18 +26,22 @@ jest.mock('../../../utils/logger', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
const { csrfProtection, generateCSRFToken, getCSRFToken } = require('../../../middleware/csrf');
|
||||
const {
|
||||
csrfProtection,
|
||||
generateCSRFToken,
|
||||
getCSRFToken,
|
||||
} = require("../../../middleware/csrf");
|
||||
|
||||
describe('CSRF Middleware', () => {
|
||||
describe("CSRF Middleware", () => {
|
||||
let req, res, next;
|
||||
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {},
|
||||
body: {},
|
||||
query: {},
|
||||
cookies: {}
|
||||
cookies: {},
|
||||
};
|
||||
res = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
@@ -45,16 +49,16 @@ describe('CSRF Middleware', () => {
|
||||
send: jest.fn(),
|
||||
cookie: jest.fn(),
|
||||
set: jest.fn(),
|
||||
locals: {}
|
||||
locals: {},
|
||||
};
|
||||
next = jest.fn();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('csrfProtection', () => {
|
||||
describe('Safe methods', () => {
|
||||
it('should skip CSRF protection for GET requests', () => {
|
||||
req.method = 'GET';
|
||||
describe("csrfProtection", () => {
|
||||
describe("Safe methods", () => {
|
||||
it("should skip CSRF protection for GET requests", () => {
|
||||
req.method = "GET";
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
@@ -62,8 +66,8 @@ describe('CSRF Middleware', () => {
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip CSRF protection for HEAD requests', () => {
|
||||
req.method = 'HEAD';
|
||||
it("should skip CSRF protection for HEAD requests", () => {
|
||||
req.method = "HEAD";
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
@@ -71,8 +75,8 @@ describe('CSRF Middleware', () => {
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip CSRF protection for OPTIONS requests', () => {
|
||||
req.method = 'OPTIONS';
|
||||
it("should skip CSRF protection for OPTIONS requests", () => {
|
||||
req.method = "OPTIONS";
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
@@ -81,389 +85,427 @@ describe('CSRF Middleware', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Token validation', () => {
|
||||
describe("Token validation", () => {
|
||||
beforeEach(() => {
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
});
|
||||
|
||||
it('should validate token from x-csrf-token header', () => {
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
it("should validate token from x-csrf-token header", () => {
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should validate token from request body', () => {
|
||||
req.body.csrfToken = 'mock-token-123';
|
||||
it("should validate token from request body", () => {
|
||||
req.body.csrfToken = "mock-token-123";
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should prefer header token over body token', () => {
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.body.csrfToken = 'different-token';
|
||||
it("should prefer header token over body token", () => {
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.body.csrfToken = "different-token";
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Missing tokens', () => {
|
||||
it('should return 403 when no token provided', () => {
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
describe("Missing tokens", () => {
|
||||
it("should return 403 when no token provided", () => {
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 403 when no cookie token provided', () => {
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
it("should return 403 when no cookie token provided", () => {
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = {};
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 403 when cookies object is missing', () => {
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
it("should return 403 when cookies object is missing", () => {
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = undefined;
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 403 when both tokens are missing', () => {
|
||||
it("should return 403 when both tokens are missing", () => {
|
||||
req.cookies = {};
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Token mismatch', () => {
|
||||
it('should return 403 when tokens do not match', () => {
|
||||
req.headers['x-csrf-token'] = 'token-from-header';
|
||||
req.cookies = { 'csrf-token': 'token-from-cookie' };
|
||||
describe("Token mismatch", () => {
|
||||
it("should return 403 when tokens do not match", () => {
|
||||
req.headers["x-csrf-token"] = "token-from-header";
|
||||
req.cookies = { "csrf-token": "token-from-cookie" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 403 when header token is empty but cookie exists', () => {
|
||||
req.headers['x-csrf-token'] = '';
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
it("should return 403 when header token is empty but cookie exists", () => {
|
||||
req.headers["x-csrf-token"] = "";
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 403 when cookie token is empty but header exists', () => {
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.cookies = { 'csrf-token': '' };
|
||||
it("should return 403 when cookie token is empty but header exists", () => {
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = { "csrf-token": "" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_MISMATCH'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_MISMATCH",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Token verification', () => {
|
||||
describe("Token verification", () => {
|
||||
beforeEach(() => {
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
});
|
||||
|
||||
it('should return 403 when token verification fails', () => {
|
||||
it("should return 403 when token verification fails", () => {
|
||||
mockTokensInstance.verify.mockReturnValue(false);
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid CSRF token',
|
||||
code: 'CSRF_TOKEN_INVALID'
|
||||
error: "Invalid CSRF token",
|
||||
code: "CSRF_TOKEN_INVALID",
|
||||
});
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call next when token verification succeeds', () => {
|
||||
it("should call next when token verification succeeds", () => {
|
||||
mockTokensInstance.verify.mockReturnValue(true);
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('should handle case-insensitive HTTP methods', () => {
|
||||
req.method = 'post';
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
describe("Edge cases", () => {
|
||||
it("should handle case-insensitive HTTP methods", () => {
|
||||
req.method = "post";
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle PUT requests', () => {
|
||||
req.method = 'PUT';
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
it("should handle PUT requests", () => {
|
||||
req.method = "PUT";
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle DELETE requests', () => {
|
||||
req.method = 'DELETE';
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
it("should handle DELETE requests", () => {
|
||||
req.method = "DELETE";
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle PATCH requests', () => {
|
||||
req.method = 'PATCH';
|
||||
req.headers['x-csrf-token'] = 'mock-token-123';
|
||||
req.cookies = { 'csrf-token': 'mock-token-123' };
|
||||
it("should handle PATCH requests", () => {
|
||||
req.method = "PATCH";
|
||||
req.headers["x-csrf-token"] = "mock-token-123";
|
||||
req.cookies = { "csrf-token": "mock-token-123" };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(process.env.CSRF_SECRET, 'mock-token-123');
|
||||
expect(mockTokensInstance.verify).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
"mock-token-123",
|
||||
);
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateCSRFToken', () => {
|
||||
it('should generate token and set cookie with proper options', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
describe("generateCSRFToken", () => {
|
||||
it("should generate token and set cookie with proper options", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(mockTokensInstance.create).toHaveBeenCalledWith(process.env.CSRF_SECRET);
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(mockTokensInstance.create).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
);
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set secure flag to false in dev environment', () => {
|
||||
process.env.NODE_ENV = 'dev';
|
||||
it("should set secure flag to false in dev environment", () => {
|
||||
process.env.NODE_ENV = "dev";
|
||||
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set secure flag to true in non-dev environment', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
it("should set secure flag to true in non-dev environment", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set token in response header', () => {
|
||||
it("should set token in response header", () => {
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(res.set).toHaveBeenCalledWith('X-CSRF-Token', 'mock-token-123');
|
||||
expect(res.set).toHaveBeenCalledWith("X-CSRF-Token", "mock-token-123");
|
||||
});
|
||||
|
||||
it('should make token available in res.locals', () => {
|
||||
it("should make token available in res.locals", () => {
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(res.locals.csrfToken).toBe('mock-token-123');
|
||||
expect(res.locals.csrfToken).toBe("mock-token-123");
|
||||
});
|
||||
|
||||
it('should call next after setting up token', () => {
|
||||
it("should call next after setting up token", () => {
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle test environment', () => {
|
||||
process.env.NODE_ENV = 'test';
|
||||
it("should handle test environment", () => {
|
||||
process.env.NODE_ENV = "test";
|
||||
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle undefined NODE_ENV', () => {
|
||||
it("should handle undefined NODE_ENV", () => {
|
||||
delete process.env.NODE_ENV;
|
||||
|
||||
generateCSRFToken(req, res, next);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCSRFToken', () => {
|
||||
it('should generate token and return it in response', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
describe("getCSRFToken", () => {
|
||||
it("should generate token and return it in response", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
getCSRFToken(req, res);
|
||||
|
||||
expect(mockTokensInstance.create).toHaveBeenCalledWith(process.env.CSRF_SECRET);
|
||||
expect(mockTokensInstance.create).toHaveBeenCalledWith(
|
||||
process.env.CSRF_SECRET,
|
||||
);
|
||||
expect(res.status).toHaveBeenCalledWith(204);
|
||||
expect(res.send).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set token in cookie with proper options', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
it("should set token in cookie with proper options", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
getCSRFToken(req, res);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set secure flag to false in dev environment', () => {
|
||||
process.env.NODE_ENV = 'dev';
|
||||
it("should set secure flag to false in dev environment", () => {
|
||||
process.env.NODE_ENV = "dev";
|
||||
|
||||
getCSRFToken(req, res);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set secure flag to true in production environment', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
it("should set secure flag to true in production environment", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
getCSRFToken(req, res);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle test environment', () => {
|
||||
process.env.NODE_ENV = 'test';
|
||||
it("should handle test environment", () => {
|
||||
process.env.NODE_ENV = "test";
|
||||
|
||||
getCSRFToken(req, res);
|
||||
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'mock-token-123', {
|
||||
expect(res.cookie).toHaveBeenCalledWith("csrf-token", "mock-token-123", {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 1000
|
||||
sameSite: "strict",
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate new token each time', () => {
|
||||
it("should generate new token each time", () => {
|
||||
mockTokensInstance.create
|
||||
.mockReturnValueOnce('token-1')
|
||||
.mockReturnValueOnce('token-2');
|
||||
.mockReturnValueOnce("token-1")
|
||||
.mockReturnValueOnce("token-2");
|
||||
|
||||
getCSRFToken(req, res);
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'token-1', expect.any(Object));
|
||||
expect(res.set).toHaveBeenCalledWith('X-CSRF-Token', 'token-1');
|
||||
expect(res.cookie).toHaveBeenCalledWith(
|
||||
"csrf-token",
|
||||
"token-1",
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(res.set).toHaveBeenCalledWith("X-CSRF-Token", "token-1");
|
||||
|
||||
jest.clearAllMocks();
|
||||
getCSRFToken(req, res);
|
||||
expect(res.cookie).toHaveBeenCalledWith('csrf-token', 'token-2', expect.any(Object));
|
||||
expect(res.set).toHaveBeenCalledWith('X-CSRF-Token', 'token-2');
|
||||
expect(res.cookie).toHaveBeenCalledWith(
|
||||
"csrf-token",
|
||||
"token-2",
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(res.set).toHaveBeenCalledWith("X-CSRF-Token", "token-2");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Integration scenarios', () => {
|
||||
it('should handle complete CSRF flow', () => {
|
||||
describe("Integration scenarios", () => {
|
||||
it("should handle complete CSRF flow", () => {
|
||||
// First, generate a token
|
||||
generateCSRFToken(req, res, next);
|
||||
const generatedToken = res.locals.csrfToken;
|
||||
@@ -472,9 +514,9 @@ describe('CSRF Middleware', () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Now test protection with the generated token
|
||||
req.method = 'POST';
|
||||
req.headers['x-csrf-token'] = generatedToken;
|
||||
req.cookies = { 'csrf-token': generatedToken };
|
||||
req.method = "POST";
|
||||
req.headers["x-csrf-token"] = generatedToken;
|
||||
req.cookies = { "csrf-token": generatedToken };
|
||||
|
||||
csrfProtection(req, res, next);
|
||||
|
||||
@@ -482,18 +524,18 @@ describe('CSRF Middleware', () => {
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle token generation endpoint flow', () => {
|
||||
it("should handle token generation endpoint flow", () => {
|
||||
getCSRFToken(req, res);
|
||||
|
||||
const cookieCall = res.cookie.mock.calls[0];
|
||||
const headerCall = res.set.mock.calls[0];
|
||||
|
||||
expect(cookieCall[0]).toBe('csrf-token');
|
||||
expect(cookieCall[1]).toBe('mock-token-123');
|
||||
expect(headerCall[0]).toBe('X-CSRF-Token');
|
||||
expect(headerCall[1]).toBe('mock-token-123');
|
||||
expect(cookieCall[0]).toBe("csrf-token");
|
||||
expect(cookieCall[1]).toBe("mock-token-123");
|
||||
expect(headerCall[0]).toBe("X-CSRF-Token");
|
||||
expect(headerCall[1]).toBe("mock-token-123");
|
||||
expect(res.status).toHaveBeenCalledWith(204);
|
||||
expect(res.send).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user