image($name, 12, 12); $stored = SecurePublicImageUpload::store($file, 'uploads', 1024); $this->assertMatchesRegularExpression('#^uploads/[0-9a-f-]{36}\.'.$ext.'$#', $stored['path']); $this->assertContains($ext, SecurePublicImageUpload::ALLOWED_EXTENSIONS); Storage::disk('public')->assertExists($stored['path']); } } /** * @dataProvider rejectedClientNamesProvider */ public function test_rejected_names_do_not_write_public_files(string $clientName): void { Storage::fake('public'); $file = UploadedFile::fake()->create($clientName, 8, 'text/plain'); try { SecurePublicImageUpload::store($file, 'uploads', 1024); $this->fail('expected validation to reject '.$clientName); } catch (ValidationException $e) { $this->assertArrayHasKey('file', $e->errors()); } $this->assertSame([], Storage::disk('public')->allFiles('uploads')); } /** * @return array */ public static function rejectedClientNamesProvider(): array { return [ 'php' => ['note.php'], 'phtml' => ['note.phtml'], 'phar' => ['note.phar'], 'php_jpg' => ['1.php.jpg'], 'php_png' => ['xxx.php.png'], ]; } public function test_jpg_name_with_non_image_bytes_is_rejected(): void { Storage::fake('public'); $file = UploadedFile::fake()->create('cover.jpg', 16, 'text/plain'); $this->expectException(ValidationException::class); SecurePublicImageUpload::store($file, 'uploads', 1024); } public function test_filename_guard_rejects_null_byte_and_denied_segments(): void { $this->expectException(ValidationException::class); UploadFilenameGuard::lastAllowedExtension("cover.php\0.jpg", SecurePublicImageUpload::ALLOWED_EXTENSIONS); } public function test_private_import_stays_off_public_disk(): void { Storage::fake('local'); Storage::fake('public'); $file = UploadedFile::fake()->create('sheet.xlsx', 12, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $absolute = SecurePrivateImportUpload::storeTemporary($file, 'tmp/declaration-import', ['xlsx', 'xls'], 10240); $this->assertStringContainsString('/storage/app/tmp/declaration-import/', str_replace('\\', '/', $absolute)); $this->assertSame([], Storage::disk('public')->allFiles()); $relative = 'tmp/declaration-import/'.basename($absolute); Storage::disk('local')->assertExists($relative); Storage::disk('local')->delete($relative); } public function test_private_import_rejects_denied_extension(): void { Storage::fake('local'); Storage::fake('public'); $file = UploadedFile::fake()->create('sheet.php', 8, 'text/plain'); try { SecurePrivateImportUpload::storeTemporary($file, 'tmp/declaration-import', ['xlsx', 'xls', 'doc', 'docx'], 10240); $this->fail('expected validation to reject denied import extension'); } catch (ValidationException $e) { $this->assertArrayHasKey('file', $e->errors()); } $this->assertSame([], Storage::disk('public')->allFiles()); $this->assertSame([], Storage::disk('local')->allFiles('tmp/declaration-import')); } }