Unauthorized property update in CheckboxGroup component in Vaadin 12-14 and 15-20
Description
Improper check in CheckboxGroup in com.vaadin:vaadin-checkbox-flow versions 1.2.0 prior to 2.0.0 (Vaadin 12.0.0 prior to 14.0.0), 2.0.0 prior to 3.0.0 (Vaadin 14.0.0 prior to 14.5.0), 3.0.0 through 4.0.1 (Vaadin 15.0.0 through 17.0.11), 14.5.0 through 14.6.7 (Vaadin 14.5.0 through 14.6.7), and 18.0.0 through 20.0.5 (Vaadin 18.0.0 through 20.0.5) allows attackers to modify the value of a disabled Checkbox inside enabled CheckboxGroup component via unspecified vectors.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
com.vaadin:vaadin-checkbox-flowMaven | >= 12.0.0, < 14.6.8 | 14.6.8 |
com.vaadin:vaadin-checkbox-flowMaven | >= 15.0.0, < 20.0.6 | 20.0.6 |
Affected products
2- Vaadin/vaadin-checkbox-flowv5Range: 1.2.0
Patches
21aa6bc94c763fix: allow changing checkboxgroup value if disabled checkbox value doesn't change (#1903) (#1970)
3 files changed · +66 −7
vaadin-checkbox-flow-parent/vaadin-checkbox-flow-integration-tests/src/main/java/com/vaadin/flow/component/checkbox/tests/CheckboxGroupDisabledItemPage.java+15 −1 modified@@ -1,6 +1,8 @@ package com.vaadin.flow.component.checkbox.tests; import com.vaadin.flow.component.checkbox.CheckboxGroup; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.router.Route; @@ -18,6 +20,18 @@ public CheckboxGroupDisabledItemPage() { group.select("bar"); group.setItemEnabledProvider(item -> !"bar".equals(item)); group.setId("checkbox-group-disabled-item"); - add(group); + + NativeButton toggleBarButton = new NativeButton("Toggle \"bar\"", + event -> { + boolean isBarSelected = group.isSelected("bar"); + if (isBarSelected) { + group.deselect("bar"); + } else { + group.select("bar"); + } + }); + toggleBarButton.setId("toggle-bar-button"); + + add(group, new Div(toggleBarButton)); } }
vaadin-checkbox-flow-parent/vaadin-checkbox-flow-integration-tests/src/test/java/com/vaadin/flow/component/checkbox/tests/CheckboxGroupDisabledItemIT.java+41 −0 modified@@ -37,4 +37,45 @@ public void disabledGroupItemChecked() { Assert.assertEquals(Boolean.TRUE.toString(), checkboxes.get(1).getAttribute("checked")); } + + @Test + public void disabledItemCanBeCheckedProgrammatically() { + open(); + TestBenchElement group = $(TestBenchElement.class) + .id("checkbox-group-disabled-item"); + List<TestBenchElement> checkboxes = group.$("vaadin-checkbox").all(); + TestBenchElement secondCheckbox = checkboxes.get(1); + TestBenchElement toggleBarButton = $("button").id("toggle-bar-button"); + + // Deselect + toggleBarButton.click(); + Assert.assertNull(secondCheckbox.getAttribute("checked")); + + // Reselect + toggleBarButton.click(); + Assert.assertEquals(Boolean.TRUE.toString(), + secondCheckbox.getAttribute("checked")); + } + + /** + * Regression test for: + * https://github.com/vaadin/flow-components/issues/1185 + */ + @Test + public void enabledItemCanBeCheckedManuallyWhenSettingItemEnabledProviderAfterSelectingValue() { + open(); + TestBenchElement group = $(TestBenchElement.class) + .id("checkbox-group-disabled-item"); + List<TestBenchElement> checkboxes = group.$("vaadin-checkbox").all(); + TestBenchElement firstCheckbox = checkboxes.get(0); + + // Select + firstCheckbox.click(); + Assert.assertEquals(Boolean.TRUE.toString(), + firstCheckbox.getAttribute("checked")); + + // Deselect + firstCheckbox.click(); + Assert.assertNull(firstCheckbox.getAttribute("checked")); + } }
vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/main/java/com/vaadin/flow/component/checkbox/CheckboxGroup.java+10 −6 modified@@ -325,12 +325,16 @@ protected boolean valueEquals(Set<T> value1, Set<T> value2) { @Override protected boolean hasValidValue() { - Set<T> selectedItems = presentationToModel(this, + // we need to compare old value with new value to see if any disabled + // items changed their value + Set<T> value = presentationToModel(this, (JsonArray) getElement().getPropertyRaw(VALUE)); - if (selectedItems == null || selectedItems.isEmpty()) { - return true; - } - return selectedItems.stream().allMatch(itemEnabledProvider); + Set<T> oldValue = getValue(); + + // disabled items cannot change their value + return getCheckboxItems().filter(CheckBoxItem::isDisabledBoolean) + .noneMatch(item -> oldValue.contains(item.getItem()) != value + .contains(item.getItem())); } private void reset() { @@ -401,7 +405,7 @@ private void validateSelectionEnabledState(PropertyChangeEvent event) { } finally { registerValidation(); } - // Now make sure that the button is still in the correct state + // Now make sure that the checkbox is still in the correct state Set<T> value = presentationToModel(this, (JsonArray) event.getValue()); getCheckboxItems()
f136532a735ffix: allow changing checkboxgroup value if disabled checkbox value doesn't change (#1903) (#1968)
3 files changed · +66 −7
vaadin-checkbox-flow-parent/vaadin-checkbox-flow-integration-tests/src/main/java/com/vaadin/flow/component/checkbox/tests/CheckboxGroupDisabledItemPage.java+15 −1 modified@@ -1,6 +1,8 @@ package com.vaadin.flow.component.checkbox.tests; import com.vaadin.flow.component.checkbox.CheckboxGroup; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.router.Route; @@ -18,6 +20,18 @@ public CheckboxGroupDisabledItemPage() { group.select("bar"); group.setItemEnabledProvider(item -> !"bar".equals(item)); group.setId("checkbox-group-disabled-item"); - add(group); + + NativeButton toggleBarButton = new NativeButton("Toggle \"bar\"", + event -> { + boolean isBarSelected = group.isSelected("bar"); + if (isBarSelected) { + group.deselect("bar"); + } else { + group.select("bar"); + } + }); + toggleBarButton.setId("toggle-bar-button"); + + add(group, new Div(toggleBarButton)); } }
vaadin-checkbox-flow-parent/vaadin-checkbox-flow-integration-tests/src/test/java/com/vaadin/flow/component/checkbox/tests/CheckboxGroupDisabledItemIT.java+41 −0 modified@@ -37,4 +37,45 @@ public void disabledGroupItemChecked() { Assert.assertEquals(Boolean.TRUE.toString(), checkboxes.get(1).getAttribute("checked")); } + + @Test + public void disabledItemCanBeCheckedProgrammatically() { + open(); + TestBenchElement group = $(TestBenchElement.class) + .id("checkbox-group-disabled-item"); + List<TestBenchElement> checkboxes = group.$("vaadin-checkbox").all(); + TestBenchElement secondCheckbox = checkboxes.get(1); + TestBenchElement toggleBarButton = $("button").id("toggle-bar-button"); + + // Deselect + toggleBarButton.click(); + Assert.assertNull(secondCheckbox.getAttribute("checked")); + + // Reselect + toggleBarButton.click(); + Assert.assertEquals(Boolean.TRUE.toString(), + secondCheckbox.getAttribute("checked")); + } + + /** + * Regression test for: + * https://github.com/vaadin/flow-components/issues/1185 + */ + @Test + public void enabledItemCanBeCheckedManuallyWhenSettingItemEnabledProviderAfterSelectingValue() { + open(); + TestBenchElement group = $(TestBenchElement.class) + .id("checkbox-group-disabled-item"); + List<TestBenchElement> checkboxes = group.$("vaadin-checkbox").all(); + TestBenchElement firstCheckbox = checkboxes.get(0); + + // Select + firstCheckbox.click(); + Assert.assertEquals(Boolean.TRUE.toString(), + firstCheckbox.getAttribute("checked")); + + // Deselect + firstCheckbox.click(); + Assert.assertNull(firstCheckbox.getAttribute("checked")); + } }
vaadin-checkbox-flow-parent/vaadin-checkbox-flow/src/main/java/com/vaadin/flow/component/checkbox/CheckboxGroup.java+10 −6 modified@@ -442,12 +442,16 @@ protected boolean valueEquals(Set<T> value1, Set<T> value2) { @Override protected boolean hasValidValue() { - Set<T> selectedItems = presentationToModel(this, + // we need to compare old value with new value to see if any disabled + // items changed their value + Set<T> value = presentationToModel(this, (JsonArray) getElement().getPropertyRaw(VALUE)); - if (selectedItems == null || selectedItems.isEmpty()) { - return true; - } - return selectedItems.stream().allMatch(itemEnabledProvider); + Set<T> oldValue = getValue(); + + // disabled items cannot change their value + return getCheckboxItems().filter(CheckBoxItem::isDisabledBoolean) + .noneMatch(item -> oldValue.contains(item.getItem()) != value + .contains(item.getItem())); } @SuppressWarnings("unchecked") @@ -540,7 +544,7 @@ private void validateSelectionEnabledState(PropertyChangeEvent event) { } finally { registerValidation(); } - // Now make sure that the button is still in the correct state + // Now make sure that the checkbox is still in the correct state Set<T> value = presentationToModel(this, (JsonArray) event.getValue()); getCheckboxItems()
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-qcc4-3rxf-gf4mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-33605ghsaADVISORY
- github.com/vaadin/flow-components/commit/1aa6bc94c763023bc3fc5849c2a1e8cab3bf6766ghsaWEB
- github.com/vaadin/flow-components/commit/f136532a735f703f1144f19fee48c9009c659f03ghsaWEB
- github.com/vaadin/flow-components/pull/1903ghsax_refsource_CONFIRMWEB
- github.com/vaadin/flow-components/security/advisories/GHSA-qcc4-3rxf-gf4mghsaWEB
- vaadin.com/security/cve-2021-33605ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.