feat: add security hardening for reservation APIs

- Add rate limiting (5/hr public, 100/min admin endpoints)
- Add honeypot bot protection on public POST
- Add audit logging for status changes and deletions
- Add input validation (email, phone, date, length limits)
- Fix missing auth on private event GET endpoints
- Add audit_log table to schema
- Fix failing tests (auth.test, rooms-route.test)
This commit is contained in:
2026-07-02 20:56:36 -05:00
parent 0ca4f961f6
commit dc0c5fd289
8 changed files with 541 additions and 32 deletions
+5 -1
View File
@@ -31,13 +31,17 @@ describe('auth helpers', () => {
it('authenticates a user with the stored password hash', async () => {
const passwordHash = await hashPassword('secret-password');
query.mockResolvedValueOnce({
rows: [{ id: 2, username: 'bob', password_hash: passwordHash, role: 'admin' }],
rows: [{ id: 2, username: 'bob', password_hash: passwordHash, role: 'admin', first_name: null, last_name: null, preferred_language: 'es', terms_accepted_at: null }],
});
await expect(authenticateUser('bob', 'secret-password')).resolves.toEqual({
id: 2,
username: 'bob',
role: 'admin',
first_name: null,
last_name: null,
preferred_language: 'es',
terms_accepted_at: null,
});
});
+7 -2
View File
@@ -20,13 +20,18 @@ describe('/api/rooms', () => {
});
it('returns active rooms', async () => {
// Mock rooms query
query.mockResolvedValueOnce({ rows: [{ id: 1, name: 'Suite', active: true }] });
// Mock amenities query
query.mockResolvedValueOnce({ rows: [] });
// Mock images query
query.mockResolvedValueOnce({ rows: [] });
const response = await GET();
const body = await response.json();
expect(response.status).toBe(200);
expect(body).toEqual({ data: [{ id: 1, name: 'Suite', active: true }] });
expect(body).toEqual({ data: [{ id: 1, name: 'Suite', active: true, photos: [], amenities: [] }] });
});
it('creates a room for admins', async () => {
@@ -52,6 +57,6 @@ describe('/api/rooms', () => {
expect(requireRole).toHaveBeenCalledWith('admin');
expect(response.status).toBe(200);
expect(body).toEqual({ data: { id: 7, name: 'Garden Room', price: '120.00' } });
expect(body).toEqual({ data: { id: 7, name: 'Garden Room', price: '120.00', photos: [], amenity_ids: [] } });
});
});