Files
michaelschiemer/tests/Unit/Framework/Filesystem/FileValidatorTest.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

294 lines
11 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Core\ValueObjects\FileSize;
use App\Framework\Filesystem\FileValidator;
use App\Framework\Filesystem\Exceptions\FileValidationException;
describe('FileValidator', function () {
// Factory Methods Tests
it('creates default validator with safe defaults', function () {
$validator = FileValidator::createDefault();
expect($validator->getAllowedExtensions())->toBeNull();
expect($validator->getBlockedExtensions())->toBe(['exe', 'bat', 'sh', 'cmd', 'com']);
expect($validator->getMaxFileSize())->toBeInstanceOf(FileSize::class);
expect($validator->getMaxFileSize()->toBytes())->toBe(100 * 1024 * 1024); // 100MB
});
it('creates strict validator with allowed extensions only', function () {
$validator = FileValidator::createStrict(['txt', 'pdf']);
expect($validator->getAllowedExtensions())->toBe(['txt', 'pdf']);
expect($validator->getBlockedExtensions())->toBeNull();
expect($validator->getMaxFileSize()->toBytes())->toBe(50 * 1024 * 1024); // 50MB
});
it('creates upload validator with secure defaults', function () {
$validator = FileValidator::forUploads();
expect($validator->getAllowedExtensions())->toBe(['jpg', 'jpeg', 'png', 'gif', 'pdf', 'txt', 'csv', 'json']);
expect($validator->getBlockedExtensions())->toBe(['exe', 'bat', 'sh', 'cmd', 'com', 'php', 'phtml']);
expect($validator->getMaxFileSize()->toBytes())->toBe(10 * 1024 * 1024); // 10MB
});
it('creates image validator with image extensions only', function () {
$validator = FileValidator::forImages();
expect($validator->getAllowedExtensions())->toBe(['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']);
expect($validator->getBlockedExtensions())->toBeNull();
expect($validator->getMaxFileSize()->toBytes())->toBe(5 * 1024 * 1024); // 5MB
});
it('allows custom max size for uploads', function () {
$customSize = FileSize::fromMegabytes(20);
$validator = FileValidator::forUploads($customSize);
expect($validator->getMaxFileSize()->toBytes())->toBe(20 * 1024 * 1024);
});
// Path Validation Tests
it('validates normal file paths', function () {
$validator = FileValidator::createDefault();
$validator->validatePath('/var/www/html/test.txt');
$validator->validatePath('relative/path/file.json');
expect(true)->toBeTrue(); // No exception thrown
});
it('throws exception for empty path', function () {
$validator = FileValidator::createDefault();
try {
$validator->validatePath('');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('File path cannot be empty');
}
});
it('throws exception for path with null bytes', function () {
$validator = FileValidator::createDefault();
try {
$validator->validatePath("/path/to/file\0.txt");
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('null bytes');
}
});
it('detects path traversal with ../', function () {
$validator = FileValidator::createDefault();
try {
$validator->validatePath('../../../etc/passwd');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('Path traversal attempt');
}
});
it('detects path traversal with backslash notation', function () {
$validator = FileValidator::createDefault();
try {
$validator->validatePath('..\\..\\windows\\system32');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('Path traversal attempt');
}
});
it('detects URL-encoded path traversal', function () {
$validator = FileValidator::createDefault();
try {
$validator->validatePath('/path/%2e%2e/etc/passwd');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('Path traversal attempt');
}
});
// Extension Validation Tests
it('validates allowed extensions', function () {
$validator = FileValidator::createStrict(['txt', 'pdf']);
$validator->validateExtension('/path/to/file.txt');
$validator->validateExtension('/path/to/document.pdf');
expect(true)->toBeTrue(); // No exception thrown
});
it('throws exception for disallowed extension', function () {
$validator = FileValidator::createStrict(['txt', 'pdf']);
try {
$validator->validateExtension('/path/to/file.exe');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('not allowed');
}
});
it('throws exception for blocked extension', function () {
$validator = FileValidator::createDefault();
try {
$validator->validateExtension('/path/to/malware.exe');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('is blocked');
}
});
it('allows files without extension when no whitelist defined', function () {
$validator = FileValidator::createDefault();
$validator->validateExtension('/path/to/Makefile');
expect(true)->toBeTrue(); // No exception thrown
});
it('throws exception for files without extension when whitelist defined', function () {
$validator = FileValidator::createStrict(['txt', 'pdf']);
try {
$validator->validateExtension('/path/to/Makefile');
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('no extension');
}
});
it('handles case-insensitive extension validation', function () {
$validator = FileValidator::createStrict(['txt', 'pdf']);
$validator->validateExtension('/path/to/file.TXT');
$validator->validateExtension('/path/to/document.PDF');
expect(true)->toBeTrue(); // No exception thrown
});
// FileSize Validation Tests
it('validates file size within limit', function () {
$validator = FileValidator::forUploads(FileSize::fromMegabytes(10));
$validator->validateFileSize(FileSize::fromMegabytes(5));
$validator->validateFileSize(FileSize::fromMegabytes(9));
expect(true)->toBeTrue(); // No exception thrown
});
it('throws exception when file size exceeds limit', function () {
$validator = FileValidator::forUploads(FileSize::fromMegabytes(10));
try {
$validator->validateFileSize(FileSize::fromMegabytes(15));
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('exceeds maximum allowed size');
}
});
it('allows any file size when no limit defined', function () {
$validator = new FileValidator(
allowedExtensions: null,
blockedExtensions: null,
maxFileSize: null,
baseDirectory: null
);
$validator->validateFileSize(FileSize::fromGigabytes(10));
expect(true)->toBeTrue(); // No exception thrown
});
// Extension Check Helper Tests
it('checks if extension is allowed', function () {
$validator = FileValidator::createStrict(['txt', 'pdf']);
expect($validator->isExtensionAllowed('txt'))->toBeTrue();
expect($validator->isExtensionAllowed('.pdf'))->toBeTrue(); // With dot
expect($validator->isExtensionAllowed('TXT'))->toBeTrue(); // Case insensitive
expect($validator->isExtensionAllowed('exe'))->toBeFalse();
});
it('checks blocked extensions correctly', function () {
$validator = FileValidator::createDefault();
expect($validator->isExtensionAllowed('exe'))->toBeFalse();
expect($validator->isExtensionAllowed('txt'))->toBeTrue();
expect($validator->isExtensionAllowed('pdf'))->toBeTrue();
});
it('allows all extensions when no restrictions', function () {
$validator = new FileValidator();
expect($validator->isExtensionAllowed('exe'))->toBeTrue();
expect($validator->isExtensionAllowed('anything'))->toBeTrue();
});
// Composite Validation Tests
it('validates upload with all checks', function () {
$validator = FileValidator::forUploads();
$validator->validateUpload('/path/to/image.jpg', FileSize::fromMegabytes(2));
expect(true)->toBeTrue(); // No exception thrown
});
it('composite upload validation catches path traversal', function () {
$validator = FileValidator::forUploads();
try {
$validator->validateUpload('../../../etc/passwd.jpg', FileSize::fromMegabytes(1));
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('Path traversal');
}
});
it('composite upload validation catches disallowed extension', function () {
$validator = FileValidator::forUploads();
try {
$validator->validateUpload('/path/to/script.php', FileSize::fromMegabytes(1));
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('is not allowed');
}
});
it('composite upload validation catches oversized file', function () {
$validator = FileValidator::forUploads();
try {
$validator->validateUpload('/path/to/image.jpg', FileSize::fromMegabytes(50));
expect(true)->toBeFalse('Should have thrown exception');
} catch (FileValidationException $e) {
expect($e->getMessage())->toContain('exceeds maximum');
}
});
// Getter Tests
it('provides access to configuration', function () {
$allowedExtensions = ['txt', 'pdf'];
$blockedExtensions = ['exe', 'bat'];
$maxSize = FileSize::fromMegabytes(20);
$validator = new FileValidator(
allowedExtensions: $allowedExtensions,
blockedExtensions: $blockedExtensions,
maxFileSize: $maxSize
);
expect($validator->getAllowedExtensions())->toBe($allowedExtensions);
expect($validator->getBlockedExtensions())->toBe($blockedExtensions);
expect($validator->getMaxFileSize())->toBe($maxSize);
});
});