Fix critical security vulnerabilities in location endpoints

SECURITY FIXES:
- Remove dangerous public DELETE /api/locations/:id endpoint
- Add rate limiting to POST /api/locations (10 requests per 15 minutes)
- Add input validation with length limits (500 chars address, 1000 chars description)
- Add suspicious activity logging for abuse detection
- Install express-rate-limit for protection against spam/DoS

CHANGES:
- Removed LocationDeleteRequest interface (no longer needed)
- Updated tests to expect new security validation behavior
- Added comprehensive tests for length validation
- Fixed test setup issue with undefined constants

Security Impact:
- CRITICAL: Prevents unauthorized deletion of location reports
- HIGH: Prevents spam submissions and DoS attacks
- MEDIUM: Prevents buffer overflow and injection attacks via oversized inputs

All 125 tests passing with new security validations.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Code 2025-07-05 21:54:23 -04:00
parent 22e4a9dc45
commit 88f7e72501
5 changed files with 86 additions and 34 deletions

View file

@ -313,15 +313,42 @@ describe('Public API Routes', () => {
});
describe('Request validation', () => {
it('should handle very long addresses', async () => {
const longAddress = 'A'.repeat(1000);
it('should reject overly long addresses for security', async () => {
const longAddress = 'A'.repeat(1000); // Over 500 character limit
const response = await request(app)
.post('/api/locations')
.send({ address: longAddress })
.expect(400);
expect(response.body).toHaveProperty('error');
expect(response.body.error).toBe('Address must be a string with maximum 500 characters');
});
it('should accept addresses within the limit', async () => {
const validLongAddress = 'A'.repeat(400); // Under 500 character limit
const response = await request(app)
.post('/api/locations')
.send({ address: validLongAddress })
.expect(200);
expect(response.body.address).toBe(longAddress);
expect(response.body.address).toBe(validLongAddress);
});
it('should reject overly long descriptions for security', async () => {
const longDescription = 'B'.repeat(1500); // Over 1000 character limit
const response = await request(app)
.post('/api/locations')
.send({
address: 'Test Address',
description: longDescription
})
.expect(400);
expect(response.body).toHaveProperty('error');
expect(response.body.error).toBe('Description must be a string with maximum 1000 characters');
});
it('should handle special characters in address', async () => {

View file

@ -70,14 +70,9 @@ export const createTestProfanityDatabase = (): Promise<Database> => {
});
};
// Cleanup function for tests
// Cleanup function for tests (in-memory databases don't need file cleanup)
export const cleanupTestDatabases = () => {
// Clean up any test database files
[TEST_DB_PATH, TEST_PROFANITY_DB_PATH].forEach(dbPath => {
if (fs.existsSync(dbPath)) {
fs.unlinkSync(dbPath);
}
});
// Using in-memory databases (:memory:) - no file cleanup needed
};
// Global test cleanup