From ca2a7d66b80ede7098cd862e0558ee67ee59de8b Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 18 Dec 2024 03:29:27 -0500
Subject: [PATCH] Fix missing value limits for `UserRole` position (#33172)

---
 app/models/user_role.rb       |  3 +++
 spec/models/user_role_spec.rb | 10 ++++++++++
 2 files changed, 13 insertions(+)

diff --git a/app/models/user_role.rb b/app/models/user_role.rb
index 815a89408..f3edbf811 100644
--- a/app/models/user_role.rb
+++ b/app/models/user_role.rb
@@ -41,6 +41,8 @@ class UserRole < ApplicationRecord
   EVERYONE_ROLE_ID = -99
   NOBODY_POSITION = -1
 
+  POSITION_LIMIT = 2**31
+
   module Flags
     NONE = 0
     ALL  = FLAGS.values.reduce(&:|)
@@ -89,6 +91,7 @@ class UserRole < ApplicationRecord
 
   validates :name, presence: true, unless: :everyone?
   validates :color, format: { with: /\A#?(?:[A-F0-9]{3}){1,2}\z/i }, unless: -> { color.blank? }
+  validates :position, numericality: { greater_than_or_equal_to: -POSITION_LIMIT, less_than_or_equal_to: POSITION_LIMIT }
 
   validate :validate_permissions_elevation
   validate :validate_position_elevation
diff --git a/spec/models/user_role_spec.rb b/spec/models/user_role_spec.rb
index 4ad7e0421..8fc09a296 100644
--- a/spec/models/user_role_spec.rb
+++ b/spec/models/user_role_spec.rb
@@ -18,6 +18,16 @@ RSpec.describe UserRole do
       end
     end
 
+    describe 'position' do
+      subject { Fabricate.build :user_role }
+
+      let(:excess) { 2**32 }
+      let(:limit) { 2**31 }
+
+      it { is_expected.to_not allow_values(-excess, excess).for(:position) }
+      it { is_expected.to allow_values(-limit, limit).for(:position) }
+    end
+
     describe 'color' do
       it { is_expected.to allow_values('#112233', '#aabbcc', '').for(:color) }
       it { is_expected.to_not allow_values('x', '112233445566', '#xxyyzz').for(:color) }