Add length validations for SHA columns to composer packages related tables

🎯 What does this MR do and why?

Add length validations for composer metadata SHA columns to prevent storing arbitrarily large data.

  • Add a 64-character length limit validation to target_sha and a 255-character length limit validation to version_cache_sha columns
  • Protect both Packages::Composer::Metadatum and Packages::Composer::Package models
  • Include both application-level validations and database-level check constraints

Changelog: changed

🗄️ Database migrations

This MR adds database-level check constraints to two tables:

packages_composer_metadata

⬆️ Up

> gdk rails db:migrate:up:main VERSION=20260203174943
main: == [advisory_lock_connection] object_id: 134200, pg_backend_pid: 43260
main: == 20260203174943 AddCheckConstraintToPackagesComposerMetadata: migrating =====
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("ALTER TABLE packages_composer_metadata\nADD CONSTRAINT check_packages_composer_metadata_target_sha_max_length\nCHECK ( octet_length(target_sha) <= 64 )\nNOT VALID;\n")
main:    -> 0.0030s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("ALTER TABLE packages_composer_metadata\nADD CONSTRAINT check_packages_composer_metadata_version_cache_sha_max_length\nCHECK ( octet_length(version_cache_sha) <= 255 )\nNOT VALID;\n")
main:    -> 0.0011s
main: == 20260203174943 AddCheckConstraintToPackagesComposerMetadata: migrated (0.0917s) 
main: == [advisory_lock_connection] object_id: 134200, pg_backend_pid: 43260

⬇️ Down

> gdk rails db:migrate:down:main VERSION=20260203174943
main: == [advisory_lock_connection] object_id: 134200, pg_backend_pid: 42335
main: == 20260203174943 AddCheckConstraintToPackagesComposerMetadata: reverting =====
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("            ALTER TABLE packages_composer_metadata\n            DROP CONSTRAINT IF EXISTS check_packages_composer_metadata_target_sha_max_length\n")
main:    -> 0.0007s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("            ALTER TABLE packages_composer_metadata\n            DROP CONSTRAINT IF EXISTS check_packages_composer_metadata_version_cache_sha_max_length\n")
main:    -> 0.0005s
main: == 20260203174943 AddCheckConstraintToPackagesComposerMetadata: reverted (0.0437s) 
main: == [advisory_lock_connection] object_id: 134200, pg_backend_pid: 42335

packages_composer_packages

⬆️ Up

> gdk rails db:migrate:up:main VERSION=20260203174944
main: == [advisory_lock_connection] object_id: 134200, pg_backend_pid: 43926
main: == 20260203174944 AddCheckConstraintToPackagesComposerPackages: migrating =====
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("ALTER TABLE packages_composer_packages\nADD CONSTRAINT check_packages_composer_packages_target_sha_max_length\nCHECK ( octet_length(target_sha) <= 64 )\nNOT VALID;\n")
main:    -> 0.0013s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("ALTER TABLE packages_composer_packages\nADD CONSTRAINT check_packages_composer_packages_version_cache_sha_max_length\nCHECK ( octet_length(version_cache_sha) <= 255 )\nNOT VALID;\n")
main:    -> 0.0011s
main: == 20260203174944 AddCheckConstraintToPackagesComposerPackages: migrated (0.1193s) 
main: == [advisory_lock_connection] object_id: 134200, pg_backend_pid: 43926

⬇️ Down

> gdk rails db:migrate:down:main VERSION=20260203174944
main: == [advisory_lock_connection] object_id: 134180, pg_backend_pid: 41324
main: == 20260203174944 AddCheckConstraintToPackagesComposerPackages: reverting =====
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("            ALTER TABLE packages_composer_packages\n            DROP CONSTRAINT IF EXISTS check_packages_composer_packages_target_sha_max_length\n")
main:    -> 0.0059s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- transaction_open?(nil)
main:    -> 0.0000s
main: -- execute("            ALTER TABLE packages_composer_packages\n            DROP CONSTRAINT IF EXISTS check_packages_composer_packages_version_cache_sha_max_length\n")
main:    -> 0.0011s
main: == 20260203174944 AddCheckConstraintToPackagesComposerPackages: reverted (0.0736s) 
main: == [advisory_lock_connection] object_id: 134180, pg_backend_pid: 41324

📚 References

  • #585485
  • Related to #368465

📸 Screenshots or screen recordings

N/A

🧪 How to set up and validate locally

  1. Open Rails console:

    gdk rails c 
  2. Test Packages::Composer::Metadatum validation:

    package_sti = FactoryBot.create(:composer_package_sti)
    metadatum = FactoryBot.create(:composer_metadatum, package: package_sti)
    metadatum.target_sha = 'A' * 65
    metadatum.valid?  # Should be false
    
    metadatum.reload
    
    metadatum.version_cache_sha = 'B' * 256
    metadatum.valid?  # Should be false
  3. Test Packages::Composer::Package validation:

    package = Packages::Composer::Package.first
    package.target_sha = 'C' * 65
    package.valid?  # Should be false
    
    package.reload
    
    package.version_cache_sha = 'D' * 256
    package.valid?  # Should be false

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Sylvia Shen

Merge request reports

Loading