view_component 4.8.0 → 4.10.0
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/app/controllers/view_components_system_test_controller.rb +12 -1
- data/docs/CHANGELOG.md +24 -0
- data/lib/view_component/base.rb +17 -0
- data/lib/view_component/compiler.rb +2 -2
- data/lib/view_component/config.rb +4 -0
- data/lib/view_component/engine.rb +5 -1
- data/lib/view_component/preview.rb +2 -0
- data/lib/view_component/slot.rb +0 -1
- data/lib/view_component/system_test_helpers.rb +1 -0
- data/lib/view_component/test_helpers.rb +11 -9
- data/lib/view_component/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: af6c301478d5448d3784a0dd502e0baee242ee766a48aa0a9615aa1612bd9ceb
|
|
4
|
+
data.tar.gz: e58be6763cb626c356f002a87d354297471eed752a912315b5fcd885f66b5652
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58728c661bc715b65307ecce22fcd97552be486e788be3e3fedd7f308c214ab318d5a92fdf71c43330f376b134c0c6ae4f3588f1e3685eb0796aa7fead47da8e
|
|
7
|
+
data.tar.gz: 845b3ffbd2fcfe4732f0970867568de978152a2fc3360b8b8b0d2038ee3dc03df0b7e0784e3cefc9b96c5cc57672fe56e5917ce7f072efd4f68c218cd843cc6d
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "view_component/errors"
|
|
4
|
+
|
|
3
5
|
class ViewComponentsSystemTestController < ActionController::Base # :nodoc:
|
|
4
6
|
if Rails.env.test?
|
|
5
7
|
before_action :validate_file_path
|
|
@@ -8,18 +10,27 @@ class ViewComponentsSystemTestController < ActionController::Base # :nodoc:
|
|
|
8
10
|
@_tmpdir ||= FileUtils.mkdir_p("./tmp/view_components/").first
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
rescue_from ViewComponent::SystemTestControllerNefariousPathError, with: :render_not_found
|
|
14
|
+
|
|
11
15
|
def system_test_entrypoint
|
|
12
16
|
render file: @path
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
private
|
|
16
20
|
|
|
21
|
+
def render_not_found
|
|
22
|
+
head :not_found
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
# Ensure that the file path is valid and doesn't target files outside
|
|
18
26
|
# the expected directory (e.g. via a path traversal or symlink attack)
|
|
19
27
|
def validate_file_path
|
|
20
28
|
base_path = ::File.realpath(self.class.temp_dir)
|
|
21
29
|
@path = ::File.realpath(params.permit(:file)[:file], base_path)
|
|
22
|
-
|
|
30
|
+
allowed_prefix = "#{base_path}#{::File::SEPARATOR}"
|
|
31
|
+
unless @path == base_path || @path.start_with?(allowed_prefix)
|
|
32
|
+
raise ViewComponent::SystemTestControllerNefariousPathError
|
|
33
|
+
end
|
|
23
34
|
end
|
|
24
35
|
end
|
|
25
36
|
end
|
data/docs/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,30 @@ nav_order: 6
|
|
|
10
10
|
|
|
11
11
|
## main
|
|
12
12
|
|
|
13
|
+
## 4.10.0
|
|
14
|
+
|
|
15
|
+
* Fix `NameError: uninitialized constant ViewComponent::SystemTestControllerNefariousPathError` when booting in the test environment with `eager_load = true`.
|
|
16
|
+
|
|
17
|
+
*Joel Hawksley*
|
|
18
|
+
|
|
19
|
+
* Fix yielded content rendered at wrong location when using form helpers.
|
|
20
|
+
|
|
21
|
+
*Joel Hawksley*, *Markus*
|
|
22
|
+
|
|
23
|
+
## 4.9.0
|
|
24
|
+
|
|
25
|
+
* Fix path traversal vulnerability in `ViewComponentsSystemTestController` where sibling directories sharing a string prefix with the allowed temp directory could bypass the path containment check. The `start_with?` check has been replaced with a separator-aware prefix check, and nefarious path errors now return a 404 instead of an unhandled exception.
|
|
26
|
+
|
|
27
|
+
*Joel Hawksley*
|
|
28
|
+
|
|
29
|
+
* Fix preview route vulnerability where inherited methods on `ViewComponent::Preview` (such as `render_with_template`) could be invoked via the preview URL, allowing arbitrary internal Rails templates to be rendered with attacker-controlled locals and request parameters. `render_args` now raises `AbstractController::ActionNotFound` for any example not explicitly declared on the preview subclass.
|
|
30
|
+
|
|
31
|
+
*Joel Hawksley*
|
|
32
|
+
|
|
33
|
+
* Add `yard-lint` to CI.
|
|
34
|
+
|
|
35
|
+
*Joel Hawksley*
|
|
36
|
+
|
|
13
37
|
## 4.8.0
|
|
14
38
|
|
|
15
39
|
* Add `compile.view_component` ActiveSupport::Notifications event for eager compilation at boot time.
|
data/lib/view_component/base.rb
CHANGED
|
@@ -82,6 +82,7 @@ module ViewComponent
|
|
|
82
82
|
# so helpers, etc work as expected.
|
|
83
83
|
#
|
|
84
84
|
# @param view_context [ActionView::Base] The original view context.
|
|
85
|
+
#
|
|
85
86
|
# @return [void]
|
|
86
87
|
def set_original_view_context(view_context)
|
|
87
88
|
# noop
|
|
@@ -278,6 +279,22 @@ module ViewComponent
|
|
|
278
279
|
end
|
|
279
280
|
end
|
|
280
281
|
|
|
282
|
+
# Sync @output_buffer with the view context's current output buffer before
|
|
283
|
+
# capturing. Form helpers create builders with @template_object pointing to
|
|
284
|
+
# the component. When those builders later call capture inside a partial
|
|
285
|
+
# (whose _run allocated a fresh OutputBuffer on the view context), the
|
|
286
|
+
# component's stale @output_buffer would capture from the wrong buffer.
|
|
287
|
+
# Temporarily switching to the view context's buffer keeps both in sync.
|
|
288
|
+
#
|
|
289
|
+
# @private
|
|
290
|
+
def capture(...)
|
|
291
|
+
old_output_buffer = @output_buffer
|
|
292
|
+
@output_buffer = view_context.output_buffer if view_context
|
|
293
|
+
super
|
|
294
|
+
ensure
|
|
295
|
+
@output_buffer = old_output_buffer
|
|
296
|
+
end
|
|
297
|
+
|
|
281
298
|
# The current controller. Use sparingly as doing so introduces coupling
|
|
282
299
|
# that inhibits encapsulation & reuse, often making testing difficult.
|
|
283
300
|
#
|
|
@@ -57,9 +57,9 @@ module ViewComponent
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
# @
|
|
60
|
+
# @param requested_details [ActionView::TemplateDetails::Requested] i.e. locales, formats, variants
|
|
61
61
|
#
|
|
62
|
-
# @
|
|
62
|
+
# @return all matching compiled templates, in priority order based on the requested details from LookupContext
|
|
63
63
|
def find_templates_for(requested_details)
|
|
64
64
|
filtered_templates = @templates.select do |template|
|
|
65
65
|
template.details.matches?(requested_details)
|
|
@@ -19,6 +19,7 @@ module ViewComponent
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @!attribute generate
|
|
22
|
+
#
|
|
22
23
|
# @return [ActiveSupport::OrderedOptions]
|
|
23
24
|
# The subset of configuration options relating to generators.
|
|
24
25
|
#
|
|
@@ -95,6 +96,7 @@ module ViewComponent
|
|
|
95
96
|
# in `spec/views/components/` rather than the default `spec/components/`.
|
|
96
97
|
|
|
97
98
|
# @!attribute previews
|
|
99
|
+
#
|
|
98
100
|
# @return [ActiveSupport::OrderedOptions]
|
|
99
101
|
# The subset of configuration options relating to previews.
|
|
100
102
|
#
|
|
@@ -124,6 +126,7 @@ module ViewComponent
|
|
|
124
126
|
#
|
|
125
127
|
|
|
126
128
|
# @!attribute instrumentation_enabled
|
|
129
|
+
#
|
|
127
130
|
# @return [Boolean]
|
|
128
131
|
# Whether ActiveSupport notifications are enabled.
|
|
129
132
|
# Defaults to `false`.
|
|
@@ -171,6 +174,7 @@ module ViewComponent
|
|
|
171
174
|
end
|
|
172
175
|
|
|
173
176
|
# @!attribute current
|
|
177
|
+
#
|
|
174
178
|
# @return [ViewComponent::Config]
|
|
175
179
|
# Returns the current ViewComponent::Config. This is persisted against this
|
|
176
180
|
# class so that config options remain accessible before the rest of
|
|
@@ -81,7 +81,11 @@ module ViewComponent
|
|
|
81
81
|
ActiveSupport.on_load(:after_initialize) do
|
|
82
82
|
if Rails.application.config.eager_load
|
|
83
83
|
ActiveSupport::Notifications.instrument("compile.view_component") do
|
|
84
|
-
ViewComponent::Base.descendants.each
|
|
84
|
+
ViewComponent::Base.descendants.each do |component|
|
|
85
|
+
next if component.anonymous?
|
|
86
|
+
|
|
87
|
+
component.__vc_compile
|
|
88
|
+
end
|
|
85
89
|
end
|
|
86
90
|
end
|
|
87
91
|
end
|
|
@@ -40,6 +40,8 @@ module ViewComponent # :nodoc:
|
|
|
40
40
|
|
|
41
41
|
# Returns the arguments for rendering of the component in its layout
|
|
42
42
|
def render_args(example, params: {})
|
|
43
|
+
raise AbstractController::ActionNotFound, "#{example} is not a valid preview example" unless examples.include?(example.to_s)
|
|
44
|
+
|
|
43
45
|
example_params_names = instance_method(example).parameters.map(&:last)
|
|
44
46
|
provided_params = params.slice(*example_params_names).to_h.symbolize_keys
|
|
45
47
|
result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
|
data/lib/view_component/slot.rb
CHANGED
|
@@ -7,6 +7,7 @@ module ViewComponent
|
|
|
7
7
|
# Returns a block that can be used to visit the path of the inline rendered component.
|
|
8
8
|
# @param fragment [Nokogiri::Fragment] The fragment returned from `render_inline`.
|
|
9
9
|
# @param layout [String] The (optional) layout to use.
|
|
10
|
+
#
|
|
10
11
|
# @return [Proc] A block that can be used to visit the path of the inline rendered component.
|
|
11
12
|
def with_rendered_component_path(fragment, layout: false, &block)
|
|
12
13
|
file = Tempfile.new(
|
|
@@ -35,6 +35,7 @@ module ViewComponent
|
|
|
35
35
|
# ```
|
|
36
36
|
#
|
|
37
37
|
# @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered.
|
|
38
|
+
#
|
|
38
39
|
# @return [Nokogiri::HTML5]
|
|
39
40
|
def render_inline(component, **args, &block)
|
|
40
41
|
@page = nil
|
|
@@ -71,17 +72,18 @@ module ViewComponent
|
|
|
71
72
|
# assert_text("Hello, World!")
|
|
72
73
|
# ```
|
|
73
74
|
#
|
|
74
|
-
# Note: `#rendered_preview` expects a preview to be defined with the same class
|
|
75
|
-
# name as the calling test, but with `Test` replaced with `Preview`:
|
|
76
|
-
#
|
|
77
|
-
# MyComponentTest -> MyComponentPreview etc.
|
|
78
|
-
#
|
|
79
|
-
# In RSpec, `Preview` is appended to `described_class`.
|
|
80
|
-
#
|
|
81
75
|
# @param name [String] The name of the preview to be rendered.
|
|
82
76
|
# @param from [ViewComponent::Preview] The class of the preview to be rendered.
|
|
83
77
|
# @param params [Hash] Parameters to be passed to the preview.
|
|
78
|
+
#
|
|
84
79
|
# @return [Nokogiri::HTML5]
|
|
80
|
+
#
|
|
81
|
+
# @note `#rendered_preview` expects a preview to be defined with the same class
|
|
82
|
+
# name as the calling test, but with `Test` replaced with `Preview`:
|
|
83
|
+
#
|
|
84
|
+
# MyComponentTest -> MyComponentPreview etc.
|
|
85
|
+
#
|
|
86
|
+
# In RSpec, `Preview` is appended to `described_class`.
|
|
85
87
|
def render_preview(name, from: __vc_test_helpers_preview_class, params: {})
|
|
86
88
|
previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.previews.controller.constantize)
|
|
87
89
|
|
|
@@ -125,7 +127,7 @@ module ViewComponent
|
|
|
125
127
|
# end
|
|
126
128
|
# ```
|
|
127
129
|
#
|
|
128
|
-
# @param variants [Symbol
|
|
130
|
+
# @param variants [Array<Symbol>] The variants to be set for the provided block.
|
|
129
131
|
def with_variant(*variants)
|
|
130
132
|
old_variants = vc_test_controller.view_context.lookup_context.variants
|
|
131
133
|
|
|
@@ -162,7 +164,7 @@ module ViewComponent
|
|
|
162
164
|
# end
|
|
163
165
|
# ```
|
|
164
166
|
#
|
|
165
|
-
# @param formats [Symbol
|
|
167
|
+
# @param formats [Array<Symbol>] The format(s) to be set for the provided block.
|
|
166
168
|
def with_format(*formats)
|
|
167
169
|
old_formats = vc_test_controller.view_context.lookup_context.formats
|
|
168
170
|
|