eventDispatcher = Mockery::mock(EventDispatcherInterface::class); $this->service = new FileUploadSecurityService($this->eventDispatcher); }); afterEach(function () { Mockery::close(); }); it('validates a safe uploaded file', function () { $file = UploadedFile::createForTesting( name: 'test.jpg', type: 'image/jpeg', size: 1024 * 1024, // 1MB tmpName: '/tmp/test_upload', error: UploadError::OK ); // Mock the file system file_put_contents('/tmp/test_upload', 'fake image content'); $result = $this->service->validateUpload($file); expect($result)->toBeTrue(); unlink('/tmp/test_upload'); }); it('rejects files with upload errors', function () { $file = UploadedFile::createForTesting( name: 'test.jpg', type: 'image/jpeg', size: 1024, tmpName: '/tmp/test_upload', error: UploadError::PARTIAL ); $this->eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse(); }); it('rejects files that are too large', function () { $file = UploadedFile::createForTesting( name: 'large.jpg', type: 'image/jpeg', size: 20 * 1024 * 1024, // 20MB (exceeds 10MB limit) tmpName: '/tmp/large_upload', error: UploadError::OK ); $this->eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse(); }); it('rejects files with dangerous extensions', function () { $file = UploadedFile::createForTesting( name: 'malicious.php', type: 'application/x-php', size: 1024, tmpName: '/tmp/malicious_upload', error: UploadError::OK ); $this->eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse(); }); it('rejects files with forbidden MIME types', function () { $file = UploadedFile::createForTesting( name: 'test.txt', type: 'application/x-executable', size: 1024, tmpName: '/tmp/executable_upload', error: UploadError::OK ); // Create mock file content file_put_contents('/tmp/executable_upload', 'fake executable'); $this->eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse(); unlink('/tmp/executable_upload'); }); it('rejects files with malware signatures', function () { $file = UploadedFile::createForTesting( name: 'suspicious.txt', type: 'text/plain', size: 1024, tmpName: '/tmp/suspicious_upload', error: UploadError::OK ); // Create file with malware signature file_put_contents('/tmp/suspicious_upload', 'normal content '); $this->eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse(); unlink('/tmp/suspicious_upload'); }); it('rejects files with double extensions', function () { $file = UploadedFile::createForTesting( name: 'image.jpg.php', type: 'image/jpeg', size: 1024, tmpName: '/tmp/double_ext_upload', error: UploadError::OK ); // Create mock file file_put_contents('/tmp/double_ext_upload', 'fake image content'); $this->eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse(); unlink('/tmp/double_ext_upload'); }); it('accepts various safe file types', function () { $safeFiles = [ ['test.jpg', 'image/jpeg'], ['test.png', 'image/png'], ['test.gif', 'image/gif'], ['test.webp', 'image/webp'], ['document.pdf', 'application/pdf'], ['data.csv', 'text/csv'], ]; foreach ($safeFiles as [$filename, $mimeType]) { $file = UploadedFile::createForTesting( name: $filename, type: $mimeType, size: 1024, tmpName: '/tmp/safe_upload_' . $filename, error: UploadError::OK ); file_put_contents('/tmp/safe_upload_' . $filename, 'safe content'); $result = $this->service->validateUpload($file); expect($result)->toBeTrue("File $filename should be valid"); unlink('/tmp/safe_upload_' . $filename); } }); it('handles multiple malware signatures', function () { $malwareSignatures = [ 'eval(', 'base64_decode(', 'system(', 'exec(', 'shell_exec(', 'eventDispatcher ->shouldReceive('dispatch') ->once() ->with(Mockery::type(SuspiciousFileUploadEvent::class)); $result = $this->service->validateUpload($file); expect($result)->toBeFalse("Signature '$signature' should be detected"); unlink('/tmp/malware_test'); } }); });