- Implemented SecurityTest to validate token generation, CSRF protection, input sanitization, and rate limiting. - Created UserModelTest to ensure correct database operations for user management, including creation, updating, banning, and fetching active users. - Developed ValidationTest to verify input validation and sanitization for user IDs, nicknames, messages, and API requests. - Introduced Security and Validation utility classes with methods for secure token generation, input sanitization, and comprehensive validation rules.
187 lines
6.2 KiB
PHP
187 lines
6.2 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Test Security utility functions
|
|
*/
|
|
class SecurityTest extends TestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
// Clear any previous session data
|
|
$_SESSION = [];
|
|
$_POST = [];
|
|
$_GET = [];
|
|
}
|
|
|
|
public function testGenerateSecureToken()
|
|
{
|
|
$token1 = Security::generateSecureToken(16);
|
|
$token2 = Security::generateSecureToken(16);
|
|
|
|
// Test length
|
|
$this->assertEquals(32, strlen($token1)); // 16 bytes = 32 hex chars
|
|
$this->assertEquals(32, strlen($token2));
|
|
|
|
// Test uniqueness
|
|
$this->assertNotEquals($token1, $token2);
|
|
|
|
// Test valid hex characters
|
|
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $token1);
|
|
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $token2);
|
|
}
|
|
|
|
public function testGenerateSecureUserId()
|
|
{
|
|
$userId1 = Security::generateSecureUserId();
|
|
$userId2 = Security::generateSecureUserId();
|
|
|
|
// Test format (32 char hex)
|
|
$this->assertEquals(32, strlen($userId1));
|
|
$this->assertEquals(32, strlen($userId2));
|
|
|
|
// Test uniqueness
|
|
$this->assertNotEquals($userId1, $userId2);
|
|
|
|
// Test valid characters
|
|
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $userId1);
|
|
$this->assertMatchesRegularExpression('/^[a-f0-9]+$/', $userId2);
|
|
}
|
|
|
|
public function testGetClientIP()
|
|
{
|
|
// Test with default server vars
|
|
$ip = Security::getClientIP();
|
|
$this->assertEquals('127.0.0.1', $ip);
|
|
|
|
// Test with forwarded headers
|
|
$_SERVER['HTTP_X_FORWARDED_FOR'] = '192.168.1.100, 10.0.0.1';
|
|
$ip = Security::getClientIP();
|
|
$this->assertEquals('192.168.1.100', $ip);
|
|
|
|
// Test with real IP header
|
|
$_SERVER['HTTP_X_REAL_IP'] = '203.0.113.1';
|
|
unset($_SERVER['HTTP_X_FORWARDED_FOR']);
|
|
$ip = Security::getClientIP();
|
|
$this->assertEquals('203.0.113.1', $ip);
|
|
}
|
|
|
|
public function testSanitizeInput()
|
|
{
|
|
// Test string sanitization
|
|
$input = '<script>alert("xss")</script>Hello World';
|
|
$result = Security::sanitizeInput($input, 'string');
|
|
$this->assertEquals('alert("xss")Hello World', $result);
|
|
|
|
// Test email sanitization
|
|
$email = 'test@example.com<script>evil</script>';
|
|
$result = Security::sanitizeInput($email, 'email');
|
|
$this->assertEquals('test@example.com<script>evil</script>', $result);
|
|
|
|
// Test URL sanitization
|
|
$url = 'http://example.com/path<script>evil</script>';
|
|
$result = Security::sanitizeInput($url, 'url');
|
|
$this->assertEquals('http://example.com/path', $result); // Scripts should be stripped
|
|
}
|
|
|
|
public function testValidateCSRFToken()
|
|
{
|
|
// Generate a token
|
|
$token = Security::generateCSRFToken();
|
|
$_SESSION['csrf_token'] = $token;
|
|
|
|
// Test valid token
|
|
$this->assertTrue(Security::validateCSRFToken($token));
|
|
|
|
// Test invalid token
|
|
$this->assertFalse(Security::validateCSRFToken('invalid_token'));
|
|
|
|
// Test missing token
|
|
$this->assertFalse(Security::validateCSRFToken(''));
|
|
}
|
|
|
|
public function testCheckRateLimit()
|
|
{
|
|
$ip = '192.168.1.100';
|
|
|
|
// First request should succeed
|
|
$result1 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
|
$this->assertTrue($result1);
|
|
|
|
// Second request should succeed
|
|
$result2 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
|
$this->assertTrue($result2);
|
|
|
|
// Third request should succeed
|
|
$result3 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
|
$this->assertTrue($result3);
|
|
|
|
// Fourth request should fail (over limit)
|
|
$result4 = Security::checkRateLimit($ip, 'test_action', 3, 60);
|
|
$this->assertFalse($result4);
|
|
}
|
|
|
|
public function testIsValidStreamUrl()
|
|
{
|
|
// Valid URLs
|
|
$this->assertTrue(Security::isValidStreamUrl('http://127.0.0.1:8080/stream'));
|
|
$this->assertTrue(Security::isValidStreamUrl('https://127.0.0.1:8080/stream'));
|
|
$this->assertTrue(Security::isValidStreamUrl('http://localhost:8080/stream'));
|
|
|
|
// Invalid URLs
|
|
$this->assertFalse(Security::isValidStreamUrl('http://evil.com/stream'));
|
|
$this->assertFalse(Security::isValidStreamUrl('http://192.168.1.1/stream'));
|
|
$this->assertFalse(Security::isValidStreamUrl('javascript:alert(1)'));
|
|
$this->assertFalse(Security::isValidStreamUrl(''));
|
|
}
|
|
|
|
public function testAdminAuthentication()
|
|
{
|
|
// Test without any auth setup
|
|
$this->assertFalse(Security::isAdminAuthenticated());
|
|
|
|
// Set up session auth
|
|
$_SESSION['admin_authenticated'] = true;
|
|
$_SESSION['admin_login_time'] = time();
|
|
|
|
$this->assertTrue(Security::isAdminAuthenticated());
|
|
}
|
|
|
|
public function testAuthenticateAdmin()
|
|
{
|
|
// This would need proper config setup for real testing
|
|
// For now, test that the method exists and handles failures
|
|
$result = Security::authenticateAdmin('invalid_user', 'invalid_pass');
|
|
$this->assertFalse($result);
|
|
}
|
|
|
|
public function testDetectSuspiciousActivity()
|
|
{
|
|
// Test with normal request
|
|
$warnings = Security::detectSuspiciousActivity();
|
|
$this->assertIsArray($warnings);
|
|
|
|
// Test with suspicious user agent
|
|
$_SERVER['HTTP_USER_AGENT'] = 'sqlmap';
|
|
$warnings = Security::detectSuspiciousActivity();
|
|
$this->assertContains('Suspicious user agent detected', $warnings);
|
|
|
|
// Reset
|
|
$_SERVER['HTTP_USER_AGENT'] = 'PHPUnit/Test';
|
|
}
|
|
|
|
public function testLogSecurityEvent()
|
|
{
|
|
// Start output buffering to capture logs
|
|
ob_start();
|
|
|
|
// Generate a security event
|
|
Security::logSecurityEvent('test_event', ['test_data' => 'value']);
|
|
|
|
// The actual logging happens in ErrorHandler, so we test that no exceptions are thrown
|
|
$this->assertTrue(true);
|
|
|
|
ob_end_clean();
|
|
}
|
|
}
|