Add comprehensive unit tests for Security, UserModel, and Validation utilities
- 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.
This commit is contained in:
parent
5692874b10
commit
41cd7a4fd8
32 changed files with 5796 additions and 368 deletions
187
tests/unit/SecurityTest.php
Normal file
187
tests/unit/SecurityTest.php
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue