Files
michaelschiemer/tests/Security/WafTests/PathTraversalTest.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

298 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Security\WafTests;
use Tests\Security\SecurityTestCase;
use App\Framework\Waf\WafEngine;
use App\Framework\Http\Method;
/**
* Path Traversal attack detection tests
*
* Tests WAF's ability to detect and block path traversal attacks
*/
final readonly class PathTraversalTest extends SecurityTestCase
{
public function __construct(
private WafEngine $wafEngine
) {}
/**
* Test WAF blocks path traversal in file parameters
*/
public function testBlocksPathTraversalInFileParams(): void
{
$testCases = $this->generatePathTraversalTestCases();
$blocked = 0;
$failed = [];
foreach ($testCases as $testCase) {
$request = $this->createAttackRequest(
uri: '/api/files',
method: Method::GET,
queryParams: ['file' => $testCase['payload']]
);
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
$blocked++;
} else {
$failed[] = $testCase['description'];
}
}
if (!empty($failed)) {
throw new \RuntimeException(
"WAF failed to block " . count($failed) . " path traversal attacks:\n" .
implode("\n", array_slice($failed, 0, 5))
);
}
echo "✅ Blocked {$blocked}/" . count($testCases) . " path traversal attacks in file params\n";
}
/**
* Test WAF blocks path traversal to system files
*/
public function testBlocksSystemFileAccess(): void
{
$systemFiles = [
'/etc/passwd',
'/etc/shadow',
'C:\\Windows\\System32\\config\\SAM',
'/proc/self/environ',
'/var/log/apache2/access.log',
'C:\\boot.ini',
];
$blocked = 0;
$failed = [];
foreach ($systemFiles as $file) {
$request = $this->createAttackRequest(
uri: '/download',
method: Method::GET,
queryParams: ['path' => $file]
);
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
$blocked++;
} else {
$failed[] = "System file: {$file}";
}
}
if (!empty($failed)) {
throw new \RuntimeException(
"WAF failed to block system file access:\n" .
implode("\n", $failed)
);
}
echo "✅ Blocked {$blocked}/" . count($systemFiles) . " system file access attempts\n";
}
/**
* Test WAF blocks encoded path traversal
*/
public function testBlocksEncodedPathTraversal(): void
{
$encodedAttacks = [
'%2e%2e%2f%2e%2e%2fetc%2fpasswd', // URL encoding
'..%252f..%252fetc%252fpasswd', // Double URL encoding
'..%c0%af..%c0%afetc%c0%afpasswd', // Unicode encoding
'%2e%2e%5c%2e%2e%5cwindows%5csystem32', // Windows path encoded
];
$blocked = 0;
$failed = [];
foreach ($encodedAttacks as $attack) {
$request = $this->createAttackRequest(
uri: '/api/download',
method: Method::GET,
queryParams: ['file' => $attack]
);
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
$blocked++;
} else {
$failed[] = substr($attack, 0, 50);
}
}
if (!empty($failed)) {
echo "⚠️ Warning: WAF missed " . count($failed) . " encoded path traversal attacks:\n";
foreach ($failed as $fp) {
echo " - {$fp}\n";
}
}
echo "✅ Blocked {$blocked}/" . count($encodedAttacks) . " encoded path traversal attacks\n";
}
/**
* Test WAF blocks null byte injection
*/
public function testBlocksNullByteInjection(): void
{
$nullByteAttacks = [
'../../../etc/passwd%00.jpg',
'../../boot.ini%00.txt',
'malicious.php%00.jpg',
];
$blocked = 0;
foreach ($nullByteAttacks as $attack) {
$request = $this->createAttackRequest(
uri: '/upload',
method: Method::POST,
postData: ['filename' => $attack]
);
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
$blocked++;
}
}
echo "✅ Blocked {$blocked}/" . count($nullByteAttacks) . " null byte injection attacks\n";
}
/**
* Test WAF allows legitimate file paths
*/
public function testAllowsLegitimateFilePaths(): void
{
$legitimatePaths = [
'documents/report.pdf',
'images/photo.jpg',
'uploads/user123/avatar.png',
'assets/css/style.css',
'public/downloads/manual.pdf',
];
$allowedCount = 0;
$falsePositives = [];
foreach ($legitimatePaths as $path) {
$request = $this->createLegitimateRequest(
uri: '/api/files',
method: Method::GET,
data: ['path' => $path]
);
$decision = $this->wafEngine->analyzeRequest($request);
if (!$decision->shouldBlock()) {
$allowedCount++;
} else {
$falsePositives[] = $path;
}
}
if (!empty($falsePositives)) {
echo "⚠️ Warning: WAF has " . count($falsePositives) . " false positives:\n";
foreach ($falsePositives as $fp) {
echo " - {$fp}\n";
}
} else {
echo "✅ No false positives detected ({$allowedCount}/" . count($legitimatePaths) . " legitimate paths allowed)\n";
}
}
/**
* Test WAF blocks directory listing attempts
*/
public function testBlocksDirectoryListingAttempts(): void
{
$directoryAttacks = [
'.',
'..',
'../',
'../../',
'./',
'../../../',
];
$blocked = 0;
foreach ($directoryAttacks as $attack) {
$request = $this->createAttackRequest(
uri: '/api/browse',
method: Method::GET,
queryParams: ['dir' => $attack]
);
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
$blocked++;
}
}
echo "✅ Blocked {$blocked}/" . count($directoryAttacks) . " directory listing attempts\n";
}
/**
* Run all path traversal tests
*/
public function runAllTests(): array
{
$results = [];
try {
$this->testBlocksPathTraversalInFileParams();
$results['file_params'] = 'PASS';
} catch (\Exception $e) {
$results['file_params'] = 'FAIL: ' . $e->getMessage();
}
try {
$this->testBlocksSystemFileAccess();
$results['system_files'] = 'PASS';
} catch (\Exception $e) {
$results['system_files'] = 'FAIL: ' . $e->getMessage();
}
try {
$this->testBlocksEncodedPathTraversal();
$results['encoded_attacks'] = 'PASS';
} catch (\Exception $e) {
$results['encoded_attacks'] = 'FAIL: ' . $e->getMessage();
}
try {
$this->testBlocksNullByteInjection();
$results['null_byte'] = 'PASS';
} catch (\Exception $e) {
$results['null_byte'] = 'FAIL: ' . $e->getMessage();
}
try {
$this->testAllowsLegitimateFilePaths();
$results['false_positives'] = 'PASS';
} catch (\Exception $e) {
$results['false_positives'] = 'FAIL: ' . $e->getMessage();
}
try {
$this->testBlocksDirectoryListingAttempts();
$results['directory_listing'] = 'PASS';
} catch (\Exception $e) {
$results['directory_listing'] = 'FAIL: ' . $e->getMessage();
}
return $results;
}
}