diff --git a/database/migrations/2026_10_26_160000_create_image_scan_policies_table.php b/database/migrations/2026_10_26_160000_create_image_scan_policies_table.php deleted file mode 100644 index 240510c82b..0000000000 --- a/database/migrations/2026_10_26_160000_create_image_scan_policies_table.php +++ /dev/null @@ -1,41 +0,0 @@ -id(); - $table->string('uuid')->unique(); - $table->string('scope_type')->nullable(); - $table->unsignedBigInteger('scope_id')->nullable(); - $table->string('alert_level')->nullable(); - $table->boolean('gate_enabled')->nullable(); - $table->string('on_scan_error')->nullable(); - $table->timestamps(); - - $table->unique(['scope_type', 'scope_id']); - }); - - DB::table('image_scan_policies')->insert([ - 'uuid' => new_public_id(), - 'scope_type' => null, - 'scope_id' => null, - 'alert_level' => 'critical', - 'gate_enabled' => false, - 'on_scan_error' => 'deploy', - 'created_at' => now(), - 'updated_at' => now(), - ]); - } - - public function down(): void - { - Schema::dropIfExists('image_scan_policies'); - } -}; diff --git a/database/migrations/2026_10_26_160100_create_image_scan_ignores_table.php b/database/migrations/2026_10_26_160100_create_image_scan_ignores_table.php deleted file mode 100644 index 48fe187dce..0000000000 --- a/database/migrations/2026_10_26_160100_create_image_scan_ignores_table.php +++ /dev/null @@ -1,31 +0,0 @@ -id(); - $table->string('uuid')->unique(); - $table->foreignId('team_id')->constrained()->cascadeOnDelete(); - $table->nullableMorphs('resource'); - $table->string('vulnerability_id', 64)->nullable(); - $table->string('package', 214)->nullable(); - $table->string('reason', 500); - $table->timestamp('expires_at')->nullable(); - $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); - $table->timestamps(); - - $table->index(['team_id', 'resource_type', 'resource_id']); - }); - } - - public function down(): void - { - Schema::dropIfExists('image_scan_ignores'); - } -}; diff --git a/database/migrations/2026_10_26_160200_create_image_sboms_table.php b/database/migrations/2026_10_26_160200_create_image_sboms_table.php deleted file mode 100644 index 24cdd2c5ac..0000000000 --- a/database/migrations/2026_10_26_160200_create_image_sboms_table.php +++ /dev/null @@ -1,28 +0,0 @@ -id(); - $table->foreignId('server_id')->constrained()->cascadeOnDelete(); - $table->string('image_id', 80); - $table->string('platform', 32); - $table->jsonb('packages'); - $table->boolean('packages_truncated')->default(false); - $table->timestamp('last_used_at'); - - $table->unique(['server_id', 'image_id']); - }); - } - - public function down(): void - { - Schema::dropIfExists('image_sboms'); - } -}; diff --git a/database/migrations/2026_10_26_160300_create_image_scans_table.php b/database/migrations/2026_10_26_160300_create_image_scans_table.php deleted file mode 100644 index 4b2cbcf1b7..0000000000 --- a/database/migrations/2026_10_26_160300_create_image_scans_table.php +++ /dev/null @@ -1,43 +0,0 @@ -id(); - $table->string('uuid')->unique(); - $table->foreignId('server_id')->constrained()->cascadeOnDelete(); - $table->foreignId('image_sbom_id')->nullable()->constrained('image_sboms')->nullOnDelete(); - $table->string('image_id', 80); - $table->string('repo_digest', 80)->nullable(); - $table->morphs('resource'); - $table->string('image_ref', 512); - $table->foreignId('deployment_queue_id')->nullable()->constrained('application_deployment_queues')->nullOnDelete(); - $table->string('status'); - $table->string('error_code')->nullable(); - $table->jsonb('error_context')->nullable(); - $table->jsonb('summary')->nullable(); - $table->jsonb('findings')->nullable(); - $table->jsonb('candidates')->nullable(); - $table->string('base_image_ref', 512)->nullable(); - $table->boolean('builds_image')->default(false); - $table->boolean('is_baseline')->default(true); - $table->timestamp('grype_db_built_at')->nullable(); - $table->timestamp('scanned_at')->nullable(); - $table->timestamps(); - - $table->unique(['resource_type', 'resource_id', 'image_id']); - $table->index(['server_id', 'image_id']); - }); - } - - public function down(): void - { - Schema::dropIfExists('image_scans'); - } -}; diff --git a/database/migrations/2026_10_26_160400_add_image_scan_settings_columns.php b/database/migrations/2026_10_26_160400_add_image_scan_settings_columns.php deleted file mode 100644 index 010cfeb221..0000000000 --- a/database/migrations/2026_10_26_160400_add_image_scan_settings_columns.php +++ /dev/null @@ -1,60 +0,0 @@ -boolean('is_image_scan_enabled')->default(false); - $table->unsignedInteger('image_candidate_scans_today')->default(0); - $table->date('image_candidate_scans_date')->nullable(); - }); - - Schema::table('instance_settings', function (Blueprint $table) { - $table->boolean('is_image_scanning_allowed')->default(true); - $table->string('image_scan_db_url')->nullable(); - }); - - Schema::table('teams', function (Blueprint $table) { - $table->boolean('scan_images_on_new_servers')->default(false); - }); - - Schema::table('application_settings', function (Blueprint $table) { - $table->boolean('is_image_scan_enabled')->default(true); - $table->boolean('image_builds_by_team')->nullable(); - }); - - Schema::table('application_deployment_queues', function (Blueprint $table) { - $table->jsonb('scan_gate_result')->nullable(); - $table->foreignId('scan_gate_bypassed_by')->nullable()->constrained('users')->nullOnDelete(); - }); - } - - public function down(): void - { - Schema::table('application_deployment_queues', function (Blueprint $table) { - $table->dropConstrainedForeignId('scan_gate_bypassed_by'); - $table->dropColumn('scan_gate_result'); - }); - - Schema::table('application_settings', function (Blueprint $table) { - $table->dropColumn(['is_image_scan_enabled', 'image_builds_by_team']); - }); - - Schema::table('teams', function (Blueprint $table) { - $table->dropColumn('scan_images_on_new_servers'); - }); - - Schema::table('instance_settings', function (Blueprint $table) { - $table->dropColumn(['is_image_scanning_allowed', 'image_scan_db_url']); - }); - - Schema::table('server_settings', function (Blueprint $table) { - $table->dropColumn(['is_image_scan_enabled', 'image_candidate_scans_today', 'image_candidate_scans_date']); - }); - } -}; diff --git a/database/migrations/2026_10_26_160500_add_image_vulnerabilities_notifications_to_email_notification_settings_table.php b/database/migrations/2026_10_26_160500_add_image_vulnerabilities_notifications_to_email_notification_settings_table.php deleted file mode 100644 index 21e1b81224..0000000000 --- a/database/migrations/2026_10_26_160500_add_image_vulnerabilities_notifications_to_email_notification_settings_table.php +++ /dev/null @@ -1,22 +0,0 @@ -boolean('image_vulnerabilities_email_notifications')->default(true); - }); - } - - public function down(): void - { - Schema::table('email_notification_settings', function (Blueprint $table) { - $table->dropColumn('image_vulnerabilities_email_notifications'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160501_add_image_vulnerabilities_notifications_to_discord_notification_settings_table.php b/database/migrations/2026_10_26_160501_add_image_vulnerabilities_notifications_to_discord_notification_settings_table.php deleted file mode 100644 index b12808cbf9..0000000000 --- a/database/migrations/2026_10_26_160501_add_image_vulnerabilities_notifications_to_discord_notification_settings_table.php +++ /dev/null @@ -1,22 +0,0 @@ -boolean('image_vulnerabilities_discord_notifications')->default(false); - }); - } - - public function down(): void - { - Schema::table('discord_notification_settings', function (Blueprint $table) { - $table->dropColumn('image_vulnerabilities_discord_notifications'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160502_add_image_vulnerabilities_notifications_to_telegram_notification_settings_table.php b/database/migrations/2026_10_26_160502_add_image_vulnerabilities_notifications_to_telegram_notification_settings_table.php deleted file mode 100644 index 3a9c030077..0000000000 --- a/database/migrations/2026_10_26_160502_add_image_vulnerabilities_notifications_to_telegram_notification_settings_table.php +++ /dev/null @@ -1,24 +0,0 @@ -boolean('image_vulnerabilities_telegram_notifications')->default(false); - $table->text('telegram_notifications_image_vulnerabilities_thread_id')->nullable(); - }); - } - - public function down(): void - { - Schema::table('telegram_notification_settings', function (Blueprint $table) { - $table->dropColumn('image_vulnerabilities_telegram_notifications'); - $table->dropColumn('telegram_notifications_image_vulnerabilities_thread_id'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160503_add_image_vulnerabilities_notifications_to_slack_notification_settings_table.php b/database/migrations/2026_10_26_160503_add_image_vulnerabilities_notifications_to_slack_notification_settings_table.php deleted file mode 100644 index beda183493..0000000000 --- a/database/migrations/2026_10_26_160503_add_image_vulnerabilities_notifications_to_slack_notification_settings_table.php +++ /dev/null @@ -1,22 +0,0 @@ -boolean('image_vulnerabilities_slack_notifications')->default(false); - }); - } - - public function down(): void - { - Schema::table('slack_notification_settings', function (Blueprint $table) { - $table->dropColumn('image_vulnerabilities_slack_notifications'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160504_add_image_vulnerabilities_notifications_to_pushover_notification_settings_table.php b/database/migrations/2026_10_26_160504_add_image_vulnerabilities_notifications_to_pushover_notification_settings_table.php deleted file mode 100644 index fb39c5de94..0000000000 --- a/database/migrations/2026_10_26_160504_add_image_vulnerabilities_notifications_to_pushover_notification_settings_table.php +++ /dev/null @@ -1,22 +0,0 @@ -boolean('image_vulnerabilities_pushover_notifications')->default(false); - }); - } - - public function down(): void - { - Schema::table('pushover_notification_settings', function (Blueprint $table) { - $table->dropColumn('image_vulnerabilities_pushover_notifications'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160505_add_image_vulnerabilities_notifications_to_webhook_notification_settings_table.php b/database/migrations/2026_10_26_160505_add_image_vulnerabilities_notifications_to_webhook_notification_settings_table.php deleted file mode 100644 index 1445ea2c17..0000000000 --- a/database/migrations/2026_10_26_160505_add_image_vulnerabilities_notifications_to_webhook_notification_settings_table.php +++ /dev/null @@ -1,22 +0,0 @@ -boolean('image_vulnerabilities_webhook_notifications')->default(false); - }); - } - - public function down(): void - { - Schema::table('webhook_notification_settings', function (Blueprint $table) { - $table->dropColumn('image_vulnerabilities_webhook_notifications'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160600_add_notified_ids_to_image_scans_table.php b/database/migrations/2026_10_26_160600_add_notified_ids_to_image_scans_table.php deleted file mode 100644 index f45212b9f6..0000000000 --- a/database/migrations/2026_10_26_160600_add_notified_ids_to_image_scans_table.php +++ /dev/null @@ -1,22 +0,0 @@ -jsonb('notified_ids')->nullable(); - }); - } - - public function down(): void - { - Schema::table('image_scans', function (Blueprint $table) { - $table->dropColumn('notified_ids'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160601_add_notified_candidates_to_image_scans_table.php b/database/migrations/2026_10_26_160601_add_notified_candidates_to_image_scans_table.php deleted file mode 100644 index f5c936b508..0000000000 --- a/database/migrations/2026_10_26_160601_add_notified_candidates_to_image_scans_table.php +++ /dev/null @@ -1,22 +0,0 @@ -jsonb('notified_candidates')->nullable(); - }); - } - - public function down(): void - { - Schema::table('image_scans', function (Blueprint $table) { - $table->dropColumn('notified_candidates'); - }); - } -}; diff --git a/database/migrations/2026_10_26_160700_create_image_scan_notification_states_table.php b/database/migrations/2026_10_26_160700_create_image_scan_notification_states_table.php deleted file mode 100644 index e9627f92e8..0000000000 --- a/database/migrations/2026_10_26_160700_create_image_scan_notification_states_table.php +++ /dev/null @@ -1,42 +0,0 @@ -id(); - $table->morphs('resource'); - $table->foreignId('team_id')->constrained()->cascadeOnDelete(); - $table->jsonb('notified_ids')->nullable(); - $table->jsonb('notified_candidates')->nullable(); - $table->boolean('is_baseline')->default(true); - $table->unsignedBigInteger('baseline_deployment_queue_id')->nullable(); - $table->timestamps(); - - $table->unique(['resource_type', 'resource_id']); - }); - - Schema::table('image_scans', function (Blueprint $table) { - $table->dropColumn(['notified_ids', 'notified_candidates']); - }); - } - - public function down(): void - { - Schema::table('image_scans', function (Blueprint $table) { - $table->jsonb('notified_ids')->nullable(); - $table->jsonb('notified_candidates')->nullable(); - }); - - Schema::dropIfExists('image_scan_notification_states'); - } -}; diff --git a/database/migrations/2026_10_26_233314_drop_duplicate_resource_index_from_image_scan_notification_states_table.php b/database/migrations/2026_10_26_233314_drop_duplicate_resource_index_from_image_scan_notification_states_table.php deleted file mode 100644 index f274bef0f8..0000000000 --- a/database/migrations/2026_10_26_233314_drop_duplicate_resource_index_from_image_scan_notification_states_table.php +++ /dev/null @@ -1,26 +0,0 @@ -dropIndex(['resource_type', 'resource_id']); - }); - } - - public function down(): void - { - Schema::table('image_scan_notification_states', function (Blueprint $table) { - $table->index(['resource_type', 'resource_id']); - }); - } -}; diff --git a/database/migrations/2026_10_27_120000_refresh_image_scan_security_status.php b/database/migrations/2026_10_27_120000_refresh_image_scan_security_status.php deleted file mode 100644 index 2bd0e122ff..0000000000 --- a/database/migrations/2026_10_27_120000_refresh_image_scan_security_status.php +++ /dev/null @@ -1,28 +0,0 @@ -whereIn('id', ImageScan::query()->select('server_id')->distinct()) - ->distinct() - ->pluck('team_id') - ->filter(fn ($teamId): bool => $teamId !== null) - ->each(fn ($teamId) => RefreshImageScanSummariesJob::dispatch((int) $teamId)); - } catch (Throwable $e) { - report($e); - } - } - - public function down(): void {} -}; diff --git a/database/migrations/2026_10_27_130000_replace_image_scan_gate_enabled_with_gate_mode.php b/database/migrations/2026_10_27_130000_replace_image_scan_gate_enabled_with_gate_mode.php deleted file mode 100644 index f8433381c4..0000000000 --- a/database/migrations/2026_10_27_130000_replace_image_scan_gate_enabled_with_gate_mode.php +++ /dev/null @@ -1,44 +0,0 @@ -string('gate_mode')->nullable()->after('gate_enabled'); - }); - - DB::table('image_scan_policies')->where('gate_enabled', true)->update(['gate_mode' => 'strict']); - DB::table('image_scan_policies')->where('gate_enabled', false)->update(['gate_mode' => 'off']); - DB::table('image_scan_policies')->whereNull('scope_type')->whereNull('gate_mode')->update(['gate_mode' => 'off']); - - Schema::table('image_scan_policies', function (Blueprint $table) { - $table->dropColumn(['gate_enabled', 'alert_level']); - }); - } - - public function down(): void - { - Schema::table('image_scan_policies', function (Blueprint $table) { - $table->string('alert_level')->nullable()->after('scope_id'); - $table->boolean('gate_enabled')->nullable()->after('alert_level'); - }); - - DB::table('image_scan_policies')->whereIn('gate_mode', ['on', 'strict'])->update(['gate_enabled' => true]); - DB::table('image_scan_policies')->where('gate_mode', 'off')->update(['gate_enabled' => false]); - DB::table('image_scan_policies')->whereNull('scope_type')->update(['alert_level' => 'critical']); - - Schema::table('image_scan_policies', function (Blueprint $table) { - $table->dropColumn('gate_mode'); - }); - } -}; diff --git a/database/migrations/2026_10_27_163900_add_digest_columns_to_image_scan_notification_states_table.php b/database/migrations/2026_10_27_163900_add_digest_columns_to_image_scan_notification_states_table.php deleted file mode 100644 index a3fb126c2a..0000000000 --- a/database/migrations/2026_10_27_163900_add_digest_columns_to_image_scan_notification_states_table.php +++ /dev/null @@ -1,29 +0,0 @@ -jsonb('digest_ids')->nullable(); - $table->boolean('check_reported')->default(false); - $table->unsignedSmallInteger('held_digests')->default(0); - }); - } - - public function down(): void - { - Schema::table('image_scan_notification_states', function (Blueprint $table) { - $table->dropColumn(['digest_ids', 'check_reported', 'held_digests']); - }); - } -}; diff --git a/database/migrations/2026_10_28_145228_add_docker_compose_image_overrides_to_applications_table.php b/database/migrations/2026_10_28_145228_add_docker_compose_image_overrides_to_applications_table.php deleted file mode 100644 index dec68e2d71..0000000000 --- a/database/migrations/2026_10_28_145228_add_docker_compose_image_overrides_to_applications_table.php +++ /dev/null @@ -1,23 +0,0 @@ -json('docker_compose_image_overrides')->nullable(); - }); - } - - public function down(): void - { - Schema::table('applications', function (Blueprint $table) { - $table->dropColumn('docker_compose_image_overrides'); - }); - } -}; diff --git a/database/migrations/2026_10_29_100000_create_image_scanning_tables.php b/database/migrations/2026_10_29_100000_create_image_scanning_tables.php new file mode 100644 index 0000000000..3cfc56c42c --- /dev/null +++ b/database/migrations/2026_10_29_100000_create_image_scanning_tables.php @@ -0,0 +1,200 @@ + notification settings table => [alert column, default] */ + private const NOTIFICATION_COLUMNS = [ + 'email_notification_settings' => ['image_vulnerabilities_email_notifications', true], + 'discord_notification_settings' => ['image_vulnerabilities_discord_notifications', false], + 'telegram_notification_settings' => ['image_vulnerabilities_telegram_notifications', false], + 'slack_notification_settings' => ['image_vulnerabilities_slack_notifications', false], + 'pushover_notification_settings' => ['image_vulnerabilities_pushover_notifications', false], + 'webhook_notification_settings' => ['image_vulnerabilities_webhook_notifications', false], + ]; + + public function up(): void + { + Schema::create('image_scan_policies', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->string('scope_type')->nullable(); + $table->unsignedBigInteger('scope_id')->nullable(); + $table->string('gate_mode')->nullable(); + $table->string('on_scan_error')->nullable(); + $table->timestamps(); + + $table->unique(['scope_type', 'scope_id']); + }); + + DB::table('image_scan_policies')->insert([ + 'uuid' => new_public_id(), + 'scope_type' => null, + 'scope_id' => null, + 'gate_mode' => 'off', + 'on_scan_error' => 'deploy', + 'created_at' => now(), + 'updated_at' => now(), + ]); + + Schema::create('image_scan_ignores', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->foreignId('team_id')->constrained()->cascadeOnDelete(); + $table->nullableMorphs('resource'); + $table->string('vulnerability_id', 64)->nullable(); + $table->string('package', 214)->nullable(); + $table->string('reason', 500); + $table->timestamp('expires_at')->nullable(); + $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); + $table->timestamps(); + + $table->index(['team_id', 'resource_type', 'resource_id']); + }); + + Schema::create('image_sboms', function (Blueprint $table) { + $table->id(); + $table->foreignId('server_id')->constrained()->cascadeOnDelete(); + $table->string('image_id', 80); + $table->string('platform', 32); + $table->jsonb('packages'); + $table->boolean('packages_truncated')->default(false); + $table->timestamp('last_used_at'); + + $table->unique(['server_id', 'image_id']); + }); + + Schema::create('image_scans', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->foreignId('server_id')->constrained()->cascadeOnDelete(); + $table->foreignId('image_sbom_id')->nullable()->constrained('image_sboms')->nullOnDelete(); + $table->string('image_id', 80); + $table->string('repo_digest', 80)->nullable(); + $table->morphs('resource'); + $table->string('image_ref', 512); + $table->foreignId('deployment_queue_id')->nullable()->constrained('application_deployment_queues')->nullOnDelete(); + $table->string('status'); + $table->string('error_code')->nullable(); + $table->jsonb('error_context')->nullable(); + $table->jsonb('summary')->nullable(); + $table->jsonb('findings')->nullable(); + $table->jsonb('candidates')->nullable(); + $table->string('base_image_ref', 512)->nullable(); + $table->boolean('builds_image')->default(false); + $table->boolean('is_baseline')->default(true); + $table->timestamp('grype_db_built_at')->nullable(); + $table->timestamp('scanned_at')->nullable(); + $table->timestamps(); + + $table->unique(['resource_type', 'resource_id', 'image_id']); + $table->index(['server_id', 'image_id']); + }); + + Schema::create('image_scan_notification_states', function (Blueprint $table) { + $table->id(); + // No morphs(): its index would repeat the unique key below. + $table->string('resource_type'); + $table->unsignedBigInteger('resource_id'); + $table->foreignId('team_id')->constrained()->cascadeOnDelete(); + $table->jsonb('notified_ids')->nullable(); + $table->jsonb('notified_candidates')->nullable(); + $table->jsonb('digest_ids')->nullable(); + $table->boolean('is_baseline')->default(true); + $table->boolean('check_reported')->default(false); + $table->unsignedSmallInteger('held_digests')->default(0); + $table->unsignedBigInteger('baseline_deployment_queue_id')->nullable(); + $table->timestamps(); + + $table->unique(['resource_type', 'resource_id']); + }); + + Schema::table('server_settings', function (Blueprint $table) { + $table->boolean('is_image_scan_enabled')->default(false); + $table->unsignedInteger('image_candidate_scans_today')->default(0); + $table->date('image_candidate_scans_date')->nullable(); + }); + + Schema::table('instance_settings', function (Blueprint $table) { + $table->boolean('is_image_scanning_allowed')->default(true); + $table->string('image_scan_db_url')->nullable(); + }); + + Schema::table('teams', function (Blueprint $table) { + $table->boolean('scan_images_on_new_servers')->default(false); + }); + + Schema::table('application_settings', function (Blueprint $table) { + $table->boolean('is_image_scan_enabled')->default(true); + $table->boolean('image_builds_by_team')->nullable(); + }); + + Schema::table('applications', function (Blueprint $table) { + // Security updates of a Compose app: {service: {from, to}}; applied while the compose file still names `from`. + $table->json('docker_compose_image_overrides')->nullable(); + }); + + Schema::table('application_deployment_queues', function (Blueprint $table) { + $table->jsonb('scan_gate_result')->nullable(); + $table->foreignId('scan_gate_bypassed_by')->nullable()->constrained('users')->nullOnDelete(); + }); + + foreach (self::NOTIFICATION_COLUMNS as $tableName => [$column, $default]) { + Schema::table($tableName, function (Blueprint $table) use ($column, $default) { + $table->boolean($column)->default($default); + }); + } + + Schema::table('telegram_notification_settings', function (Blueprint $table) { + $table->text('telegram_notifications_image_vulnerabilities_thread_id')->nullable(); + }); + } + + public function down(): void + { + Schema::table('telegram_notification_settings', function (Blueprint $table) { + $table->dropColumn('telegram_notifications_image_vulnerabilities_thread_id'); + }); + + foreach (self::NOTIFICATION_COLUMNS as $tableName => [$column]) { + Schema::table($tableName, function (Blueprint $table) use ($column) { + $table->dropColumn($column); + }); + } + + Schema::table('application_deployment_queues', function (Blueprint $table) { + $table->dropConstrainedForeignId('scan_gate_bypassed_by'); + $table->dropColumn('scan_gate_result'); + }); + + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('docker_compose_image_overrides'); + }); + + Schema::table('application_settings', function (Blueprint $table) { + $table->dropColumn(['is_image_scan_enabled', 'image_builds_by_team']); + }); + + Schema::table('teams', function (Blueprint $table) { + $table->dropColumn('scan_images_on_new_servers'); + }); + + Schema::table('instance_settings', function (Blueprint $table) { + $table->dropColumn(['is_image_scanning_allowed', 'image_scan_db_url']); + }); + + Schema::table('server_settings', function (Blueprint $table) { + $table->dropColumn(['is_image_scan_enabled', 'image_candidate_scans_today', 'image_candidate_scans_date']); + }); + + Schema::dropIfExists('image_scan_notification_states'); + Schema::dropIfExists('image_scans'); + Schema::dropIfExists('image_sboms'); + Schema::dropIfExists('image_scan_ignores'); + Schema::dropIfExists('image_scan_policies'); + } +}; diff --git a/tests/Feature/ImageScan/ImageScanModelsTest.php b/tests/Feature/ImageScan/ImageScanModelsTest.php index 0dedc9349f..d1d68070e1 100644 --- a/tests/Feature/ImageScan/ImageScanModelsTest.php +++ b/tests/Feature/ImageScan/ImageScanModelsTest.php @@ -223,28 +223,11 @@ it('keeps one index on the resource of a notification state, the unique key', fu ->and($indexes->first()['unique'])->toBeTrue(); }); -it('turns Block deploys on into strict and drops the alert level when it migrates', function () { - $migration = require database_path('migrations/2026_10_27_130000_replace_image_scan_gate_enabled_with_gate_mode.php'); - $migration->down(); - ImageScanPolicy::query()->whereNull('scope_type')->delete(); - $rows = [ - 'instance' => [null, null, null], - 'on' => [$this->server->getMorphClass(), $this->server->id, true], - 'off' => [$this->team->getMorphClass(), $this->team->id, false], - 'inherit' => ['App\\Models\\Application', 1, null], - ]; - foreach ($rows as $name => [$type, $id, $gate]) { - DB::table('image_scan_policies')->insert(['uuid' => $name, 'scope_type' => $type, 'scope_id' => $id, 'alert_level' => 'high', 'gate_enabled' => $gate]); - } +it('starts with instance rules that do not block deploys and deploy when a scan cannot run', function () { + $instance = DB::table('image_scan_policies')->whereNull('scope_type')->whereNull('scope_id')->first(); - $migration->up(); - - expect(DB::table('image_scan_policies')->pluck('gate_mode', 'uuid')->all())->toBe([ - 'instance' => 'off', - 'on' => 'strict', - 'off' => 'off', - 'inherit' => null, - ]) + expect($instance->gate_mode)->toBe('off') + ->and($instance->on_scan_error)->toBe('deploy') ->and(Schema::hasColumns('image_scan_policies', ['alert_level']))->toBeFalse() ->and(Schema::hasColumns('image_scan_policies', ['gate_enabled']))->toBeFalse(); }); diff --git a/tests/Feature/ImageScan/SecurityStatusTest.php b/tests/Feature/ImageScan/SecurityStatusTest.php index 2d238e3c4d..46f3e354af 100644 --- a/tests/Feature/ImageScan/SecurityStatusTest.php +++ b/tests/Feature/ImageScan/SecurityStatusTest.php @@ -4,7 +4,6 @@ use App\Enums\ApplicationDeploymentStatus; use App\Enums\ImageScanStatus; use App\Enums\SecurityActionType; use App\Enums\SecurityStatusType; -use App\Jobs\RefreshImageScanSummariesJob; use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\Environment; @@ -825,14 +824,4 @@ describe('stored status', function () { ->and($stored[ResourceScans::key($four[0])]->checkContext)->toBe(['checked' => 1, 'total' => 2]) ->and($stored[ResourceScans::key($four[0])]->toArray())->toBe(SecurityStatus::stored($four[0])->toArray()); }); - - it('refreshes the stored status of every team once after the upgrade', function () { - statusTestScan($this->application, []); - $migration = require database_path('migrations/2026_10_27_120000_refresh_image_scan_security_status.php'); - - $migration->up(); - - Queue::assertPushed(RefreshImageScanSummariesJob::class, fn (RefreshImageScanSummariesJob $job): bool => $job->teamId === $this->team->id); - Queue::assertPushed(RefreshImageScanSummariesJob::class, 1); - }); });