security = VSecurity::getInstance();
// Clear any existing session data
if (isset($_SESSION)) {
$_SESSION = [];
}
// Clear superglobals for clean testing
$_GET = [];
$_POST = [];
}
protected function tearDown(): void
{
// Clean up after each test
if (isset($_SESSION)) {
$_SESSION = [];
}
$_GET = [];
$_POST = [];
}
/**
* Test input validation with various data types
*/
public function testInputValidationWithEdgeCases()
{
// Test integer validation
$this->assertEquals(123, VSecurity::validateInput('123', 'int'));
$this->assertEquals(0, VSecurity::validateInput('0', 'int'));
$this->assertEquals(-123, VSecurity::validateInput('-123', 'int'));
$this->assertNull(VSecurity::validateInput('abc', 'int'));
$this->assertNull(VSecurity::validateInput('123.45', 'int'));
$this->assertNull(VSecurity::validateInput('999999999999999999999', 'int'));
// Test integer with min/max constraints
$this->assertEquals(50, VSecurity::validateInput('50', 'int', null, ['min' => 10, 'max' => 100]));
$this->assertNull(VSecurity::validateInput('5', 'int', null, ['min' => 10, 'max' => 100]));
$this->assertNull(VSecurity::validateInput('150', 'int', null, ['min' => 10, 'max' => 100]));
// Test float validation
$this->assertEquals(123.45, VSecurity::validateInput('123.45', 'float'));
$this->assertEquals(0.0, VSecurity::validateInput('0.0', 'float'));
$this->assertNull(VSecurity::validateInput('abc', 'float'));
// Test email validation
$this->assertEquals('test@example.com', VSecurity::validateInput('test@example.com', 'email'));
$this->assertEquals('user+tag@domain.co.uk', VSecurity::validateInput('user+tag@domain.co.uk', 'email'));
$this->assertNull(VSecurity::validateInput('invalid-email', 'email'));
$this->assertNull(VSecurity::validateInput('test@', 'email'));
$this->assertNull(VSecurity::validateInput('@example.com', 'email'));
// Test URL validation
$this->assertEquals('https://example.com', VSecurity::validateInput('https://example.com', 'url'));
$this->assertEquals('http://localhost:8080/path', VSecurity::validateInput('http://localhost:8080/path', 'url'));
$this->assertNull(VSecurity::validateInput('not-a-url', 'url'));
$this->assertNull(VSecurity::validateInput('ftp://invalid', 'url'));
// Test alpha validation
$this->assertEquals('abcDEF', VSecurity::validateInput('abcDEF', 'alpha'));
$this->assertEquals('test', VSecurity::validateInput('test123!@#', 'alpha'));
$this->assertNull(VSecurity::validateInput('123', 'alpha'));
// Test alphanum validation
$this->assertEquals('abc123', VSecurity::validateInput('abc123', 'alphanum'));
$this->assertEquals('test123', VSecurity::validateInput('test123!@#', 'alphanum'));
$this->assertNull(VSecurity::validateInput('!@#', 'alphanum'));
// Test slug validation
$this->assertEquals('test-slug_123', VSecurity::validateInput('test-slug_123', 'slug'));
$this->assertEquals('test-slug', VSecurity::validateInput('test-slug!@#', 'slug'));
// Test filename validation
$this->assertEquals('test.txt', VSecurity::validateInput('test.txt', 'filename'));
$this->assertEquals('file_name.pdf', VSecurity::validateInput('file_name.pdf', 'filename'));
$this->assertEquals('test.txt', VSecurity::validateInput('test/path.txt', 'filename'));
// Test boolean validation
$this->assertTrue(VSecurity::validateInput('true', 'boolean'));
$this->assertTrue(VSecurity::validateInput('1', 'boolean'));
$this->assertTrue(VSecurity::validateInput('yes', 'boolean'));
$this->assertFalse(VSecurity::validateInput('false', 'boolean'));
$this->assertFalse(VSecurity::validateInput('0', 'boolean'));
$this->assertFalse(VSecurity::validateInput('no', 'boolean'));
// Test string validation with length constraints
$this->assertEquals('test', VSecurity::validateInput('test', 'string', null, ['min_length' => 2, 'max_length' => 10]));
$this->assertNull(VSecurity::validateInput('a', 'string', null, ['min_length' => 2, 'max_length' => 10]));
$this->assertNull(VSecurity::validateInput('verylongstring', 'string', null, ['min_length' => 2, 'max_length' => 10]));
}
/**
* Test GET parameter handling
*/
public function testGetParameterHandling()
{
$_GET['test_int'] = '123';
$_GET['test_string'] = 'hello world';
$_GET['test_email'] = 'test@example.com';
$_GET['test_invalid'] = 'invalid_email';
$this->assertEquals(123, VSecurity::getParam('test_int', 'int'));
$this->assertEquals('hello world', VSecurity::getParam('test_string', 'string'));
$this->assertEquals('test@example.com', VSecurity::getParam('test_email', 'email'));
$this->assertEquals('default', VSecurity::getParam('test_invalid', 'email', 'default'));
$this->assertNull(VSecurity::getParam('nonexistent', 'string'));
$this->assertEquals('default', VSecurity::getParam('nonexistent', 'string', 'default'));
}
/**
* Test POST parameter handling
*/
public function testPostParameterHandling()
{
$_POST['username'] = 'testuser';
$_POST['age'] = '25';
$_POST['email'] = 'user@example.com';
$_POST['malicious'] = '';
$this->assertEquals('testuser', VSecurity::postParam('username', 'string'));
$this->assertEquals(25, VSecurity::postParam('age', 'int'));
$this->assertEquals('user@example.com', VSecurity::postParam('email', 'email'));
$this->assertEquals('<script>alert("xss")</script>', VSecurity::postParam('malicious', 'string'));
$this->assertNull(VSecurity::postParam('nonexistent', 'string'));
}
/**
* Test CSRF token generation and validation
*/
public function testCSRFProtection()
{
// Test token generation
$token1 = VSecurity::generateCSRFToken('test_action');
$this->assertIsString($token1);
$this->assertEquals(64, strlen($token1)); // 32 bytes = 64 hex chars
// Test token validation
$this->assertTrue(VSecurity::validateCSRFToken($token1, 'test_action'));
// Test invalid token
$this->assertFalse(VSecurity::validateCSRFToken('invalid_token', 'test_action'));
// Test token is one-time use
$this->assertFalse(VSecurity::validateCSRFToken($token1, 'test_action'));
// Test different actions have different tokens
$token2 = VSecurity::generateCSRFToken('different_action');
$this->assertNotEquals($token1, $token2);
// Test default action
$defaultToken = VSecurity::generateCSRFToken();
$this->assertTrue(VSecurity::validateCSRFToken($defaultToken, 'default'));
}
/**
* Test CSRF field generation
*/
public function testCSRFFieldGeneration()
{
$field = VSecurity::getCSRFField('test_form');
$this->assertStringContainsString('assertStringContainsString('name="csrf_token"', $field);
$this->assertStringContainsString('value="', $field);
// Extract token from field
preg_match('/value="([^"]+)"/', $field, $matches);
$token = $matches[1] ?? '';
$this->assertNotEmpty($token);
$this->assertEquals(64, strlen($token));
}
/**
* Test CSRF validation from POST data
*/
public function testCSRFValidationFromPost()
{
$token = VSecurity::generateCSRFToken('form_submit');
$_POST['csrf_token'] = $token;
$this->assertTrue(VSecurity::validateCSRFFromPost('form_submit'));
// Test with invalid token
$_POST['csrf_token'] = 'invalid_token';
$this->assertFalse(VSecurity::validateCSRFFromPost('form_submit'));
// Test with missing token
unset($_POST['csrf_token']);
$this->assertFalse(VSecurity::validateCSRFFromPost('form_submit'));
}
/**
* Test output escaping
*/
public function testOutputEscaping()
{
$maliciousInput = '';
$escaped = VSecurity::escapeOutput($maliciousInput);
$this->assertEquals('<script>alert("xss")</script>', $escaped);
// Test with quotes and ampersands
$input = 'Hello "world" & ';
$escaped = VSecurity::escapeOutput($input);
$this->assertEquals('Hello "world" & <friends>', $escaped);
}
/**
* Test JavaScript escaping
*/
public function testJavaScriptEscaping()
{
$input = 'Hello "world" & ',
'javascript:alert("XSS")',
'
',
'