CVE-2020-36190
Description
RailsAdmin (aka rails_admin) before 1.4.3 and 2.x before 2.0.2 allows XSS via nested forms.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
RailsAdmin before 1.4.3 and 2.x before 2.0.2 contains a cross-site scripting (XSS) vulnerability in nested forms via unsanitized object labels.
Vulnerability
Overview
CVE-2020-36190 is a cross-site scripting (XSS) vulnerability in RailsAdmin, a popular administration interface for Ruby on Rails applications. The flaw exists in the handling of nested forms, where user-controlled object labels are inserted into the DOM without proper sanitization. Specifically, the code used .html() to set the label text, allowing an attacker to inject arbitrary HTML and JavaScript [2]. This affects all versions before 1.4.3 and 2.x before 2.0.2 [1][3].
Exploitation
An attacker can exploit this vulnerability by crafting a malicious object label that contains JavaScript code. When a user interacts with a nested form (e.g., adding or viewing nested records), the injected script executes in the context of the victim's browser. The attack requires the ability to create or edit records that use nested forms, which typically implies administrative or privileged access to the RailsAdmin interface [4]. No authentication bypass is needed; the attacker must have legitimate access to the admin panel.
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the browser of any user viewing the affected nested form. This can lead to session hijacking, defacement, data theft, or further attacks against the admin interface. The vulnerability is classified as medium severity (CVSS 6.1) due to the requirement for authenticated access and user interaction [3].
Mitigation
The vulnerability was fixed in RailsAdmin versions 1.4.3 and 2.0.2. The fix replaces .html() with .text() when setting the object label, ensuring that user input is treated as text rather than HTML [2]. Users are strongly advised to upgrade to the latest patched version. No workarounds are documented; upgrading is the recommended course of action [1][4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
rails_adminRubyGems | < 1.4.3 | 1.4.3 |
rails_adminRubyGems | >= 2.0.0, < 2.0.2 | 2.0.2 |
Affected products
2- RailsAdmin/RailsAdmindescription
Patches
43 files changed · +19 −2
CHANGELOG.md+12 −1 modified@@ -2,7 +2,18 @@ ## [Unreleased](https://github.com/sferik/rails_admin/tree/HEAD) -[Full Changelog](https://github.com/sferik/rails_admin/compare/v2.0.1...HEAD) +[Full Changelog](https://github.com/sferik/rails_admin/compare/v2.0.2...HEAD) + + +## [2.0.2](https://github.com/sferik/rails_admin/tree/v2.0.2) - 2020-03-17 + +[Full Changelog](https://github.com/sferik/rails_admin/compare/v2.0.1...v2.0.2) + +### Fixed +- Fix to use I18n to translate the button 'Reset filters'([#3248](https://github.com/sferik/rails_admin/pull/3248)) + +### Security +- Fix XSS vulnerability in nested forms([d72090ec](https://github.com/sferik/rails_admin/commit/d72090ec6a07c3b9b7b48ab50f3d405f91ff4375)) ## [2.0.1](https://github.com/sferik/rails_admin/tree/v2.0.1) - 2019-12-31
lib/rails_admin/version.rb+1 −1 modified@@ -2,7 +2,7 @@ module RailsAdmin class Version MAJOR = 2 MINOR = 0 - PATCH = 1 + PATCH = 2 PRE = nil class << self
README.md+6 −0 modified@@ -16,6 +16,12 @@ RailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data. +## Announcements + +### [Action required] Security issue + +**RailsAdmin 2.0.1, 2.0.0 and up to 1.4.2 have been reported to have XSS vulnerability.** We strongly recommend that you upgrade RailsAdmin to 2.0.2 (and higher) or 1.4.3 as soon as possible, if you are on those versions. See [d72090ec](https://github.com/sferik/rails_admin/commit/d72090ec6a07c3b9b7b48ab50f3d405f91ff4375) for the detail. + ## Getting started * Check out [the docs][docs].
2 files changed · +20 −3
CHANGELOG.md+19 −2 modified@@ -1,8 +1,25 @@ # RailsAdmin Changelog -## [Unreleased](https://github.com/sferik/rails_admin/tree/HEAD) +## [Unreleased](https://github.com/sferik/rails_admin/tree/1.x-stable) -[Full Changelog](https://github.com/sferik/rails_admin/compare/v1.4.2...HEAD) +[Full Changelog](https://github.com/sferik/rails_admin/compare/v1.4.3...1.x-stable) + + +## [1.4.3](https://github.com/sferik/rails_admin/tree/v1.4.3) - 2020-03-17 + +[Full Changelog](https://github.com/sferik/rails_admin/compare/v1.4.2...v1.4.3) + +### Fixed +- Fix NoMethodError when used with Rails 6.0 ([#3122](https://github.com/sferik/rails_admin/pull/3122)) +- Fix wrong styles when using multiple instances of CodeMirror([#3107](https://github.com/sferik/rails_admin/pull/3107)) +- Fix password being cleared when used with Devise 4.6([72bc0373](https://github.com/sferik/rails_admin/commit/72bc03736162ffef8e5b99f42ca605d17fe7e7d0)) +- ActiveStorage factory caused const missing for Mongoid([#3088](https://github.com/sferik/rails_admin/pull/3088), [db927687](https://github.com/sferik/rails_admin/commit/db9276879c8e8c5e8772261725ef0e0cdadd9cf1)) +- Fix exact matches were using LIKE, which was not index-friendly([#3000](https://github.com/sferik/rails_admin/pull/3000)) +- Middleware check failed when using RedisStore([#3076](https://github.com/sferik/rails_admin/issues/3076)) +- Fix field being reset to default after an error([#3066](https://github.com/sferik/rails_admin/pull/3066)) + +### Security +- Fix XSS vulnerability in nested forms([872a637b](https://github.com/sferik/rails_admin/commit/872a637b118e0607eccd8b0b3b94e5f6f9db6758)) ## [1.4.2](https://github.com/sferik/rails_admin/tree/v1.4.2) - 2018-09-23
lib/rails_admin/version.rb+1 −1 modified@@ -2,7 +2,7 @@ module RailsAdmin class Version MAJOR = 1 MINOR = 4 - PATCH = 2 + PATCH = 3 PRE = nil class << self
d72090ec6a07Fix XSS vulnerability in nested forms
4 files changed · +50 −5
app/assets/javascripts/rails_admin/ra.nested-form-hooks.js+4 −2 modified@@ -11,7 +11,9 @@ $(document).on('nested:fieldAdded', 'form', function(content) { var controls, field, nav, new_tab, one_to_one, parent_group, toggler; field = content.field.addClass('tab-pane').attr('id', 'unique-id-' + (new Date().getTime())); - new_tab = $('<li><a data-toggle="tab" href="#' + field.attr('id') + '">' + field.children('.object-infos').data('object-label') + '</a></li>'); + new_tab = $('<li></li>').append( + $('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + field.attr('id')).text(field.children('.object-infos').data('object-label')) + ) parent_group = field.closest('.control-group'); controls = parent_group.children('.controls'); one_to_one = controls.data('nestedone') !== void 0; @@ -27,7 +29,7 @@ content.select(':hidden').show('slow'); toggler.addClass('active').removeClass('disabled').children('i').addClass('icon-chevron-down').removeClass('icon-chevron-right'); if (one_to_one) { - controls.find('.add_nested_fields').removeClass('add_nested_fields').html(field.children('.object-infos').data('object-label')); + controls.find('.add_nested_fields').removeClass('add_nested_fields').text(field.children('.object-infos').data('object-label')); } });
app/assets/javascripts/rails_admin/ra.widgets.js+11 −3 modified@@ -137,7 +137,11 @@ toggler = field.find('> .controls > .btn-group > .toggler'); tab_content.children('.fields:not(.tab-pane)').addClass('tab-pane').each(function() { $(this).attr('id', 'unique-id-' + (new Date().getTime()) + Math.floor(Math.random() * 100000)); - nav.append('<li><a data-toggle="tab" href="#' + this.id + '">' + $(this).children('.object-infos').data('object-label') + '</a></li>'); + nav.append( + $('<li></li>').append( + $('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + this.id).text($(this).children('.object-infos').data('object-label')) + ) + ); }); if (nav.find("> li.active").length === 0) { nav.find("> li > a[data-toggle='tab']:first").tab('show'); @@ -165,8 +169,12 @@ tab_content = field.find("> .tab-content"); toggler = field.find('> .controls > .btn-group > .toggler'); tab_content.children(".fields:not(.tab-pane)").addClass('tab-pane active').each(function() { - field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').html($(this).children('.object-infos').data('object-label')); - nav.append('<li><a data-toggle="tab" href="#' + this.id + '">' + $(this).children('.object-infos').data('object-label') + '</a></li>'); + field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').text($(this).children('.object-infos').data('object-label')); + nav.append( + $('<li></li>').append( + $('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + this.id).text($(this).children('.object-infos').data('object-label')) + ) + ); }); first_tab = nav.find("> li > a[data-toggle='tab']:first"); first_tab.tab('show');
spec/integration/fields/has_many_association_spec.rb+16 −0 modified@@ -211,6 +211,22 @@ expect(page.body).to include('field_test_nested_field_tests_attributes_new_nested_field_tests_deeply_nested_field_tests_attributes_new_deeply_nested_field_tests_title') end end + + context 'when XSS attack is attempted', js: true do + it 'does not break on adding a new item' do + allow(I18n).to receive(:t).and_call_original + expect(I18n).to receive(:t).with('admin.form.new_model', name: 'Nested field test').and_return('<script>throw "XSS";</script>') + @record = FactoryBot.create :field_test + visit edit_path(model_name: 'field_test', id: @record.id) + find('#field_test_nested_field_tests_attributes_field .add_nested_fields').click + end + + it 'does not break on editing an existing item' do + @record = FactoryBot.create :field_test + NestedFieldTest.create! title: '<script>throw "XSS";</script>', field_test: @record + visit edit_path(model_name: 'field_test', id: @record.id) + end + end end context 'with not nullable foreign key', active_record: true do
spec/integration/fields/has_one_association_spec.rb+19 −0 renamed@@ -91,6 +91,25 @@ @record.reload expect(@record.comment).to be_nil end + + context 'when XSS attack is attempted', js: true do + it 'does not break on adding a new item' do + allow(I18n).to receive(:t).and_call_original + expect(I18n).to receive(:t).with('admin.form.new_model', name: 'Comment').and_return('<script>throw "XSS";</script>') + @record = FactoryBot.create :field_test + visit edit_path(model_name: 'field_test', id: @record.id) + find('#field_test_comment_attributes_field .add_nested_fields').click + end + + it 'does not break on adding an existing item' do + RailsAdmin.config Comment do + object_label_method :content + end + @record = FactoryBot.create :field_test + FactoryBot.create :comment, content: '<script>throw "XSS";</script>', commentable: @record + visit edit_path(model_name: 'field_test', id: @record.id) + end + end end context 'with custom primary_key option' do
d72090ec6a07Fix XSS vulnerability in nested forms
4 files changed · +50 −5
app/assets/javascripts/rails_admin/ra.nested-form-hooks.js+4 −2 modified@@ -11,7 +11,9 @@ $(document).on('nested:fieldAdded', 'form', function(content) { var controls, field, nav, new_tab, one_to_one, parent_group, toggler; field = content.field.addClass('tab-pane').attr('id', 'unique-id-' + (new Date().getTime())); - new_tab = $('<li><a data-toggle="tab" href="#' + field.attr('id') + '">' + field.children('.object-infos').data('object-label') + '</a></li>'); + new_tab = $('<li></li>').append( + $('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + field.attr('id')).text(field.children('.object-infos').data('object-label')) + ) parent_group = field.closest('.control-group'); controls = parent_group.children('.controls'); one_to_one = controls.data('nestedone') !== void 0; @@ -27,7 +29,7 @@ content.select(':hidden').show('slow'); toggler.addClass('active').removeClass('disabled').children('i').addClass('icon-chevron-down').removeClass('icon-chevron-right'); if (one_to_one) { - controls.find('.add_nested_fields').removeClass('add_nested_fields').html(field.children('.object-infos').data('object-label')); + controls.find('.add_nested_fields').removeClass('add_nested_fields').text(field.children('.object-infos').data('object-label')); } });
app/assets/javascripts/rails_admin/ra.widgets.js+11 −3 modified@@ -137,7 +137,11 @@ toggler = field.find('> .controls > .btn-group > .toggler'); tab_content.children('.fields:not(.tab-pane)').addClass('tab-pane').each(function() { $(this).attr('id', 'unique-id-' + (new Date().getTime()) + Math.floor(Math.random() * 100000)); - nav.append('<li><a data-toggle="tab" href="#' + this.id + '">' + $(this).children('.object-infos').data('object-label') + '</a></li>'); + nav.append( + $('<li></li>').append( + $('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + this.id).text($(this).children('.object-infos').data('object-label')) + ) + ); }); if (nav.find("> li.active").length === 0) { nav.find("> li > a[data-toggle='tab']:first").tab('show'); @@ -165,8 +169,12 @@ tab_content = field.find("> .tab-content"); toggler = field.find('> .controls > .btn-group > .toggler'); tab_content.children(".fields:not(.tab-pane)").addClass('tab-pane active').each(function() { - field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').html($(this).children('.object-infos').data('object-label')); - nav.append('<li><a data-toggle="tab" href="#' + this.id + '">' + $(this).children('.object-infos').data('object-label') + '</a></li>'); + field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').text($(this).children('.object-infos').data('object-label')); + nav.append( + $('<li></li>').append( + $('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + this.id).text($(this).children('.object-infos').data('object-label')) + ) + ); }); first_tab = nav.find("> li > a[data-toggle='tab']:first"); first_tab.tab('show');
spec/integration/fields/has_many_association_spec.rb+16 −0 modified@@ -211,6 +211,22 @@ expect(page.body).to include('field_test_nested_field_tests_attributes_new_nested_field_tests_deeply_nested_field_tests_attributes_new_deeply_nested_field_tests_title') end end + + context 'when XSS attack is attempted', js: true do + it 'does not break on adding a new item' do + allow(I18n).to receive(:t).and_call_original + expect(I18n).to receive(:t).with('admin.form.new_model', name: 'Nested field test').and_return('<script>throw "XSS";</script>') + @record = FactoryBot.create :field_test + visit edit_path(model_name: 'field_test', id: @record.id) + find('#field_test_nested_field_tests_attributes_field .add_nested_fields').click + end + + it 'does not break on editing an existing item' do + @record = FactoryBot.create :field_test + NestedFieldTest.create! title: '<script>throw "XSS";</script>', field_test: @record + visit edit_path(model_name: 'field_test', id: @record.id) + end + end end context 'with not nullable foreign key', active_record: true do
spec/integration/fields/has_one_association_spec.rb+19 −0 renamed@@ -91,6 +91,25 @@ @record.reload expect(@record.comment).to be_nil end + + context 'when XSS attack is attempted', js: true do + it 'does not break on adding a new item' do + allow(I18n).to receive(:t).and_call_original + expect(I18n).to receive(:t).with('admin.form.new_model', name: 'Comment').and_return('<script>throw "XSS";</script>') + @record = FactoryBot.create :field_test + visit edit_path(model_name: 'field_test', id: @record.id) + find('#field_test_comment_attributes_field .add_nested_fields').click + end + + it 'does not break on adding an existing item' do + RailsAdmin.config Comment do + object_label_method :content + end + @record = FactoryBot.create :field_test + FactoryBot.create :comment, content: '<script>throw "XSS";</script>', commentable: @record + visit edit_path(model_name: 'field_test', id: @record.id) + end + end end context 'with custom primary_key option' do
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-wjx2-7hqq-8h7mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-36190ghsaADVISORY
- github.com/rubysec/ruby-advisory-db/blob/master/gems/rails_admin/CVE-2020-36190.ymlghsaWEB
- github.com/sferik/rails_admin/blob/master/README.mdghsax_refsource_MISCWEB
- github.com/sferik/rails_admin/commit/d72090ec6a07c3b9b7b48ab50f3d405f91ff4375ghsax_refsource_MISCWEB
- github.com/sferik/rails_admin/compare/v1.4.2...v1.4.3ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.