Skip to content

Commit d72090e

Browse files
committed
Fix XSS vulnerability in nested forms
1 parent 4ef2ea4 commit d72090e

File tree

4 files changed

+50
-5
lines changed

4 files changed

+50
-5
lines changed

app/assets/javascripts/rails_admin/ra.nested-form-hooks.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
$(document).on('nested:fieldAdded', 'form', function(content) {
1212
var controls, field, nav, new_tab, one_to_one, parent_group, toggler;
1313
field = content.field.addClass('tab-pane').attr('id', 'unique-id-' + (new Date().getTime()));
14-
new_tab = $('<li><a data-toggle="tab" href="#' + field.attr('id') + '">' + field.children('.object-infos').data('object-label') + '</a></li>');
14+
new_tab = $('<li></li>').append(
15+
$('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + field.attr('id')).text(field.children('.object-infos').data('object-label'))
16+
)
1517
parent_group = field.closest('.control-group');
1618
controls = parent_group.children('.controls');
1719
one_to_one = controls.data('nestedone') !== void 0;
@@ -27,7 +29,7 @@
2729
content.select(':hidden').show('slow');
2830
toggler.addClass('active').removeClass('disabled').children('i').addClass('icon-chevron-down').removeClass('icon-chevron-right');
2931
if (one_to_one) {
30-
controls.find('.add_nested_fields').removeClass('add_nested_fields').html(field.children('.object-infos').data('object-label'));
32+
controls.find('.add_nested_fields').removeClass('add_nested_fields').text(field.children('.object-infos').data('object-label'));
3133
}
3234
});
3335

app/assets/javascripts/rails_admin/ra.widgets.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@
137137
toggler = field.find('> .controls > .btn-group > .toggler');
138138
tab_content.children('.fields:not(.tab-pane)').addClass('tab-pane').each(function() {
139139
$(this).attr('id', 'unique-id-' + (new Date().getTime()) + Math.floor(Math.random() * 100000));
140-
nav.append('<li><a data-toggle="tab" href="#' + this.id + '">' + $(this).children('.object-infos').data('object-label') + '</a></li>');
140+
nav.append(
141+
$('<li></li>').append(
142+
$('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + this.id).text($(this).children('.object-infos').data('object-label'))
143+
)
144+
);
141145
});
142146
if (nav.find("> li.active").length === 0) {
143147
nav.find("> li > a[data-toggle='tab']:first").tab('show');
@@ -165,8 +169,12 @@
165169
tab_content = field.find("> .tab-content");
166170
toggler = field.find('> .controls > .btn-group > .toggler');
167171
tab_content.children(".fields:not(.tab-pane)").addClass('tab-pane active').each(function() {
168-
field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').html($(this).children('.object-infos').data('object-label'));
169-
nav.append('<li><a data-toggle="tab" href="#' + this.id + '">' + $(this).children('.object-infos').data('object-label') + '</a></li>');
172+
field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').text($(this).children('.object-infos').data('object-label'));
173+
nav.append(
174+
$('<li></li>').append(
175+
$('<a></a>').attr('data-toggle', 'tab').attr('href', '#' + this.id).text($(this).children('.object-infos').data('object-label'))
176+
)
177+
);
170178
});
171179
first_tab = nav.find("> li > a[data-toggle='tab']:first");
172180
first_tab.tab('show');

spec/integration/fields/has_many_association_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,22 @@
211211
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')
212212
end
213213
end
214+
215+
context 'when XSS attack is attempted', js: true do
216+
it 'does not break on adding a new item' do
217+
allow(I18n).to receive(:t).and_call_original
218+
expect(I18n).to receive(:t).with('admin.form.new_model', name: 'Nested field test').and_return('<script>throw "XSS";</script>')
219+
@record = FactoryBot.create :field_test
220+
visit edit_path(model_name: 'field_test', id: @record.id)
221+
find('#field_test_nested_field_tests_attributes_field .add_nested_fields').click
222+
end
223+
224+
it 'does not break on editing an existing item' do
225+
@record = FactoryBot.create :field_test
226+
NestedFieldTest.create! title: '<script>throw "XSS";</script>', field_test: @record
227+
visit edit_path(model_name: 'field_test', id: @record.id)
228+
end
229+
end
214230
end
215231

216232
context 'with not nullable foreign key', active_record: true do

spec/integration/fields/has_one_assosiation_spec.rb renamed to spec/integration/fields/has_one_association_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,25 @@
9191
@record.reload
9292
expect(@record.comment).to be_nil
9393
end
94+
95+
context 'when XSS attack is attempted', js: true do
96+
it 'does not break on adding a new item' do
97+
allow(I18n).to receive(:t).and_call_original
98+
expect(I18n).to receive(:t).with('admin.form.new_model', name: 'Comment').and_return('<script>throw "XSS";</script>')
99+
@record = FactoryBot.create :field_test
100+
visit edit_path(model_name: 'field_test', id: @record.id)
101+
find('#field_test_comment_attributes_field .add_nested_fields').click
102+
end
103+
104+
it 'does not break on adding an existing item' do
105+
RailsAdmin.config Comment do
106+
object_label_method :content
107+
end
108+
@record = FactoryBot.create :field_test
109+
FactoryBot.create :comment, content: '<script>throw "XSS";</script>', commentable: @record
110+
visit edit_path(model_name: 'field_test', id: @record.id)
111+
end
112+
end
94113
end
95114

96115
context 'with custom primary_key option' do

0 commit comments

Comments
 (0)