From 9502ba3923977b275ad8276299133e3c995ab9fc Mon Sep 17 00:00:00 2001 From: onoda Date: Sat, 20 Jun 2026 03:49:25 +0900 Subject: [PATCH 1/3] feat: opt-in CSRF protection for dashboard actions Add `config.csrf_protection_enabled` (default false) so hosts with a session store can enable Rails' CSRF protection for the dashboard's destructive POST actions, on par with Sidekiq::Web. When enabled, `verify_authenticity_token` is no longer skipped, all forms embed an authenticity token via the `csrf_token_field` helper, and the layout renders `csrf_meta_tags`. Disabled by default to keep working in session-less / API-only hosts. --- CHANGELOG.md | 6 + README.md | 24 ++++ .../solid_queue_monitor/application.js | 4 +- .../application_controller.rb | 7 +- .../solid_queue_monitor/application_helper.rb | 17 +++ .../solid_queue_monitor/application.html.erb | 1 + .../failed_jobs/_row.html.erb | 2 + .../failed_jobs/index.html.erb | 1 + .../solid_queue_monitor/jobs/_header.html.erb | 3 + .../overview/_recent_job_row.html.erb | 2 + .../queues/_job_row.html.erb | 2 + .../solid_queue_monitor/queues/_row.html.erb | 2 + .../solid_queue_monitor/queues/show.html.erb | 2 + .../scheduled_jobs/index.html.erb | 1 + .../solid_queue_monitor/workers/_row.html.erb | 1 + .../workers/index.html.erb | 2 +- .../templates/initializer.rb | 7 ++ lib/solid_queue_monitor.rb | 6 +- .../application_helper_spec.rb | 30 +++++ .../csrf_protection_spec.rb | 103 ++++++++++++++++++ 20 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 spec/requests/solid_queue_monitor/csrf_protection_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index ee947a8..aec03f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Added + +- `SolidQueueMonitor.csrf_protection_enabled` config option (default `false`). When enabled, the engine no longer skips `verify_authenticity_token`: all dashboard forms embed an `authenticity_token`, `csrf_meta_tags` are added to the layout, and unverified `POST` requests to the destructive actions (retry / discard / pause / resume / execute / reject / remove / prune) are rejected. Disabled by default for backward compatibility, since the gem does not assume the host app has a session store. See the new "CSRF Protection" section in the README for requirements. + ## [2.1.0] - 2026-05-13 ### Added diff --git a/README.md b/README.md index 3dba0ec..2999ca7 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,9 @@ SolidQueueMonitor.setup do |config| # Disable the chart on the overview page to skip chart queries entirely # config.show_chart = true + + # Enable CSRF protection for the dashboard's destructive actions (opt-in) + # config.csrf_protection_enabled = false end # Optional: inherit from a host-app controller to plug into your existing auth. @@ -140,6 +143,27 @@ If you don't need the job activity chart, disable it to skip chart queries entir config.show_chart = false ``` +### CSRF Protection + +The dashboard's destructive actions (retry, discard, pause, resume, execute, reject, remove/prune workers) are all `POST` requests. By default CSRF protection is **disabled**, because the gem does not assume the host application has a session store (it works in API-only apps without one). + +If your host app has a session store and the dashboard is mounted on the same origin, you should enable CSRF protection: + +```ruby +config.csrf_protection_enabled = true +``` + +When enabled: + +- All dashboard forms embed an `authenticity_token`, and `csrf_meta_tags` are added to the layout for JS/`fetch`-driven requests. +- Unverified `POST` requests are rejected by Rails' standard `verify_authenticity_token` (returns `422 Unprocessable Entity`). Safe methods (`GET`/`HEAD`) pass through. + +Requirements: + +- The host app has a session store configured (e.g. `config.session_store :cookie_store`). +- `config.api_only` is not enabled (or session middleware is otherwise present). +- The dashboard is mounted on the same origin as the host app, so `form_authenticity_token` works. + ### Authentication By default, Solid Queue Monitor does not require authentication to access the dashboard. This makes it easy to get started in development environments. diff --git a/app/assets/javascripts/solid_queue_monitor/application.js b/app/assets/javascripts/solid_queue_monitor/application.js index 3f73653..421d4c9 100644 --- a/app/assets/javascripts/solid_queue_monitor/application.js +++ b/app/assets/javascripts/solid_queue_monitor/application.js @@ -322,7 +322,9 @@ function bulkSubmit(action, promptMsg) { var ids = checkedBoxes().map(function (checkbox) { return checkbox.value; }); if (ids.length === 0 || !window.confirm(promptMsg)) return; - Array.prototype.slice.call(form.querySelectorAll('input[type="hidden"]')).forEach(function (input) { input.remove(); }); + // Only clear previously-appended job id inputs. Other hidden inputs + // (e.g. the CSRF authenticity_token) must be preserved. + Array.prototype.slice.call(form.querySelectorAll('input[type="hidden"][name="job_ids[]"]')).forEach(function (input) { input.remove(); }); form.action = action; ids.forEach(function (id) { appendHidden('job_ids[]', id); }); form.submit(); diff --git a/app/controllers/solid_queue_monitor/application_controller.rb b/app/controllers/solid_queue_monitor/application_controller.rb index 9a84c4a..6981c7f 100644 --- a/app/controllers/solid_queue_monitor/application_controller.rb +++ b/app/controllers/solid_queue_monitor/application_controller.rb @@ -13,7 +13,12 @@ class ApplicationController < SolidQueueMonitor.base_controller_class.safe_const before_action :authenticate, if: -> { SolidQueueMonitor::AuthenticationService.authentication_required? } layout 'solid_queue_monitor/application' - skip_before_action :verify_authenticity_token + + # CSRF protection is opt-in (config.csrf_protection_enabled). By default the + # token check is skipped so the dashboard works in hosts without a session + # store. When the host enables it, the standard verify_authenticity_token + # before_action runs and unverified POSTs are rejected. + skip_before_action :verify_authenticity_token, unless: -> { SolidQueueMonitor.csrf_protection_enabled } def set_flash_message(message, type) # Store in instance variable for access in views diff --git a/app/helpers/solid_queue_monitor/application_helper.rb b/app/helpers/solid_queue_monitor/application_helper.rb index 7efbea1..d889448 100644 --- a/app/helpers/solid_queue_monitor/application_helper.rb +++ b/app/helpers/solid_queue_monitor/application_helper.rb @@ -25,6 +25,23 @@ def message_class(type) type.to_s == 'success' ? 'message-success' : 'message-error' end + # Hidden authenticity_token field for raw HTML POST forms. + # Renders nothing unless CSRF protection is enabled, so hosts without a + # session store are unaffected (form_authenticity_token needs a session). + def csrf_token_field + return ''.html_safe unless SolidQueueMonitor.csrf_protection_enabled + + hidden_field_tag(:authenticity_token, form_authenticity_token) + end + + # CSRF meta tags for JS/fetch-driven POSTs (defense in depth). + # Only emitted when CSRF protection is enabled, for the same reason. + def csrf_meta_tags_if_enabled + return ''.html_safe unless SolidQueueMonitor.csrf_protection_enabled + + csrf_meta_tags + end + def queue_link(queue_name, css_class: nil) return '-' if queue_name.blank? diff --git a/app/views/layouts/solid_queue_monitor/application.html.erb b/app/views/layouts/solid_queue_monitor/application.html.erb index cd827b7..d0a4c23 100644 --- a/app/views/layouts/solid_queue_monitor/application.html.erb +++ b/app/views/layouts/solid_queue_monitor/application.html.erb @@ -4,6 +4,7 @@ Solid Queue Monitor - <%= content_for?(:title) ? yield(:title) : 'Dashboard' %> + <%= csrf_meta_tags_if_enabled %> <%= stylesheet_link_tag asset_url_for('application.css'), nonce: content_security_policy_nonce %>
+ <%= csrf_token_field %>
+ <%= csrf_token_field %>
diff --git a/app/views/solid_queue_monitor/failed_jobs/index.html.erb b/app/views/solid_queue_monitor/failed_jobs/index.html.erb index 976befe..93f0163 100644 --- a/app/views/solid_queue_monitor/failed_jobs/index.html.erb +++ b/app/views/solid_queue_monitor/failed_jobs/index.html.erb @@ -15,6 +15,7 @@
+ <%= csrf_token_field %> <% columns = [ { sort_key: nil, label: tag.input(type: 'checkbox', id: 'select-all', class: 'select-all-checkbox') }, { sort_key: :class_name, label: 'Job' }, diff --git a/app/views/solid_queue_monitor/jobs/_header.html.erb b/app/views/solid_queue_monitor/jobs/_header.html.erb index 5a83cc0..5a972ac 100644 --- a/app/views/solid_queue_monitor/jobs/_header.html.erb +++ b/app/views/solid_queue_monitor/jobs/_header.html.erb @@ -15,6 +15,7 @@
<% if @failed_execution %> + <%= csrf_token_field %> @@ -22,12 +23,14 @@ method="post" class="inline-form" data-confirm="Are you sure you want to discard this job?"> + <%= csrf_token_field %> <% end %> <% if @scheduled_execution %>
+ <%= csrf_token_field %>
diff --git a/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb b/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb index 4db408d..0546b55 100644 --- a/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb +++ b/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb @@ -10,6 +10,7 @@ <% if failed_execution %>
+ <%= csrf_token_field %>
@@ -17,6 +18,7 @@ action="<%= discard_failed_job_path(id: failed_execution.id) %>" class="inline-form" data-confirm="Are you sure you want to discard this job?"> + <%= csrf_token_field %> diff --git a/app/views/solid_queue_monitor/queues/_job_row.html.erb b/app/views/solid_queue_monitor/queues/_job_row.html.erb index 27bc4f1..780aa74 100644 --- a/app/views/solid_queue_monitor/queues/_job_row.html.erb +++ b/app/views/solid_queue_monitor/queues/_job_row.html.erb @@ -10,6 +10,7 @@ <% if failed_execution %>
+ <%= csrf_token_field %>
@@ -17,6 +18,7 @@ action="<%= discard_failed_job_path(id: failed_execution.id) %>" class="inline-form" data-confirm="Are you sure you want to discard this job?"> + <%= csrf_token_field %> diff --git a/app/views/solid_queue_monitor/queues/_row.html.erb b/app/views/solid_queue_monitor/queues/_row.html.erb index d5a5213..6862e35 100644 --- a/app/views/solid_queue_monitor/queues/_row.html.erb +++ b/app/views/solid_queue_monitor/queues/_row.html.erb @@ -17,6 +17,7 @@ <% if paused %>
+ <%= csrf_token_field %>
@@ -25,6 +26,7 @@ method="post" class="inline-form" data-confirm="Are you sure you want to pause the <%= queue_name %> queue? Workers will stop processing jobs from this queue."> + <%= csrf_token_field %> diff --git a/app/views/solid_queue_monitor/queues/show.html.erb b/app/views/solid_queue_monitor/queues/show.html.erb index 5fab2b6..9c80bef 100644 --- a/app/views/solid_queue_monitor/queues/show.html.erb +++ b/app/views/solid_queue_monitor/queues/show.html.erb @@ -11,12 +11,14 @@
<% if @paused %>
+ <%= csrf_token_field %>
<% else %>
+ <%= csrf_token_field %> diff --git a/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb b/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb index 5ddd374..4e2e50f 100644 --- a/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb +++ b/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb @@ -15,6 +15,7 @@
+ <%= csrf_token_field %> <% columns = [ { sort_key: nil, label: tag.input(type: 'checkbox', id: 'scheduled-jobs-select-all') }, { sort_key: :class_name, label: 'Job' }, diff --git a/app/views/solid_queue_monitor/workers/_row.html.erb b/app/views/solid_queue_monitor/workers/_row.html.erb index caf55d5..d5140f6 100644 --- a/app/views/solid_queue_monitor/workers/_row.html.erb +++ b/app/views/solid_queue_monitor/workers/_row.html.erb @@ -13,6 +13,7 @@ method="post" class="inline-form" data-confirm="Remove this dead process from the registry?"> + <%= csrf_token_field %> <% else %> diff --git a/app/views/solid_queue_monitor/workers/index.html.erb b/app/views/solid_queue_monitor/workers/index.html.erb index 1d0f924..31a70ac 100644 --- a/app/views/solid_queue_monitor/workers/index.html.erb +++ b/app/views/solid_queue_monitor/workers/index.html.erb @@ -24,7 +24,7 @@ data-confirm="Remove all <%= @summary[:dead] %> dead process<%= suffix %>? This will clean up processes that have stopped sending heartbeats."> Prune all - + <% end %>
diff --git a/lib/generators/solid_queue_monitor/templates/initializer.rb b/lib/generators/solid_queue_monitor/templates/initializer.rb index 14a0ae0..80203b0 100644 --- a/lib/generators/solid_queue_monitor/templates/initializer.rb +++ b/lib/generators/solid_queue_monitor/templates/initializer.rb @@ -27,4 +27,11 @@ # Disable the chart on the overview page to skip chart queries entirely. # config.show_chart = true + + # Enable CSRF protection for the dashboard's destructive POST actions. + # Disabled by default for backward compatibility. Requires the host app to + # have a session store (e.g. cookie_store) and the dashboard mounted on the + # same origin. When enabled, all dashboard forms embed an authenticity token + # and unverified POSTs are rejected. + # config.csrf_protection_enabled = false end diff --git a/lib/solid_queue_monitor.rb b/lib/solid_queue_monitor.rb index 9e32ffd..a078aae 100644 --- a/lib/solid_queue_monitor.rb +++ b/lib/solid_queue_monitor.rb @@ -11,7 +11,8 @@ class Error < StandardError; end class << self attr_writer :username, :password, :base_controller_class attr_accessor :jobs_per_page, :authentication_enabled, - :auto_refresh_enabled, :auto_refresh_interval, :show_chart + :auto_refresh_enabled, :auto_refresh_interval, :show_chart, + :csrf_protection_enabled def username resolve_value(@username) @@ -39,6 +40,9 @@ def resolve_value(value) @auto_refresh_enabled = true @auto_refresh_interval = 30 # seconds @show_chart = true + # Disabled by default for backward compatibility: enabling CSRF protection + # requires a session-backed host app, which the gem does not assume. + @csrf_protection_enabled = false def self.setup yield self diff --git a/spec/helpers/solid_queue_monitor/application_helper_spec.rb b/spec/helpers/solid_queue_monitor/application_helper_spec.rb index cb49269..2ba292a 100644 --- a/spec/helpers/solid_queue_monitor/application_helper_spec.rb +++ b/spec/helpers/solid_queue_monitor/application_helper_spec.rb @@ -39,6 +39,36 @@ end end + describe '#csrf_token_field' do + it 'returns an empty string when CSRF protection is disabled' do + allow(SolidQueueMonitor).to receive(:csrf_protection_enabled).and_return(false) + expect(helper.csrf_token_field).to eq('') + end + + it 'renders a hidden authenticity_token field when enabled' do + allow(SolidQueueMonitor).to receive(:csrf_protection_enabled).and_return(true) + allow(helper).to receive(:form_authenticity_token).and_return('abc123') + + result = helper.csrf_token_field + expect(result).to include('type="hidden"') + expect(result).to include('name="authenticity_token"') + expect(result).to include('value="abc123"') + end + end + + describe '#csrf_meta_tags_if_enabled' do + it 'returns an empty string when CSRF protection is disabled' do + allow(SolidQueueMonitor).to receive(:csrf_protection_enabled).and_return(false) + expect(helper.csrf_meta_tags_if_enabled).to eq('') + end + + it 'delegates to csrf_meta_tags when enabled' do + allow(SolidQueueMonitor).to receive(:csrf_protection_enabled).and_return(true) + allow(helper).to receive(:csrf_meta_tags).and_return(''.html_safe) + expect(helper.csrf_meta_tags_if_enabled).to include('name="csrf-token"') + end + end + describe '#queue_link' do it 'renders a link to the queue details page' do result = helper.queue_link('default') diff --git a/spec/requests/solid_queue_monitor/csrf_protection_spec.rb b/spec/requests/solid_queue_monitor/csrf_protection_spec.rb new file mode 100644 index 0000000..384b32f --- /dev/null +++ b/spec/requests/solid_queue_monitor/csrf_protection_spec.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require 'spec_helper' + +# Real CSRF token generation needs a working session store with a key +# generator and cookie salts. The shared request app in spec_helper wires +# session middleware but none of that, so these specs build a small Rack stack +# around the engine and merge in the host application's env config (key +# generator, secret_key_base, cookie salts). Requests still hit the engine +# directly, so paths are engine-relative (e.g. "/queues"). +RSpec.describe 'CSRF protection' do + def build_app_with_session + stack = SolidQueueMonitor::Engine + stack = ActionDispatch::Session::CookieStore.new(stack, key: '_sqm_csrf_session') + stack = ActionDispatch::Cookies.new(stack) + + lambda do |env| + stack.call(Rails.application.env_config.merge(env)) + end + end + + before { @app = build_app_with_session } + + describe 'default configuration' do + it 'is disabled by default' do + expect(SolidQueueMonitor.csrf_protection_enabled).to be(false) + end + end + + context 'when disabled (default)' do + it 'allows a destructive POST without a token' do + post '/pause_queue', params: { queue_name: 'default' } + + expect(response).to have_http_status(:found) + expect(SolidQueue::Pause.exists?(queue_name: 'default')).to be(true) + end + + it 'does not render CSRF meta tags' do + get '/queues' + + expect(response.body).not_to include('name="csrf-token"') + end + + it 'does not embed an authenticity_token in forms' do + create(:solid_queue_job, queue_name: 'default') + + get '/queues' + + expect(response.body).not_to include('name="authenticity_token"') + end + end + + context 'when enabled' do + around do |example| + SolidQueueMonitor.csrf_protection_enabled = true + original_protection = ActionController::Base.allow_forgery_protection + ActionController::Base.allow_forgery_protection = true + example.run + ensure + ActionController::Base.allow_forgery_protection = original_protection + SolidQueueMonitor.csrf_protection_enabled = false + end + + it 'lets safe (GET) requests through' do + get '/queues' + + expect(response).to have_http_status(:ok) + end + + it 'renders CSRF meta tags in the layout' do + get '/queues' + + expect(response.body).to include('name="csrf-token"') + end + + it 'embeds an authenticity_token in destructive forms' do + create(:solid_queue_job, queue_name: 'default') + + get '/queues' + + expect(response.body).to include('name="authenticity_token"') + end + + it 'rejects a destructive POST without a token' do + expect do + post '/pause_queue', params: { queue_name: 'default' } + end.to raise_error(ActionController::InvalidAuthenticityToken) + + expect(SolidQueue::Pause.exists?(queue_name: 'default')).to be(false) + end + + it 'accepts a destructive POST carrying a valid token' do + get '/queues' + token = response.body[/ Date: Mon, 22 Jun 2026 10:34:55 +0900 Subject: [PATCH 2/3] fix: exempt assets controller from CSRF to avoid cross-origin JS error --- .../solid_queue_monitor/assets_controller.rb | 4 +++ .../csrf_protection_spec.rb | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/app/controllers/solid_queue_monitor/assets_controller.rb b/app/controllers/solid_queue_monitor/assets_controller.rb index 9d03c83..95f03d7 100644 --- a/app/controllers/solid_queue_monitor/assets_controller.rb +++ b/app/controllers/solid_queue_monitor/assets_controller.rb @@ -4,6 +4,10 @@ module SolidQueueMonitor class AssetsController < ApplicationController skip_before_action :authenticate, raise: false + # Public read-only assets: exempt from CSRF so the cross-origin JavaScript + # guard doesn't reject GETs for the JS asset when csrf_protection_enabled. + skip_forgery_protection + MIME_TYPES = { '.css' => 'text/css', '.js' => 'application/javascript' }.freeze FINGERPRINT_PATTERN = /\A(?[A-Za-z0-9_]+)-(?[a-f0-9]+)(?\.css|\.js)\z/ diff --git a/spec/requests/solid_queue_monitor/csrf_protection_spec.rb b/spec/requests/solid_queue_monitor/csrf_protection_spec.rb index 384b32f..f25053e 100644 --- a/spec/requests/solid_queue_monitor/csrf_protection_spec.rb +++ b/spec/requests/solid_queue_monitor/csrf_protection_spec.rb @@ -99,5 +99,33 @@ def build_app_with_session expect(response).to have_http_status(:found) expect(SolidQueue::Pause.exists?(queue_name: 'default')).to be(true) end + + # Regression: enabling CSRF protection also turns on Rails' cross-origin + # JavaScript guard (verify_same_origin_request). Since assets are served + # from a controller, a plain GET for the JS asset would otherwise raise + # ActionController::InvalidCrossOriginRequest. Assets are public and must + # stay exempt from forgery protection. + it 'serves the JS asset without a cross-origin request error' do + SolidQueueMonitor::AssetCache.clear! + fingerprint = SolidQueueMonitor::AssetCache.fingerprint_for('application.js') + + get "/assets/application-#{fingerprint}.js" + + expect(response).to have_http_status(:ok) + expect(response.content_type).to start_with('application/javascript') + ensure + SolidQueueMonitor::AssetCache.clear! + end + + it 'serves the CSS asset' do + SolidQueueMonitor::AssetCache.clear! + fingerprint = SolidQueueMonitor::AssetCache.fingerprint_for('application.css') + + get "/assets/application-#{fingerprint}.css" + + expect(response).to have_http_status(:ok) + ensure + SolidQueueMonitor::AssetCache.clear! + end end end From b6eae16a48df11a759954f962c1a56578b169ada Mon Sep 17 00:00:00 2001 From: onoda Date: Wed, 24 Jun 2026 10:29:37 +0900 Subject: [PATCH 3/3] refactor: rename csrf_token_field to csrf_token_field_if_enabled for naming consistency --- app/helpers/solid_queue_monitor/application_helper.rb | 2 +- app/views/solid_queue_monitor/failed_jobs/_row.html.erb | 4 ++-- app/views/solid_queue_monitor/failed_jobs/index.html.erb | 2 +- app/views/solid_queue_monitor/jobs/_header.html.erb | 6 +++--- .../solid_queue_monitor/overview/_recent_job_row.html.erb | 4 ++-- app/views/solid_queue_monitor/queues/_job_row.html.erb | 4 ++-- app/views/solid_queue_monitor/queues/_row.html.erb | 4 ++-- app/views/solid_queue_monitor/queues/show.html.erb | 4 ++-- app/views/solid_queue_monitor/scheduled_jobs/index.html.erb | 2 +- app/views/solid_queue_monitor/workers/_row.html.erb | 2 +- app/views/solid_queue_monitor/workers/index.html.erb | 2 +- spec/helpers/solid_queue_monitor/application_helper_spec.rb | 6 +++--- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/helpers/solid_queue_monitor/application_helper.rb b/app/helpers/solid_queue_monitor/application_helper.rb index d889448..d4c15ed 100644 --- a/app/helpers/solid_queue_monitor/application_helper.rb +++ b/app/helpers/solid_queue_monitor/application_helper.rb @@ -28,7 +28,7 @@ def message_class(type) # Hidden authenticity_token field for raw HTML POST forms. # Renders nothing unless CSRF protection is enabled, so hosts without a # session store are unaffected (form_authenticity_token needs a session). - def csrf_token_field + def csrf_token_field_if_enabled return ''.html_safe unless SolidQueueMonitor.csrf_protection_enabled hidden_field_tag(:authenticity_token, form_authenticity_token) diff --git a/app/views/solid_queue_monitor/failed_jobs/_row.html.erb b/app/views/solid_queue_monitor/failed_jobs/_row.html.erb index a5b91c8..36449aa 100644 --- a/app/views/solid_queue_monitor/failed_jobs/_row.html.erb +++ b/app/views/solid_queue_monitor/failed_jobs/_row.html.erb @@ -13,14 +13,14 @@
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
diff --git a/app/views/solid_queue_monitor/failed_jobs/index.html.erb b/app/views/solid_queue_monitor/failed_jobs/index.html.erb index 93f0163..bd72428 100644 --- a/app/views/solid_queue_monitor/failed_jobs/index.html.erb +++ b/app/views/solid_queue_monitor/failed_jobs/index.html.erb @@ -15,7 +15,7 @@
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> <% columns = [ { sort_key: nil, label: tag.input(type: 'checkbox', id: 'select-all', class: 'select-all-checkbox') }, { sort_key: :class_name, label: 'Job' }, diff --git a/app/views/solid_queue_monitor/jobs/_header.html.erb b/app/views/solid_queue_monitor/jobs/_header.html.erb index 5a972ac..7e31814 100644 --- a/app/views/solid_queue_monitor/jobs/_header.html.erb +++ b/app/views/solid_queue_monitor/jobs/_header.html.erb @@ -15,7 +15,7 @@
<% if @failed_execution %> - <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> @@ -23,14 +23,14 @@ method="post" class="inline-form" data-confirm="Are you sure you want to discard this job?"> - <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> <% end %> <% if @scheduled_execution %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
diff --git a/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb b/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb index 0546b55..56fdac2 100644 --- a/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb +++ b/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb @@ -10,7 +10,7 @@ <% if failed_execution %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
@@ -18,7 +18,7 @@ action="<%= discard_failed_job_path(id: failed_execution.id) %>" class="inline-form" data-confirm="Are you sure you want to discard this job?"> - <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> diff --git a/app/views/solid_queue_monitor/queues/_job_row.html.erb b/app/views/solid_queue_monitor/queues/_job_row.html.erb index 780aa74..7d2ba8b 100644 --- a/app/views/solid_queue_monitor/queues/_job_row.html.erb +++ b/app/views/solid_queue_monitor/queues/_job_row.html.erb @@ -10,7 +10,7 @@ <% if failed_execution %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
@@ -18,7 +18,7 @@ action="<%= discard_failed_job_path(id: failed_execution.id) %>" class="inline-form" data-confirm="Are you sure you want to discard this job?"> - <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> diff --git a/app/views/solid_queue_monitor/queues/_row.html.erb b/app/views/solid_queue_monitor/queues/_row.html.erb index 6862e35..eb18059 100644 --- a/app/views/solid_queue_monitor/queues/_row.html.erb +++ b/app/views/solid_queue_monitor/queues/_row.html.erb @@ -17,7 +17,7 @@ <% if paused %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
@@ -26,7 +26,7 @@ method="post" class="inline-form" data-confirm="Are you sure you want to pause the <%= queue_name %> queue? Workers will stop processing jobs from this queue."> - <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> diff --git a/app/views/solid_queue_monitor/queues/show.html.erb b/app/views/solid_queue_monitor/queues/show.html.erb index 9c80bef..8cb6e77 100644 --- a/app/views/solid_queue_monitor/queues/show.html.erb +++ b/app/views/solid_queue_monitor/queues/show.html.erb @@ -11,14 +11,14 @@
<% if @paused %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %>
<% else %>
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> diff --git a/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb b/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb index 4e2e50f..d246429 100644 --- a/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb +++ b/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb @@ -15,7 +15,7 @@
- <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> <% columns = [ { sort_key: nil, label: tag.input(type: 'checkbox', id: 'scheduled-jobs-select-all') }, { sort_key: :class_name, label: 'Job' }, diff --git a/app/views/solid_queue_monitor/workers/_row.html.erb b/app/views/solid_queue_monitor/workers/_row.html.erb index d5140f6..9691e9b 100644 --- a/app/views/solid_queue_monitor/workers/_row.html.erb +++ b/app/views/solid_queue_monitor/workers/_row.html.erb @@ -13,7 +13,7 @@ method="post" class="inline-form" data-confirm="Remove this dead process from the registry?"> - <%= csrf_token_field %> + <%= csrf_token_field_if_enabled %> <% else %> diff --git a/app/views/solid_queue_monitor/workers/index.html.erb b/app/views/solid_queue_monitor/workers/index.html.erb index 31a70ac..931616b 100644 --- a/app/views/solid_queue_monitor/workers/index.html.erb +++ b/app/views/solid_queue_monitor/workers/index.html.erb @@ -24,7 +24,7 @@ data-confirm="Remove all <%= @summary[:dead] %> dead process<%= suffix %>? This will clean up processes that have stopped sending heartbeats."> Prune all - + <% end %>
diff --git a/spec/helpers/solid_queue_monitor/application_helper_spec.rb b/spec/helpers/solid_queue_monitor/application_helper_spec.rb index 2ba292a..2fe3001 100644 --- a/spec/helpers/solid_queue_monitor/application_helper_spec.rb +++ b/spec/helpers/solid_queue_monitor/application_helper_spec.rb @@ -39,17 +39,17 @@ end end - describe '#csrf_token_field' do + describe '#csrf_token_field_if_enabled' do it 'returns an empty string when CSRF protection is disabled' do allow(SolidQueueMonitor).to receive(:csrf_protection_enabled).and_return(false) - expect(helper.csrf_token_field).to eq('') + expect(helper.csrf_token_field_if_enabled).to eq('') end it 'renders a hidden authenticity_token field when enabled' do allow(SolidQueueMonitor).to receive(:csrf_protection_enabled).and_return(true) allow(helper).to receive(:form_authenticity_token).and_return('abc123') - result = helper.csrf_token_field + result = helper.csrf_token_field_if_enabled expect(result).to include('type="hidden"') expect(result).to include('name="authenticity_token"') expect(result).to include('value="abc123"')