From 1925e42264633b6083698ef40a7a67e7124d032e Mon Sep 17 00:00:00 2001
From: Michael Josephson <30024+mikej@users.noreply.github.com>
Date: Wed, 21 Jan 2026 14:43:02 +0000
Subject: [PATCH 1/3] prevent potential XSS in flash messages
flash messages were being assumed to be HTML-safe which feels too
fragile as it relies on every developer remembering to escape any
user input that's included in flash messages.
fix by removing the `.html_safe` from the messages partial.
the only place that seems to be relying on this behaviour is in
`MeetingsController` where validation errors are joined into a
single flash message using
tags. Updated this to just join the
messages with commas instead.
---
app/controllers/admin/meetings_controller.rb | 4 ++--
app/views/layouts/_messages.html.haml | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/controllers/admin/meetings_controller.rb b/app/controllers/admin/meetings_controller.rb
index 8a21c1715..c66aec006 100644
--- a/app/controllers/admin/meetings_controller.rb
+++ b/app/controllers/admin/meetings_controller.rb
@@ -13,7 +13,7 @@ def create
if @meeting.save
redirect_to [:admin, @meeting], notice: t('admin.messages.meeting.created')
else
- flash[:notice] = @meeting.errors.full_messages.join('
')
+ flash[:notice] = @meeting.errors.full_messages.join(', ')
render :new
end
end
@@ -33,7 +33,7 @@ def update
if @meeting.update(meeting_params)
redirect_to [:admin, @meeting], notice: t('admin.messages.meeting.updated')
else
- flash[:notice] = @meeting.errors.full_messages.join('
')
+ flash[:notice] = @meeting.errors.full_messages.join(', ')
render 'edit'
end
end
diff --git a/app/views/layouts/_messages.html.haml b/app/views/layouts/_messages.html.haml
index 74cd2e569..9f0df4b93 100644
--- a/app/views/layouts/_messages.html.haml
+++ b/app/views/layouts/_messages.html.haml
@@ -3,11 +3,11 @@
- name = name.eql?('notice') ? 'info' : name
- if msg.is_a?(String)
.alert.alert-dismissible.fade.show.mb-0{ 'data-alert': '', class: "alert-#{name}", role: 'alert' }
- = content_tag :div, msg.html_safe
+ = content_tag :div, msg
%button.btn-close{ type: 'button', 'data-bs-dismiss': 'alert', 'aria-label': 'Close' }
- elsif msg.is_a?(Array)
- msg.each do |message|
.alert.alert-dismissible.fade.show.mb-0{ 'data-alert': '', class: "alert-#{name}", role: 'alert' }
- = content_tag :span, message.html_safe
+ = content_tag :span, message
%button.btn-close{ type: 'button', 'data-bs-dismiss': 'alert', 'aria-label': 'Close' }
From cbd78da441af449e2e63fa709765dea4d41a5396 Mon Sep 17 00:00:00 2001
From: Michael Josephson <30024+mikej@users.noreply.github.com>
Date: Wed, 21 Jan 2026 14:49:02 +0000
Subject: [PATCH 2/3] prevent duplicate validation error when venue is blank
venue gets validation as part of its belongs_to association which is
required by default. this resulted in 2 validation error messages
being shown:
"Venue must be set" and "Venue can't be blank".
remove the presence validator so we now get just "Venue must be set"
---
app/models/meeting.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/models/meeting.rb b/app/models/meeting.rb
index 7c78ccdaf..ce0a3eae6 100644
--- a/app/models/meeting.rb
+++ b/app/models/meeting.rb
@@ -12,7 +12,7 @@ class Meeting < ApplicationRecord
has_many :invitations, class_name: 'MeetingInvitation'
has_and_belongs_to_many :chapters
- validates :date_and_time, :ends_at, :venue, presence: true
+ validates :date_and_time, :ends_at, presence: true
validates :slug, uniqueness: true, if: proc { |model| model.slug.present? }
before_validation :set_date_and_time, :set_end_date_and_time
From 81c79825240307bde52f3fb1d7dc46f5aa52fde1 Mon Sep 17 00:00:00 2001
From: Michael Josephson <30024+mikej@users.noreply.github.com>
Date: Wed, 21 Jan 2026 15:03:20 +0000
Subject: [PATCH 3/3] update failing specs for meeting validation
update meeting specs to reflect removal of duplicate validation in
cbd78da4
---
spec/features/admin/meeting_spec.rb | 2 +-
spec/models/meeting_spec.rb | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/spec/features/admin/meeting_spec.rb b/spec/features/admin/meeting_spec.rb
index 3eb7ed243..a54caf611 100644
--- a/spec/features/admin/meeting_spec.rb
+++ b/spec/features/admin/meeting_spec.rb
@@ -31,7 +31,7 @@
click_on 'Save'
- expect(page).to have_content('Venue can\'t be blank')
+ expect(page).to have_content('Venue must be set')
end
end
diff --git a/spec/models/meeting_spec.rb b/spec/models/meeting_spec.rb
index 00490e1af..b7895fcbb 100644
--- a/spec/models/meeting_spec.rb
+++ b/spec/models/meeting_spec.rb
@@ -6,7 +6,7 @@
subject(:meeting) { Fabricate(:meeting) }
it { is_expected.to validate_presence_of(:date_and_time) }
it { is_expected.to validate_presence_of(:ends_at) }
- it { is_expected.to validate_presence_of(:venue) }
+ it { should belong_to(:venue) }
context '#slug' do
it 'fails when slug not present' do