inline_forms 8.1.56 → 8.1.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -0
- data/app/assets/stylesheets/inline_forms/_theme.scss +13 -0
- data/app/assets/stylesheets/inline_forms/devise.scss +4 -1
- data/app/assets/stylesheets/inline_forms/foundation_and_overrides.scss +1 -0
- data/app/assets/stylesheets/inline_forms/inline_forms.scss +35 -16
- data/app/controllers/inline_forms_controller.rb +50 -6
- data/app/helpers/inline_forms_file_trash_helper.rb +10 -0
- data/app/models/concerns/inline_forms/stored_files.rb +5 -1
- data/app/models/inline_forms/trashed_file.rb +16 -2
- data/app/views/devise/sessions/_form.html.erb +2 -2
- data/app/views/inline_forms/_list.html.erb +24 -7
- data/app/views/inline_forms/_versions.html.erb +1 -1
- data/app/views/inline_forms/_versions_file_entry.html.erb +1 -1
- data/app/views/inline_forms/_versions_list.html.erb +14 -12
- data/app/views/layouts/application.html.erb +10 -3
- data/app/views/layouts/devise.html.erb +1 -1
- data/app/views/layouts/inline_forms.html.erb +9 -2
- data/lib/inline_forms/form_elements/dropdown_helper.rb +1 -1
- data/lib/inline_forms/form_elements/file_field_helper.rb +3 -1
- data/lib/inline_forms/form_elements/info_list_helper.rb +3 -1
- data/lib/inline_forms/version.rb +1 -1
- data/lib/locales/inline_forms.en.yml +23 -1
- data/lib/locales/inline_forms.nl.yml +26 -1
- data/test/dummy/config/application.rb +11 -0
- data/test/integration/csp_nonce_test.rb +50 -0
- data/test/integration/dropdown_test.rb +17 -0
- data/test/integration/file_slots_test.rb +91 -0
- data/test/integration/list_params_test.rb +200 -0
- data/test/integration/locale_test.rb +131 -0
- data/test/integration/presentation_escaping_test.rb +80 -0
- metadata +11 -2
- data/app/views/inline_forms/_flash.html.erb +0 -13
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# The gem's own strings resolve in en and nl, also under
|
|
6
|
+
# raise_on_missing_translations (StProject switched to :nl with :strict):
|
|
7
|
+
# the create flash (`success`, misspelled `succes` in both locale files), the
|
|
8
|
+
# versions panel (was hard-coded English), the nl view keys that were missing
|
|
9
|
+
# (restore, list_versions, close_versions_list, undelete), localized version
|
|
10
|
+
# timestamps, the Devise layout's lang and the sign-in button.
|
|
11
|
+
class LocaleTest < InlineFormsIntegrationTestCase
|
|
12
|
+
KEYS = %w[
|
|
13
|
+
success
|
|
14
|
+
inline_forms.view.restore inline_forms.view.list_versions inline_forms.view.close_versions_list
|
|
15
|
+
inline_forms.view.undelete inline_forms.view.versions inline_forms.view.versions_event
|
|
16
|
+
inline_forms.view.versions_when inline_forms.view.versions_done_by inline_forms.view.versions_changeset
|
|
17
|
+
inline_forms.view.versions_old_value inline_forms.view.versions_new_value inline_forms.view.versions_empty
|
|
18
|
+
inline_forms.view.versions_rich_text inline_forms.view.version_events.create
|
|
19
|
+
inline_forms.view.version_events.update inline_forms.view.version_events.destroy
|
|
20
|
+
inline_forms.devise.login common.more common.logout inline_forms.view.create_failed
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
setup do
|
|
24
|
+
@machine = Machine.create!(name: "Press")
|
|
25
|
+
@machine.update!(name: "Press 2")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
teardown do
|
|
29
|
+
I18n.backend.reload!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Every translation the request looks up must exist: a missing one raises,
|
|
33
|
+
# in the views and in the controller (create's flash).
|
|
34
|
+
def strictly(locale)
|
|
35
|
+
original_view = ActionView::Helpers::TranslationHelper.raise_on_missing_translations
|
|
36
|
+
original_handler = I18n.exception_handler
|
|
37
|
+
original_default = I18n.default_locale
|
|
38
|
+
ActionView::Helpers::TranslationHelper.raise_on_missing_translations = true
|
|
39
|
+
I18n.exception_handler = ->(exception, *) { raise exception.respond_to?(:to_exception) ? exception.to_exception : exception }
|
|
40
|
+
# with_locale first: it restores the locale it found, so the thread's
|
|
41
|
+
# locale must not be read after default_locale changed (it would then
|
|
42
|
+
# restore :nl for every later test).
|
|
43
|
+
I18n.with_locale(locale) do
|
|
44
|
+
I18n.default_locale = locale
|
|
45
|
+
yield
|
|
46
|
+
end
|
|
47
|
+
ensure
|
|
48
|
+
I18n.default_locale = original_default
|
|
49
|
+
ActionView::Helpers::TranslationHelper.raise_on_missing_translations = original_view
|
|
50
|
+
I18n.exception_handler = original_handler
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
test "the gem's keys exist in en and nl, and the misspelled succes is gone" do
|
|
54
|
+
%i[en nl].each do |locale|
|
|
55
|
+
KEYS.each { |key| assert I18n.exists?(key, locale), "#{locale}.#{key}" }
|
|
56
|
+
refute I18n.exists?("succes", locale), "#{locale}.succes"
|
|
57
|
+
end
|
|
58
|
+
assert_equal "Machine created.", I18n.t("success", message: "Machine", locale: :en)
|
|
59
|
+
assert_equal "Machine is aangemaakt.", I18n.t("success", message: "Machine", locale: :nl)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
test "create's success flash translates in nl" do
|
|
63
|
+
# The model top bar shows t(controller_name): host-supplied, like
|
|
64
|
+
# StProject's `clients: cliënten`.
|
|
65
|
+
I18n.backend.store_translations(:nl, machines: "machines")
|
|
66
|
+
strictly(:nl) do
|
|
67
|
+
post machines_path(update: "machines_list"), params: { name: "Lathe" }, headers: frame_headers("machines_list")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
assert_response :success
|
|
71
|
+
assert_equal "Machine is aangemaakt.", controller.flash[:success]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The failure header was hard-coded Dutch with the underscored class name
|
|
75
|
+
# ("Kan machine niet aanmaken.") in every locale.
|
|
76
|
+
test "create's failure flash header translates, with the human model name" do
|
|
77
|
+
I18n.backend.store_translations(:nl, machines: "machines",
|
|
78
|
+
activerecord: { models: { machine: "Werkbank" } })
|
|
79
|
+
strictly(:nl) do
|
|
80
|
+
post machines_path(update: "machines_list"), params: { name: "" }, headers: frame_headers("machines_list")
|
|
81
|
+
end
|
|
82
|
+
assert_equal [ "Kan Werkbank niet aanmaken." ], controller.flash[:header]
|
|
83
|
+
|
|
84
|
+
I18n.backend.store_translations(:en, machines: "machines")
|
|
85
|
+
strictly(:en) do
|
|
86
|
+
post machines_path(update: "machines_list"), params: { name: "" }, headers: frame_headers("machines_list")
|
|
87
|
+
end
|
|
88
|
+
assert_equal [ "Could not create Machine." ], controller.flash[:header]
|
|
89
|
+
assert_includes response.body, "Could not create Machine."
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
test "the versions panel and list are translated, with localized timestamps" do
|
|
93
|
+
row = "machine_#{@machine.id}"
|
|
94
|
+
strictly(:nl) do
|
|
95
|
+
get machine_path(@machine, update: row), headers: frame_headers(row)
|
|
96
|
+
assert_response :success
|
|
97
|
+
assert_includes response.body, "Versies (2)"
|
|
98
|
+
|
|
99
|
+
get list_versions_machine_path(@machine, update: row), headers: frame_headers(row)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
assert_response :success
|
|
103
|
+
body = response.body
|
|
104
|
+
%w[Versies Gebeurtenis Wanneer Door Wijzigingen aangemaakt gewijzigd terugzetten].each do |text|
|
|
105
|
+
assert_includes body, text
|
|
106
|
+
end
|
|
107
|
+
refute_includes body, "<strong>Event</strong>"
|
|
108
|
+
version = @machine.versions.last
|
|
109
|
+
assert_includes body, ERB::Util.h(I18n.l(version.created_at, format: :short, locale: :nl))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
test "the versions list in en keeps its English labels" do
|
|
113
|
+
row = "machine_#{@machine.id}"
|
|
114
|
+
strictly(:en) do
|
|
115
|
+
get list_versions_machine_path(@machine, update: row), headers: frame_headers(row)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
assert_response :success
|
|
119
|
+
assert_includes response.body, "Versions (2)"
|
|
120
|
+
assert_includes response.body, "<strong>Event</strong>"
|
|
121
|
+
assert_includes response.body, "old value"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
test "the devise and application layouts declare the page language" do
|
|
125
|
+
get widgets_path
|
|
126
|
+
I18n.with_locale(:nl) do
|
|
127
|
+
assert_includes controller.render_to_string(template: "stats/show", layout: "devise"), %(<html lang="nl">)
|
|
128
|
+
assert_includes controller.render_to_string(template: "stats/show", layout: "application"), %(<html lang="nl">)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# Model data that a helper concatenates into a string it marks html_safe must
|
|
6
|
+
# be escaped first. info_list_show did `out << item._presentation`, so a
|
|
7
|
+
# _presentation built from user input (StProject: User#name listed on a Role)
|
|
8
|
+
# ran as markup on every page that showed the list (stored XSS). Same audit:
|
|
9
|
+
# file_field_show (stored file name / url) and the rich-text changeset in the
|
|
10
|
+
# versions list (was `raw`).
|
|
11
|
+
class PresentationEscapingTest < InlineFormsIntegrationTestCase
|
|
12
|
+
MARKUP = "<img src=x onerror=alert(1)>"
|
|
13
|
+
ESCAPED = "<img src=x onerror=alert(1)>"
|
|
14
|
+
|
|
15
|
+
# A view context of a real request: helpers, can?, url helpers.
|
|
16
|
+
def view
|
|
17
|
+
@view ||= begin
|
|
18
|
+
get widgets_path
|
|
19
|
+
controller.view_context
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "info_list escapes each item's _presentation" do
|
|
24
|
+
machine = Machine.create!(name: "Press")
|
|
25
|
+
Part.create!(name: MARKUP, machine: machine)
|
|
26
|
+
Part.create!(name: "Gear", machine: machine)
|
|
27
|
+
|
|
28
|
+
html = view.info_list_show(machine, :parts)
|
|
29
|
+
|
|
30
|
+
refute_includes html, "<img"
|
|
31
|
+
assert_includes html, ESCAPED
|
|
32
|
+
assert_includes html, "Gear"
|
|
33
|
+
assert_includes html, "<div class='row", "the row markup itself is kept"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test "file_field escapes the stored file name and url" do
|
|
37
|
+
widget = Widget.create!(name: "Manual")
|
|
38
|
+
stored = Struct.new(:url, :path) do
|
|
39
|
+
def present? = true
|
|
40
|
+
def to_s = url
|
|
41
|
+
end.new(%(/uploads/x'><img src=x onerror=alert(1)>.pdf), %(/uploads/#{MARKUP}.pdf))
|
|
42
|
+
widget.define_singleton_method(:manual) { stored }
|
|
43
|
+
|
|
44
|
+
html = view.file_field_show(widget, :manual)
|
|
45
|
+
|
|
46
|
+
refute_includes html, "<img"
|
|
47
|
+
assert_includes html, ESCAPED
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test "dropdown_with_other and the list rows escape _presentation" do
|
|
51
|
+
kind = Kind.create!(name: MARKUP)
|
|
52
|
+
widget = Widget.create!(name: MARKUP, kind: kind)
|
|
53
|
+
|
|
54
|
+
refute_includes view.dropdown_with_other_show(widget, :kind), "<img"
|
|
55
|
+
|
|
56
|
+
get widgets_path(update: "widgets_list"), headers: frame_headers("widgets_list")
|
|
57
|
+
assert_response :success
|
|
58
|
+
refute_includes response.body, "<img"
|
|
59
|
+
assert_includes response.body, ESCAPED
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
test "a rich-text changeset in the versions list is sanitized, not raw" do
|
|
63
|
+
machine = Machine.create!(name: "Press")
|
|
64
|
+
version = Struct.new(:id, :event, :changeset, :created_at, :whodunnit) do
|
|
65
|
+
def to_param = id.to_s
|
|
66
|
+
end.new(1, "update", { "body" => [ "<p>old</p><script>alert(1)</script>", "<p>new</p>#{MARKUP}" ] }, Time.current, nil)
|
|
67
|
+
context = view
|
|
68
|
+
context.instance_variable_set(:@object, machine)
|
|
69
|
+
context.define_singleton_method(:inline_forms_history_for) do |_object|
|
|
70
|
+
[ { version: version, kind: :rich_text, rich_text_name: "description", at: version.created_at } ]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
html = context.render(partial: "inline_forms/versions_list")
|
|
74
|
+
|
|
75
|
+
refute_includes html, "<script"
|
|
76
|
+
refute_includes html, "onerror"
|
|
77
|
+
assert_includes html, "<p>old</p>"
|
|
78
|
+
assert_includes html, "<p>new</p>"
|
|
79
|
+
end
|
|
80
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inline_forms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.
|
|
4
|
+
version: 8.1.58
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ace Suares
|
|
@@ -153,7 +153,6 @@ files:
|
|
|
153
153
|
- app/views/inline_forms/_close.html.erb
|
|
154
154
|
- app/views/inline_forms/_edit.html.erb
|
|
155
155
|
- app/views/inline_forms/_file_slot.html.erb
|
|
156
|
-
- app/views/inline_forms/_flash.html.erb
|
|
157
156
|
- app/views/inline_forms/_generic_search.html.erb
|
|
158
157
|
- app/views/inline_forms/_header.html.erb
|
|
159
158
|
- app/views/inline_forms/_list.html.erb
|
|
@@ -314,16 +313,21 @@ files:
|
|
|
314
313
|
- test/inline_edit_polymorphic_path_test.rb
|
|
315
314
|
- test/inline_forms_addto_generator_test.rb
|
|
316
315
|
- test/inline_forms_generator_test.rb
|
|
316
|
+
- test/integration/csp_nonce_test.rb
|
|
317
|
+
- test/integration/dropdown_test.rb
|
|
317
318
|
- test/integration/field_authorization_test.rb
|
|
318
319
|
- test/integration/file_slots_test.rb
|
|
319
320
|
- test/integration/html_refusal_test.rb
|
|
320
321
|
- test/integration/inline_forms_crud_test.rb
|
|
321
322
|
- test/integration/layout_title_test.rb
|
|
322
323
|
- test/integration/legacy_elements_test.rb
|
|
324
|
+
- test/integration/list_params_test.rb
|
|
325
|
+
- test/integration/locale_test.rb
|
|
323
326
|
- test/integration/model_top_bar_test.rb
|
|
324
327
|
- test/integration/native_inputs_test.rb
|
|
325
328
|
- test/integration/open_after_create_test.rb
|
|
326
329
|
- test/integration/pending_migration_gate_test.rb
|
|
330
|
+
- test/integration/presentation_escaping_test.rb
|
|
327
331
|
- test/integration/revert_skipped_columns_test.rb
|
|
328
332
|
- test/integration/schema_batch_pipeline_test.rb
|
|
329
333
|
- test/integration/schema_edit_test.rb
|
|
@@ -398,16 +402,21 @@ test_files:
|
|
|
398
402
|
- test/inline_edit_polymorphic_path_test.rb
|
|
399
403
|
- test/inline_forms_addto_generator_test.rb
|
|
400
404
|
- test/inline_forms_generator_test.rb
|
|
405
|
+
- test/integration/csp_nonce_test.rb
|
|
406
|
+
- test/integration/dropdown_test.rb
|
|
401
407
|
- test/integration/field_authorization_test.rb
|
|
402
408
|
- test/integration/file_slots_test.rb
|
|
403
409
|
- test/integration/html_refusal_test.rb
|
|
404
410
|
- test/integration/inline_forms_crud_test.rb
|
|
405
411
|
- test/integration/layout_title_test.rb
|
|
406
412
|
- test/integration/legacy_elements_test.rb
|
|
413
|
+
- test/integration/list_params_test.rb
|
|
414
|
+
- test/integration/locale_test.rb
|
|
407
415
|
- test/integration/model_top_bar_test.rb
|
|
408
416
|
- test/integration/native_inputs_test.rb
|
|
409
417
|
- test/integration/open_after_create_test.rb
|
|
410
418
|
- test/integration/pending_migration_gate_test.rb
|
|
419
|
+
- test/integration/presentation_escaping_test.rb
|
|
411
420
|
- test/integration/revert_skipped_columns_test.rb
|
|
412
421
|
- test/integration/schema_batch_pipeline_test.rb
|
|
413
422
|
- test/integration/schema_edit_test.rb
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<% unless flash.empty? %>
|
|
2
|
-
<% identifier = Time.now.to_i.to_s %>
|
|
3
|
-
<div class="row" id="flash_<%= identifier %>">
|
|
4
|
-
<% flash.each do |key, value| %>
|
|
5
|
-
<%= value %>
|
|
6
|
-
<% end %>
|
|
7
|
-
<script>
|
|
8
|
-
$("#<%= "#{update_span}_auto_header" %>").ScrollTo();
|
|
9
|
-
$("#flash_<%= identifier %>").delay(1000).fadeToggle('2000');
|
|
10
|
-
</script>
|
|
11
|
-
</div>
|
|
12
|
-
<% end %>
|
|
13
|
-
|