view_component 4.13.0 → 4.14.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: aaf81704d1bb151178b288ef446a657e2d8ac0158768ffee8db3c3e2c530aa29
4
- data.tar.gz: 4e2192552574c18ff0dfc4613d797204abe98cd26b6343712c7edb7a4cba65bd
3
+ metadata.gz: 83cf2f7914c64ec4ca156f19d975d79d311619024fb550fde59c0e6f807ca02c
4
+ data.tar.gz: 918cc12667440e6a50793addc531431050e06bda525f348c8522487ea0208439
5
5
  SHA512:
6
- metadata.gz: c06fd1d045b7c78a32cc66d1db448df80b1c2543d6cf9e44ab5bfa4e92c5cfe3d6151f80b24b345168e62dde6b8b400dfb7c28006716494b9e33366376f8cb9d
7
- data.tar.gz: ddf8a61600fb66a0e2ae8ce4095e1d9398355ff2c5b19a16e79d8adeaff3da896dc4ecd948c4a13eaac8683df811fb0b578147486e7bfc609773f41532964466
6
+ metadata.gz: fa5696a0ca7c945fa3ed682fb8f679fd75c462ddb0651b8926e64448392507a5616dcee97218e0a74c3eb4f4d384a89ff2f26fd08d89c9ff86ca961fd6cd410d
7
+ data.tar.gz: f1ced2760c93c93d46e2ed508ec234abbe498100ccb80d1ef83f6d7c1b17e79b7ecbd44f26745e36a7fef74426da49867bbdef038885b954b04982d788e4c322
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@ nav_order: 6
10
10
 
11
11
  ## main
12
12
 
13
+ ## 4.14.0
14
+
15
+ * Freeze `ReusedInstanceError::MESSAGE` and update `test_renders_component_with_asset_url` to build a fresh `AssetComponent` per render, fixing CI regressions introduced by the GHSA-8qw7-6phv-7q6p remediation.
16
+
17
+ *Joel Hawksley*
18
+
19
+ * [Security] Fix incomplete remediation for CVE-2026-54497 (GHSA-8qw7-6phv-7q6p): reused ViewComponent instances could still leak `with_content` and `renders_one`/`renders_many` slot content from an earlier render into a later render because slot state and content set via `with_content` are populated by the caller before `render_in` runs and were not cleared by the previous per-render reset. Reinstate the `ViewComponent::ReusedInstanceError` guard that raises when a component instance is rendered more than once. Rebuild collection child components per render and dup collection spacer components before each render so that legitimate re-rendering of `Collection`/spacer objects continues to work.
20
+
21
+ *Yazan Balawneh, Cystack.ps*
22
+
23
+ * Update GitHub Actions workflows to use `actions/checkout` v7.
24
+
25
+ *Richard Macklin*
26
+
13
27
  ## 4.13.0
14
28
 
15
29
  * Add support for Turbo-streaming ViewComponents.
@@ -106,6 +106,7 @@ module ViewComponent
106
106
  def render_in(view_context, **, &block)
107
107
  self.class.__vc_compile(raise_errors: true) unless self.class.__vc_compiled?
108
108
 
109
+ __vc_check_reused_instance!
109
110
  __vc_reset_render_state!
110
111
 
111
112
  @view_context = view_context
@@ -180,6 +181,7 @@ module ViewComponent
180
181
  ensure
181
182
  view_context.instance_variable_set(:@virtual_path, @old_virtual_path)
182
183
  @current_template = old_current_template
184
+ @__vc_rendered = true
183
185
  end
184
186
 
185
187
  # Subclass components that call `super` inside their template code will cause a
@@ -468,7 +470,9 @@ module ViewComponent
468
470
  # state from a previous render. Slot state (`@__vc_set_slots`,
469
471
  # `@__vc_content_set_by_with_content`) is intentionally preserved because it
470
472
  # is populated by callers _before_ `render_in` runs (e.g. via `with_*`
471
- # slot setters or `with_content`).
473
+ # slot setters or `with_content`); reuse of an instance that has slot or
474
+ # `with_content` state is guarded against separately by
475
+ # `__vc_check_reused_instance!`.
472
476
  RENDER_STATE_IVARS = %i[
473
477
  @__vc_controller
474
478
  @__vc_helpers
@@ -481,6 +485,17 @@ module ViewComponent
481
485
  end
482
486
  end
483
487
 
488
+ # Raises when a component instance is rendered more than once.
489
+ # Reusing a component instance across renders can leak request-scoped state
490
+ # (controller, helpers, request, view_flow, slot content, `with_content`)
491
+ # from an earlier render into a later one. See
492
+ # `ViewComponent::ReusedInstanceError` and GHSA-8qw7-6phv-7q6p.
493
+ def __vc_check_reused_instance!
494
+ return unless defined?(@__vc_rendered) && @__vc_rendered
495
+
496
+ raise ReusedInstanceError.new(self.class.name)
497
+ end
498
+
484
499
  # Configuration for generators.
485
500
  #
486
501
  # All options under this namespace default to `false` unless otherwise
@@ -72,11 +72,16 @@ module ViewComponent
72
72
  end
73
73
 
74
74
  # Render the spacer through a fresh `dup` so a collection rendered multiple
75
- # times always gets a clean spacer instance.
75
+ # times does not reuse (and trip the single-render guard on) the spacer
76
+ # instance passed by the caller.
76
77
  def rendered_spacer(view_context)
77
78
  return "" unless @spacer_component
78
79
 
79
- @spacer_component.dup.render_in(view_context)
80
+ spacer = @spacer_component.dup
81
+ if spacer.instance_variable_defined?(:@__vc_rendered)
82
+ spacer.remove_instance_variable(:@__vc_rendered)
83
+ end
84
+ spacer.render_in(view_context)
80
85
  end
81
86
  end
82
87
  end
@@ -179,6 +179,19 @@ module ViewComponent
179
179
  "To fix this issue, pass a value.".freeze
180
180
  end
181
181
 
182
+ class ReusedInstanceError < StandardError
183
+ MESSAGE =
184
+ "ViewComponent instances are single-use; a COMPONENT instance was rendered more than once.\n\n" \
185
+ "Reusing a component instance across renders can leak request-scoped state " \
186
+ "(controller, helpers, request, view_flow, slot content, `with_content`) from an earlier render " \
187
+ "into a later one, which has security and correctness implications (GHSA-8qw7-6phv-7q6p).\n\n" \
188
+ "To fix this issue, create a new component instance per render.".freeze
189
+
190
+ def initialize(klass_name)
191
+ super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
192
+ end
193
+ end
194
+
182
195
  class TranslateCalledBeforeRenderError < BaseError
183
196
  MESSAGE =
184
197
  "`#translate` can't be used before rendering as it depends " \
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 4
6
- MINOR = 13
6
+ MINOR = 14
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.13.0
4
+ version: 4.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team