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 +4 -4
- data/docs/CHANGELOG.md +14 -0
- data/lib/view_component/base.rb +16 -1
- data/lib/view_component/collection.rb +7 -2
- data/lib/view_component/errors.rb +13 -0
- 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: 83cf2f7914c64ec4ca156f19d975d79d311619024fb550fde59c0e6f807ca02c
|
|
4
|
+
data.tar.gz: 918cc12667440e6a50793addc531431050e06bda525f348c8522487ea0208439
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
data/lib/view_component/base.rb
CHANGED
|
@@ -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
|
|
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
|
|
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 " \
|