mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00

* fix: ReDoS in ChatGPT Import * ci: should correctly process citations from real ChatGPT data * ci: Add ReDoS vulnerability test for processAssistantMessage * refactor: Update thread management and citation handling * refactor(validateImageRequest): robust validation * refactor(Prompt.js): update name search regex to escape special characters * refactor(Preset): exclude user from preset update to prevent mass assignment * refactor(files.js): Improve file deletion process * ci: updated validateImageRequest.spec.js * a11y: plugin pagination * refactor(CreatePromptForm.tsx): Improve input field styling * chore(Prompts): typing and accessibility * fix: prompt creation access role check * chore: remove duplicate jsdocs
125 lines
4.4 KiB
JavaScript
125 lines
4.4 KiB
JavaScript
const jwt = require('jsonwebtoken');
|
|
const validateImageRequest = require('~/server/middleware/validateImageRequest');
|
|
|
|
describe('validateImageRequest middleware', () => {
|
|
let req, res, next;
|
|
const validObjectId = '65cfb246f7ecadb8b1e8036b';
|
|
|
|
beforeEach(() => {
|
|
req = {
|
|
app: { locals: { secureImageLinks: true } },
|
|
headers: {},
|
|
originalUrl: '',
|
|
};
|
|
res = {
|
|
status: jest.fn().mockReturnThis(),
|
|
send: jest.fn(),
|
|
};
|
|
next = jest.fn();
|
|
process.env.JWT_REFRESH_SECRET = 'test-secret';
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test('should call next() if secureImageLinks is false', () => {
|
|
req.app.locals.secureImageLinks = false;
|
|
validateImageRequest(req, res, next);
|
|
expect(next).toHaveBeenCalled();
|
|
});
|
|
|
|
test('should return 401 if refresh token is not provided', () => {
|
|
validateImageRequest(req, res, next);
|
|
expect(res.status).toHaveBeenCalledWith(401);
|
|
expect(res.send).toHaveBeenCalledWith('Unauthorized');
|
|
});
|
|
|
|
test('should return 403 if refresh token is invalid', () => {
|
|
req.headers.cookie = 'refreshToken=invalid-token';
|
|
validateImageRequest(req, res, next);
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
|
expect(res.send).toHaveBeenCalledWith('Access Denied');
|
|
});
|
|
|
|
test('should return 403 if refresh token is expired', () => {
|
|
const expiredToken = jwt.sign(
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) - 3600 },
|
|
process.env.JWT_REFRESH_SECRET,
|
|
);
|
|
req.headers.cookie = `refreshToken=${expiredToken}`;
|
|
validateImageRequest(req, res, next);
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
|
expect(res.send).toHaveBeenCalledWith('Access Denied');
|
|
});
|
|
|
|
test('should call next() for valid image path', () => {
|
|
const validToken = jwt.sign(
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
|
process.env.JWT_REFRESH_SECRET,
|
|
);
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
|
req.originalUrl = `/images/${validObjectId}/example.jpg`;
|
|
validateImageRequest(req, res, next);
|
|
expect(next).toHaveBeenCalled();
|
|
});
|
|
|
|
test('should return 403 for invalid image path', () => {
|
|
const validToken = jwt.sign(
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
|
process.env.JWT_REFRESH_SECRET,
|
|
);
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
|
req.originalUrl = '/images/65cfb246f7ecadb8b1e8036c/example.jpg'; // Different ObjectId
|
|
validateImageRequest(req, res, next);
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
|
expect(res.send).toHaveBeenCalledWith('Access Denied');
|
|
});
|
|
|
|
test('should return 403 for invalid ObjectId format', () => {
|
|
const validToken = jwt.sign(
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
|
process.env.JWT_REFRESH_SECRET,
|
|
);
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
|
req.originalUrl = '/images/123/example.jpg'; // Invalid ObjectId
|
|
validateImageRequest(req, res, next);
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
|
expect(res.send).toHaveBeenCalledWith('Access Denied');
|
|
});
|
|
|
|
// File traversal tests
|
|
test('should prevent file traversal attempts', () => {
|
|
const validToken = jwt.sign(
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
|
process.env.JWT_REFRESH_SECRET,
|
|
);
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
|
|
|
const traversalAttempts = [
|
|
`/images/${validObjectId}/../../../etc/passwd`,
|
|
`/images/${validObjectId}/..%2F..%2F..%2Fetc%2Fpasswd`,
|
|
`/images/${validObjectId}/image.jpg/../../../etc/passwd`,
|
|
`/images/${validObjectId}/%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd`,
|
|
];
|
|
|
|
traversalAttempts.forEach((attempt) => {
|
|
req.originalUrl = attempt;
|
|
validateImageRequest(req, res, next);
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
|
expect(res.send).toHaveBeenCalledWith('Access Denied');
|
|
jest.clearAllMocks();
|
|
});
|
|
});
|
|
|
|
test('should handle URL encoded characters in valid paths', () => {
|
|
const validToken = jwt.sign(
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
|
process.env.JWT_REFRESH_SECRET,
|
|
);
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
|
req.originalUrl = `/images/${validObjectId}/image%20with%20spaces.jpg`;
|
|
validateImageRequest(req, res, next);
|
|
expect(next).toHaveBeenCalled();
|
|
});
|
|
});
|