Fix jwt-token
This commit is contained in:
663
server/tests/security/vulnerabilities.test.js
Normal file
663
server/tests/security/vulnerabilities.test.js
Normal file
@@ -0,0 +1,663 @@
|
||||
const { describe, it, beforeEach, afterEach, before, after } = require('mocha');
|
||||
const { expect } = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, createTestUser, createTestTenant, createTestDevice, generateTestToken } = require('../setup');
|
||||
|
||||
describe('Security Tests', () => {
|
||||
let models, sequelize;
|
||||
|
||||
before(async () => {
|
||||
({ models, sequelize } = await setupTestEnvironment());
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await teardownTestEnvironment();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await cleanDatabase();
|
||||
});
|
||||
|
||||
describe('Authentication Security', () => {
|
||||
it('should prevent JWT token manipulation', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
|
||||
const validToken = generateTestToken(user, tenant);
|
||||
const [header, payload, signature] = validToken.split('.');
|
||||
|
||||
// Test various token manipulation attempts
|
||||
const manipulationTests = [
|
||||
{
|
||||
name: 'Modified payload',
|
||||
token: header + '.' + Buffer.from(JSON.stringify({
|
||||
...JSON.parse(Buffer.from(payload, 'base64').toString()),
|
||||
role: 'admin' // Attempt privilege escalation
|
||||
})).toString('base64') + '.' + signature
|
||||
},
|
||||
{
|
||||
name: 'Modified signature',
|
||||
token: header + '.' + payload + '.' + 'tampered_signature'
|
||||
},
|
||||
{
|
||||
name: 'Wrong algorithm',
|
||||
token: jwt.sign({
|
||||
userId: user.id,
|
||||
tenantId: tenant.id
|
||||
}, 'secret', { algorithm: 'HS256' }) // Different algorithm
|
||||
},
|
||||
{
|
||||
name: 'Expired token',
|
||||
token: jwt.sign({
|
||||
userId: user.id,
|
||||
tenantId: tenant.id,
|
||||
exp: Math.floor(Date.now() / 1000) - 3600 // Expired 1 hour ago
|
||||
}, process.env.JWT_SECRET || 'test-secret')
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of manipulationTests) {
|
||||
try {
|
||||
const decoded = jwt.verify(test.token, process.env.JWT_SECRET || 'test-secret');
|
||||
// If we get here, the token was accepted when it shouldn't be
|
||||
if (test.name === 'Wrong algorithm') {
|
||||
// This might be valid depending on configuration
|
||||
continue;
|
||||
}
|
||||
expect.fail(`Token manipulation test "${test.name}" should have failed`);
|
||||
} catch (error) {
|
||||
// Expected behavior - token should be rejected
|
||||
expect(error.name).to.be.oneOf(['JsonWebTokenError', 'TokenExpiredError', 'NotBeforeError']);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should enforce tenant boundaries in JWT tokens', async () => {
|
||||
const tenant1 = await createTestTenant({ slug: 'tenant1' });
|
||||
const tenant2 = await createTestTenant({ slug: 'tenant2' });
|
||||
|
||||
const user1 = await createTestUser({ tenant_id: tenant1.id });
|
||||
const user2 = await createTestUser({ tenant_id: tenant2.id });
|
||||
|
||||
// Create device for tenant1
|
||||
const device1 = await createTestDevice({
|
||||
id: 111,
|
||||
tenant_id: tenant1.id,
|
||||
is_approved: true
|
||||
});
|
||||
|
||||
// User from tenant2 tries to access tenant1's device
|
||||
const crossTenantToken = generateTestToken(user2, tenant2);
|
||||
|
||||
// Simulate middleware that would check tenant access
|
||||
const checkTenantAccess = (token, targetTenantId) => {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'test-secret');
|
||||
return decoded.tenantId === targetTenantId;
|
||||
};
|
||||
|
||||
const hasAccess = checkTenantAccess(crossTenantToken, tenant1.id);
|
||||
expect(hasAccess).to.be.false;
|
||||
|
||||
// User from tenant1 should have access to their own tenant
|
||||
const validToken = generateTestToken(user1, tenant1);
|
||||
const hasValidAccess = checkTenantAccess(validToken, tenant1.id);
|
||||
expect(hasValidAccess).to.be.true;
|
||||
});
|
||||
|
||||
it('should handle brute force login attempts', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
username: 'brutetest',
|
||||
password: 'SecurePassword123!'
|
||||
});
|
||||
|
||||
// Mock rate limiting storage
|
||||
const rateLimitStore = new Map();
|
||||
|
||||
const checkRateLimit = (identifier, maxAttempts = 5, windowMs = 15 * 60 * 1000) => {
|
||||
const now = Date.now();
|
||||
const attempts = rateLimitStore.get(identifier) || { count: 0, resetTime: now + windowMs };
|
||||
|
||||
if (now > attempts.resetTime) {
|
||||
// Reset window
|
||||
attempts.count = 0;
|
||||
attempts.resetTime = now + windowMs;
|
||||
}
|
||||
|
||||
attempts.count++;
|
||||
rateLimitStore.set(identifier, attempts);
|
||||
|
||||
return attempts.count <= maxAttempts;
|
||||
};
|
||||
|
||||
// Simulate multiple failed login attempts
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const allowed = checkRateLimit('brutetest');
|
||||
|
||||
if (i < 5) {
|
||||
expect(allowed).to.be.true;
|
||||
} else {
|
||||
expect(allowed).to.be.false; // Should be blocked after 5 attempts
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Authorization Security', () => {
|
||||
it('should prevent privilege escalation attempts', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const regularUser = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
role: 'user'
|
||||
});
|
||||
const adminUser = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
role: 'admin'
|
||||
});
|
||||
|
||||
// Test role-based access control
|
||||
const checkPermission = (user, action) => {
|
||||
const permissions = {
|
||||
'user': ['view_detections', 'view_devices'],
|
||||
'admin': ['view_detections', 'view_devices', 'manage_devices', 'manage_users', 'view_system'],
|
||||
'system_admin': ['*'] // All permissions
|
||||
};
|
||||
|
||||
const userPermissions = permissions[user.role] || [];
|
||||
return userPermissions.includes(action) || userPermissions.includes('*');
|
||||
};
|
||||
|
||||
// Regular user should not have admin permissions
|
||||
expect(checkPermission(regularUser, 'manage_devices')).to.be.false;
|
||||
expect(checkPermission(regularUser, 'manage_users')).to.be.false;
|
||||
expect(checkPermission(regularUser, 'view_system')).to.be.false;
|
||||
|
||||
// But should have basic permissions
|
||||
expect(checkPermission(regularUser, 'view_detections')).to.be.true;
|
||||
expect(checkPermission(regularUser, 'view_devices')).to.be.true;
|
||||
|
||||
// Admin should have admin permissions
|
||||
expect(checkPermission(adminUser, 'manage_devices')).to.be.true;
|
||||
expect(checkPermission(adminUser, 'manage_users')).to.be.true;
|
||||
});
|
||||
|
||||
it('should enforce IP address restrictions', async () => {
|
||||
const tenant = await createTestTenant({
|
||||
ip_restrictions: '192.168.1.0/24,10.0.0.0/8'
|
||||
});
|
||||
|
||||
const checkIPRestriction = (clientIP, allowedRanges) => {
|
||||
if (!allowedRanges) return true;
|
||||
|
||||
const isIPInRange = (ip, range) => {
|
||||
if (range.includes('/')) {
|
||||
// CIDR notation
|
||||
const [network, prefixLength] = range.split('/');
|
||||
const prefix = parseInt(prefixLength);
|
||||
|
||||
// Simplified check for testing
|
||||
if (prefix === 24) {
|
||||
const networkPrefix = network.substring(0, network.lastIndexOf('.'));
|
||||
const ipPrefix = ip.substring(0, ip.lastIndexOf('.'));
|
||||
return networkPrefix === ipPrefix;
|
||||
}
|
||||
if (prefix === 8) {
|
||||
const networkPrefix = network.split('.')[0];
|
||||
const ipPrefix = ip.split('.')[0];
|
||||
return networkPrefix === ipPrefix;
|
||||
}
|
||||
}
|
||||
return ip === range;
|
||||
};
|
||||
|
||||
const ranges = allowedRanges.split(',');
|
||||
return ranges.some(range => isIPInRange(clientIP, range.trim()));
|
||||
};
|
||||
|
||||
const allowedIPs = [
|
||||
'192.168.1.100',
|
||||
'192.168.1.50',
|
||||
'10.0.0.15',
|
||||
'10.5.3.100'
|
||||
];
|
||||
|
||||
const blockedIPs = [
|
||||
'203.0.113.1', // External IP
|
||||
'172.16.0.1', // Different private range
|
||||
'192.168.2.100' // Wrong subnet
|
||||
];
|
||||
|
||||
allowedIPs.forEach(ip => {
|
||||
expect(checkIPRestriction(ip, tenant.ip_restrictions)).to.be.true;
|
||||
});
|
||||
|
||||
blockedIPs.forEach(ip => {
|
||||
expect(checkIPRestriction(ip, tenant.ip_restrictions)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
it('should prevent unauthorized data modification', async () => {
|
||||
const tenant1 = await createTestTenant();
|
||||
const tenant2 = await createTestTenant();
|
||||
|
||||
const user1 = await createTestUser({ tenant_id: tenant1.id, role: 'admin' });
|
||||
const user2 = await createTestUser({ tenant_id: tenant2.id, role: 'admin' });
|
||||
|
||||
const device1 = await createTestDevice({
|
||||
id: 123,
|
||||
tenant_id: tenant1.id
|
||||
});
|
||||
|
||||
// User2 attempts to modify device belonging to tenant1
|
||||
const unauthorizedUpdate = async () => {
|
||||
return await models.Device.update(
|
||||
{ name: 'Hacked Device' },
|
||||
{
|
||||
where: {
|
||||
id: device1.id,
|
||||
tenant_id: user2.tenant_id // Wrong tenant
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const result = await unauthorizedUpdate();
|
||||
expect(result[0]).to.equal(0); // No rows affected
|
||||
|
||||
// Verify device was not modified
|
||||
const device = await models.Device.findByPk(device1.id);
|
||||
expect(device.name).to.not.equal('Hacked Device');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input Validation Security', () => {
|
||||
it('should prevent SQL injection attempts', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
|
||||
// SQL injection payloads
|
||||
const injectionPayloads = [
|
||||
"'; DROP TABLE drone_detections; --",
|
||||
"' OR '1'='1",
|
||||
"1; DELETE FROM devices WHERE 1=1; --",
|
||||
"' UNION SELECT * FROM users --"
|
||||
];
|
||||
|
||||
for (const payload of injectionPayloads) {
|
||||
try {
|
||||
// Attempt to use payload in various contexts
|
||||
await models.DroneDetection.findAll({
|
||||
where: {
|
||||
tenant_id: tenant.id,
|
||||
// Using parameterized queries should prevent injection
|
||||
drone_id: payload
|
||||
}
|
||||
});
|
||||
|
||||
// The query should execute safely without SQL injection
|
||||
// (Sequelize uses parameterized queries by default)
|
||||
} catch (error) {
|
||||
// If there's an error, it should be a validation error, not a SQL error
|
||||
expect(error.name).to.not.include('SQL');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should validate and sanitize detection data', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const device = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
is_approved: true
|
||||
});
|
||||
|
||||
const maliciousInputs = [
|
||||
{
|
||||
name: 'XSS attempt in coordinates',
|
||||
data: {
|
||||
device_id: device.id,
|
||||
geo_lat: '<script>alert("xss")</script>',
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: Date.now(),
|
||||
drone_type: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Extremely large coordinates',
|
||||
data: {
|
||||
device_id: device.id,
|
||||
geo_lat: 999999.999999,
|
||||
geo_lon: -999999.999999,
|
||||
device_timestamp: Date.now(),
|
||||
drone_type: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Invalid data types',
|
||||
data: {
|
||||
device_id: device.id,
|
||||
geo_lat: null,
|
||||
geo_lon: undefined,
|
||||
device_timestamp: 'invalid_timestamp',
|
||||
drone_type: 'invalid_type'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Buffer overflow attempt',
|
||||
data: {
|
||||
device_id: device.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: Date.now(),
|
||||
drone_type: 2,
|
||||
additional_data: 'A'.repeat(10000) // Very long string
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of maliciousInputs) {
|
||||
try {
|
||||
await models.DroneDetection.create({
|
||||
...test.data,
|
||||
tenant_id: tenant.id
|
||||
});
|
||||
|
||||
// If creation succeeds, verify data was sanitized
|
||||
const detection = await models.DroneDetection.findOne({
|
||||
where: { device_id: device.id },
|
||||
order: [['id', 'DESC']]
|
||||
});
|
||||
|
||||
if (detection) {
|
||||
// Coordinates should be valid numbers
|
||||
if (detection.geo_lat !== null) {
|
||||
expect(detection.geo_lat).to.be.a('number');
|
||||
expect(detection.geo_lat).to.be.within(-90, 90);
|
||||
}
|
||||
if (detection.geo_lon !== null) {
|
||||
expect(detection.geo_lon).to.be.a('number');
|
||||
expect(detection.geo_lon).to.be.within(-180, 180);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Expected for invalid data - should be validation error
|
||||
expect(error.name).to.be.oneOf(['SequelizeValidationError', 'SequelizeDatabaseError']);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should prevent path traversal attacks', async () => {
|
||||
// Test potential file path manipulation
|
||||
const pathTraversalPayloads = [
|
||||
'../../../etc/passwd',
|
||||
'..\\..\\..\\windows\\system32\\config\\sam',
|
||||
'/etc/shadow',
|
||||
'C:\\Windows\\System32\\drivers\\etc\\hosts',
|
||||
'%2e%2e%2f%2e%2e%2f%2e%2e%2fbootini', // URL encoded
|
||||
'....//....//....//etc/passwd'
|
||||
];
|
||||
|
||||
pathTraversalPayloads.forEach(payload => {
|
||||
// Test file path validation function
|
||||
const isValidPath = (path) => {
|
||||
// Should reject paths with traversal attempts
|
||||
return !path.includes('..') &&
|
||||
!path.includes('%2e') &&
|
||||
!path.startsWith('/') &&
|
||||
!path.match(/^[a-zA-Z]:\\/);
|
||||
};
|
||||
|
||||
expect(isValidPath(payload)).to.be.false;
|
||||
});
|
||||
|
||||
// Valid paths should pass
|
||||
const validPaths = [
|
||||
'device_logs.txt',
|
||||
'reports/detection_summary.pdf',
|
||||
'data/export.csv'
|
||||
];
|
||||
|
||||
validPaths.forEach(path => {
|
||||
const isValidPath = (path) => {
|
||||
return !path.includes('..') &&
|
||||
!path.includes('%2e') &&
|
||||
!path.startsWith('/') &&
|
||||
!path.match(/^[a-zA-Z]:\\/);
|
||||
};
|
||||
|
||||
expect(isValidPath(path)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Data Protection Security', () => {
|
||||
it('should protect sensitive data in database', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
username: 'testuser',
|
||||
email: 'test@example.com',
|
||||
password: 'SecurePassword123!'
|
||||
});
|
||||
|
||||
// Verify password is hashed, not stored in plain text
|
||||
expect(user.password_hash).to.exist;
|
||||
expect(user.password_hash).to.not.equal('SecurePassword123!');
|
||||
expect(user.password_hash.length).to.be.greaterThan(20); // Hashed passwords are longer
|
||||
|
||||
// Verify sensitive fields are not exposed in JSON
|
||||
const userJSON = user.toJSON();
|
||||
expect(userJSON.password_hash).to.be.undefined; // Should be hidden
|
||||
expect(userJSON.username).to.exist; // Public fields should remain
|
||||
});
|
||||
|
||||
it('should enforce data retention policies', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
|
||||
// Create old detections (simulate 1 year old data)
|
||||
const oldDetections = [];
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
oldDetections.push({
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: oneYearAgo,
|
||||
drone_type: 2,
|
||||
rssi: -60,
|
||||
freq: 2400,
|
||||
drone_id: 1000 + i,
|
||||
threat_level: 'low',
|
||||
createdAt: oneYearAgo,
|
||||
updatedAt: oneYearAgo
|
||||
});
|
||||
}
|
||||
|
||||
await models.DroneDetection.bulkCreate(oldDetections);
|
||||
|
||||
// Create recent detections
|
||||
const recentDetections = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
recentDetections.push({
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(),
|
||||
drone_type: 2,
|
||||
rssi: -60,
|
||||
freq: 2400,
|
||||
drone_id: 2000 + i,
|
||||
threat_level: 'medium',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
await models.DroneDetection.bulkCreate(recentDetections);
|
||||
|
||||
// Simulate data retention cleanup (delete data older than 6 months)
|
||||
const sixMonthsAgo = new Date();
|
||||
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
|
||||
|
||||
const deleteResult = await models.DroneDetection.destroy({
|
||||
where: {
|
||||
tenant_id: tenant.id,
|
||||
createdAt: {
|
||||
[models.Sequelize.Op.lt]: sixMonthsAgo
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(deleteResult).to.equal(10); // Should delete old records
|
||||
|
||||
// Verify recent data remains
|
||||
const remainingDetections = await models.DroneDetection.findAll({
|
||||
where: { tenant_id: tenant.id }
|
||||
});
|
||||
|
||||
expect(remainingDetections).to.have.length(5);
|
||||
});
|
||||
|
||||
it('should anonymize exported data', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
const device = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686
|
||||
});
|
||||
|
||||
const detection = await models.DroneDetection.create({
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(),
|
||||
drone_type: 2,
|
||||
rssi: -60,
|
||||
freq: 2400,
|
||||
drone_id: 12345,
|
||||
threat_level: 'medium'
|
||||
});
|
||||
|
||||
// Function to anonymize data for export
|
||||
const anonymizeForExport = (data) => {
|
||||
return {
|
||||
...data,
|
||||
// Remove exact coordinates, use general area
|
||||
geo_lat: Math.round(data.geo_lat * 100) / 100, // Reduce precision
|
||||
geo_lon: Math.round(data.geo_lon * 100) / 100,
|
||||
// Remove device-specific identifiers
|
||||
device_id: null,
|
||||
// Hash drone ID instead of exposing it
|
||||
drone_id_hash: require('crypto').createHash('sha256').update(data.drone_id.toString()).digest('hex').substring(0, 8)
|
||||
};
|
||||
};
|
||||
|
||||
const anonymized = anonymizeForExport(detection.toJSON());
|
||||
|
||||
expect(anonymized.device_id).to.be.null;
|
||||
expect(anonymized.drone_id_hash).to.exist;
|
||||
expect(anonymized.drone_id_hash).to.not.equal(detection.drone_id);
|
||||
expect(anonymized.geo_lat).to.be.lessThan(detection.geo_lat + 0.005); // Reduced precision
|
||||
});
|
||||
});
|
||||
|
||||
describe('API Security', () => {
|
||||
it('should prevent API abuse and rate limiting bypass', async () => {
|
||||
const device = await createTestDevice({ is_approved: true });
|
||||
|
||||
// Simulate rate limiting for detection endpoint
|
||||
const requestCounts = new Map();
|
||||
const checkRateLimit = (deviceId, maxPerMinute = 60) => {
|
||||
const now = Date.now();
|
||||
const windowStart = Math.floor(now / 60000) * 60000; // 1-minute windows
|
||||
const key = `${deviceId}-${windowStart}`;
|
||||
|
||||
const count = requestCounts.get(key) || 0;
|
||||
requestCounts.set(key, count + 1);
|
||||
|
||||
return count < maxPerMinute;
|
||||
};
|
||||
|
||||
// Test normal usage - should be allowed
|
||||
for (let i = 0; i < 50; i++) {
|
||||
expect(checkRateLimit(device.id)).to.be.true;
|
||||
}
|
||||
|
||||
// Test excessive usage - should be blocked
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const allowed = checkRateLimit(device.id);
|
||||
if (i < 10) {
|
||||
expect(allowed).to.be.true;
|
||||
} else {
|
||||
expect(allowed).to.be.false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should validate API request size limits', async () => {
|
||||
const validateRequestSize = (data, maxSizeKB = 100) => {
|
||||
const dataSize = JSON.stringify(data).length;
|
||||
const maxSizeBytes = maxSizeKB * 1024;
|
||||
return dataSize <= maxSizeBytes;
|
||||
};
|
||||
|
||||
// Normal request should pass
|
||||
const normalRequest = {
|
||||
device_id: 123,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: Date.now(),
|
||||
drone_type: 2
|
||||
};
|
||||
|
||||
expect(validateRequestSize(normalRequest)).to.be.true;
|
||||
|
||||
// Oversized request should fail
|
||||
const oversizedRequest = {
|
||||
...normalRequest,
|
||||
malicious_payload: 'A'.repeat(200 * 1024) // 200KB of data
|
||||
};
|
||||
|
||||
expect(validateRequestSize(oversizedRequest)).to.be.false;
|
||||
});
|
||||
|
||||
it('should prevent CSRF attacks', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
|
||||
// Generate CSRF token
|
||||
const generateCSRFToken = (userId, secret = 'csrf-secret') => {
|
||||
return require('crypto')
|
||||
.createHmac('sha256', secret)
|
||||
.update(`${userId}-${Date.now()}`)
|
||||
.digest('hex');
|
||||
};
|
||||
|
||||
// Validate CSRF token
|
||||
const validateCSRFToken = (token, userId, secret = 'csrf-secret', maxAge = 3600000) => {
|
||||
try {
|
||||
// In a real implementation, you'd store token metadata
|
||||
// This is a simplified validation
|
||||
return token && token.length === 64; // Valid format
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const validToken = generateCSRFToken(user.id);
|
||||
expect(validateCSRFToken(validToken, user.id)).to.be.true;
|
||||
|
||||
// Invalid tokens should fail
|
||||
expect(validateCSRFToken('invalid_token', user.id)).to.be.false;
|
||||
expect(validateCSRFToken(null, user.id)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user