diff --git a/veza-backend-api/internal/testutils/db.go b/veza-backend-api/internal/testutils/db.go index 296ae90cb..5baa49b4a 100644 --- a/veza-backend-api/internal/testutils/db.go +++ b/veza-backend-api/internal/testutils/db.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "strings" "testing" "gorm.io/driver/postgres" @@ -11,6 +12,72 @@ import ( "gorm.io/gorm/logger" ) +// allowedTestTables is a whitelist of table names that are safe to use +// in DELETE FROM / TRUNCATE statements in test utilities. +// This prevents SQL injection via table name interpolation in fmt.Sprintf. +var allowedTestTables = map[string]bool{ + "users": true, + "user_sessions": true, + "user_profiles": true, + "user_settings": true, + "user_roles": true, + "user_blocks": true, + "roles": true, + "role_permissions": true, + "permissions": true, + "tracks": true, + "track_likes": true, + "track_plays": true, + "track_comments": true, + "track_shares": true, + "track_versions": true, + "track_history": true, + "playlists": true, + "playlist_tracks": true, + "playlist_collaborators": true, + "playlist_follows": true, + "playlist_share_links": true, + "messages": true, + "rooms": true, + "room_members": true, + "notifications": true, + "follows": true, + "likes": true, + "comments": true, + "posts": true, + "sessions": true, + "jobs": true, + "audit_logs": true, + "refresh_tokens": true, + "password_reset_tokens": true, + "email_verification_tokens": true, + "federated_identities": true, + "files": true, + "file_uploads": true, + "file_metadata": true, + "file_conversions": true, + "analytics_events": true, + "admin_settings": true, + "webhooks": true, + "webhook_failures": true, + "playback_history": true, + "queues": true, + "queue_items": true, + "hls_streams": true, + "hls_transcode_queue": true, + "bitrate_adaptation_logs": true, +} + +// validateTableName checks that a table name is in the allowed whitelist. +// Panics if the name is not allowed, preventing potential SQL injection. +func validateTableName(table string) { + // Strip "public." prefix if present + clean := strings.TrimPrefix(table, "public.") + if !allowedTestTables[clean] { + panic(fmt.Sprintf("SECURITY: table name %q is not in the allowed whitelist for test cleanup", table)) + } +} + // SetupTestDB creates a connection to the test container database. // It ensures the container is running and the schema is migrated. // The container is shared across tests (singleton in setup.go), so be mindful of data state. @@ -80,6 +147,9 @@ func ResetTestDB(db *gorm.DB) error { // Also, we need to check if table exists to avoid errors? // With the container setup, tables should always exist. + // Validate table name against whitelist before interpolation + validateTableName(table) + // For simplicity and safety, we try DELETE or TRUNCATE CASCADE. // TRUNCATE table_name CASCADE; if err := db.Exec(fmt.Sprintf("TRUNCATE TABLE %s CASCADE", table)).Error; err != nil { @@ -175,6 +245,9 @@ func cleanupTables(t *testing.T, db *gorm.DB, opts CleanupOptions) error { } for _, table := range tables { + // Validate table name against whitelist before interpolation + validateTableName(table) + var query string if opts.Cascade && isPostgreSQL { // CASCADE est supporté par PostgreSQL