Note: the description below was machine-generated for CPANSec. The issue has been assigned CVE-2026-19873, and has been added here publicly as per request from Dean Hamstead.
When a Repeatable element has counter_name set, Element/Repeatable.pm:279-282 takes the repeat count straight from the query string, validating only that it is a positive integer, and :290 passes it to repeat(), whose loops at :103 and :193 deep-clone the block's whole child subtree once per iteration. There is no upper bound, and no attribute an application could set to impose one.
my $input = $form->query->param($counter_name);
if ( defined $input && $input =~ /^[1-9][0-9]*\z/ ) {
$count = $input;
}
Element::Repeatable::process runs from HTML::FormFu::process() on every request and reads the counter before the submitted / not-submitted decision, so GET /form?count=N reaches it with no session, no authentication, no POST body and no submitted field values.
Cost is super-linear rather than linear: each cloned field's constraints call HTML::FormFu::Constraint::_find_field_value, which walks the entire element tree via get_fields({ nested_name => ... }) once per constraint. Measured on 2.07 under perl 5.40 with one Text field and one Required constraint in the block — the shape t/repeatable/constraints/required.yml uses — count=500 costs 1.0s CPU, 1000 4.1s, 2000 18.9s and 4000 98.2s, so doubling the parameter multiplies CPU by 4-5x and a five-digit value is CPU-hours from a single request. Removing the constraint makes it linear and cheap, which isolates the multiplier. Nested Repeatables are the memory variant and multiply: outer_count=100&inner_count=100 builds 10,000 fields and 95 MB RSS from two three-digit numbers, and that nesting is a shipped example (t/nested/elements/repeatable_repeatable.yml).
Note: the description below was machine-generated for CPANSec. The issue has been assigned CVE-2026-19873, and has been added here publicly as per request from Dean Hamstead.
When a Repeatable element has counter_name set, Element/Repeatable.pm:279-282 takes the repeat count straight from the query string, validating only that it is a positive integer, and :290 passes it to repeat(), whose loops at :103 and :193 deep-clone the block's whole child subtree once per iteration. There is no upper bound, and no attribute an application could set to impose one.
my $input = $form->query->param($counter_name);
if ( defined $input && $input =~ /^[1-9][0-9]*\z/ ) {
$count = $input;
}
Element::Repeatable::process runs from HTML::FormFu::process() on every request and reads the counter before the submitted / not-submitted decision, so GET /form?count=N reaches it with no session, no authentication, no POST body and no submitted field values.
Cost is super-linear rather than linear: each cloned field's constraints call HTML::FormFu::Constraint::_find_field_value, which walks the entire element tree via get_fields({ nested_name => ... }) once per constraint. Measured on 2.07 under perl 5.40 with one Text field and one Required constraint in the block — the shape t/repeatable/constraints/required.yml uses — count=500 costs 1.0s CPU, 1000 4.1s, 2000 18.9s and 4000 98.2s, so doubling the parameter multiplies CPU by 4-5x and a five-digit value is CPU-hours from a single request. Removing the constraint makes it linear and cheap, which isolates the multiplier. Nested Repeatables are the memory variant and multiply: outer_count=100&inner_count=100 builds 10,000 fields and 95 MB RSS from two three-digit numbers, and that nesting is a shipped example (t/nested/elements/repeatable_repeatable.yml).