2024-08-05 03:34:00 -04:00
|
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
|
const validateImageRequest = require('~/server/middleware/validateImageRequest');
|
|
|
|
|
|
|
|
|
|
describe('validateImageRequest middleware', () => {
|
|
|
|
|
let req, res, next;
|
2024-10-24 15:50:48 -04:00
|
|
|
const validObjectId = '65cfb246f7ecadb8b1e8036b';
|
2024-08-05 03:34:00 -04:00
|
|
|
|
|
|
|
|
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(
|
2024-10-24 15:50:48 -04:00
|
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) - 3600 },
|
2024-08-05 03:34:00 -04:00
|
|
|
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(
|
2024-10-24 15:50:48 -04:00
|
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
2024-08-05 03:34:00 -04:00
|
|
|
process.env.JWT_REFRESH_SECRET,
|
|
|
|
|
);
|
|
|
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
2024-10-24 15:50:48 -04:00
|
|
|
req.originalUrl = `/images/${validObjectId}/example.jpg`;
|
2024-08-05 03:34:00 -04:00
|
|
|
validateImageRequest(req, res, next);
|
|
|
|
|
expect(next).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should return 403 for invalid image path', () => {
|
|
|
|
|
const validToken = jwt.sign(
|
2024-10-24 15:50:48 -04:00
|
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
2024-08-05 03:34:00 -04:00
|
|
|
process.env.JWT_REFRESH_SECRET,
|
|
|
|
|
);
|
|
|
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
2024-10-24 15:50:48 -04:00
|
|
|
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
|
2024-08-05 03:34:00 -04:00
|
|
|
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(
|
2024-10-24 15:50:48 -04:00
|
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
2024-08-05 03:34:00 -04:00
|
|
|
process.env.JWT_REFRESH_SECRET,
|
|
|
|
|
);
|
|
|
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
|
|
|
|
|
|
|
|
|
const traversalAttempts = [
|
2024-10-24 15:50:48 -04:00
|
|
|
`/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`,
|
2024-08-05 03:34:00 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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(
|
2024-10-24 15:50:48 -04:00
|
|
|
{ id: validObjectId, exp: Math.floor(Date.now() / 1000) + 3600 },
|
2024-08-05 03:34:00 -04:00
|
|
|
process.env.JWT_REFRESH_SECRET,
|
|
|
|
|
);
|
|
|
|
|
req.headers.cookie = `refreshToken=${validToken}`;
|
2024-10-24 15:50:48 -04:00
|
|
|
req.originalUrl = `/images/${validObjectId}/image%20with%20spaces.jpg`;
|
2024-08-05 03:34:00 -04:00
|
|
|
validateImageRequest(req, res, next);
|
|
|
|
|
expect(next).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
});
|