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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cd32d6d38604882019810f1213145bafb749c66b57d4edcee32902092f436ab
4
- data.tar.gz: c29d17a4f72223b7e342e5f224fc39505d3bc50cb1a8174db6371c6d6b8ded6b
3
+ metadata.gz: af6c301478d5448d3784a0dd502e0baee242ee766a48aa0a9615aa1612bd9ceb
4
+ data.tar.gz: e58be6763cb626c356f002a87d354297471eed752a912315b5fcd885f66b5652
5
5
  SHA512:
6
- metadata.gz: aecaee657c788ae3395994d2234711101fc58f5439f9488993e9b928cb103abba5f5add66d53719259c4f6069a661b7e0a2c3a96ce3c990ca1191fa92b2933fd
7
- data.tar.gz: 87688cbcfeb6a50be556a435b97e0ff6541fdb4383589b24971c7a6c0caee61d506acee766da36b76345d780cdb0877a6ebff3fb4965c1cb7d1b2878f65e4aab
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
- raise ViewComponent::SystemTestControllerNefariousPathError unless @path.start_with?(base_path)
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.
@@ -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
- # @return all matching compiled templates, in priority order based on the requested details from LookupContext
60
+ # @param requested_details [ActionView::TemplateDetails::Requested] i.e. locales, formats, variants
61
61
  #
62
- # @param [ActionView::TemplateDetails::Requested] requested_details i.e. locales, formats, variants
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(&:__vc_compile)
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)
@@ -96,7 +96,6 @@ module ViewComponent
96
96
  # end
97
97
  # end
98
98
  # end
99
- #
100
99
  def method_missing(symbol, *args, **kwargs, &block)
101
100
  @__vc_component_instance.public_send(symbol, *args, **kwargs, &block)
102
101
  end
@@ -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[]] The variants to be set for the provided block.
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[]] The format(s) to be set for the provided block.
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
 
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 4
6
- MINOR = 8
6
+ MINOR = 10
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.0
4
+ version: 4.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team