diff --git a/includes/fields/class-acf-field-text.php b/includes/fields/class-acf-field-text.php index 173cbce5..c829bcc8 100644 --- a/includes/fields/class-acf-field-text.php +++ b/includes/fields/class-acf-field-text.php @@ -178,8 +178,8 @@ function render_field_presentation_settings( $field ) { */ function validate_value( $valid, $value, $field, $input ) { - // Check maxlength - if ( isset( $field['maxlength'] ) && $field['maxlength'] && ( acf_strlen( $value ) > $field['maxlength'] ) ) { + // Check maxlength. A non-scalar value (e.g. an array from a crafted submission) is not valid text, so skip the string length check. + if ( isset( $field['maxlength'] ) && $field['maxlength'] && is_scalar( $value ) && ( acf_strlen( $value ) > $field['maxlength'] ) ) { /* translators: %d: the maximum number of characters */ return sprintf( __( 'Value must not exceed %d characters', 'secure-custom-fields' ), $field['maxlength'] ); } diff --git a/includes/fields/class-acf-field-textarea.php b/includes/fields/class-acf-field-textarea.php index 20e6809c..2c5d767c 100644 --- a/includes/fields/class-acf-field-textarea.php +++ b/includes/fields/class-acf-field-textarea.php @@ -214,8 +214,8 @@ function format_value( $value, $post_id, $field ) { */ function validate_value( $valid, $value, $field, $input ) { - // Check maxlength. - if ( $field['maxlength'] && ( acf_strlen( $value ) > $field['maxlength'] ) ) { + // Check maxlength. A non-scalar value (e.g. an array from a crafted submission) is not valid text, so skip the string length check. + if ( $field['maxlength'] && is_scalar( $value ) && ( acf_strlen( $value ) > $field['maxlength'] ) ) { /* translators: %d: the maximum number of characters */ return sprintf( __( 'Value must not exceed %d characters', 'secure-custom-fields' ), $field['maxlength'] ); } diff --git a/tests/php/includes/fields/test-class-acf-field-text.php b/tests/php/includes/fields/test-class-acf-field-text.php index cdc59d21..2570151c 100644 --- a/tests/php/includes/fields/test-class-acf-field-text.php +++ b/tests/php/includes/fields/test-class-acf-field-text.php @@ -215,4 +215,36 @@ public function test_get_rest_schema_casts_maxlength_to_int() { $this->assertArrayHasKey( 'maxLength', $schema ); $this->assertSame( 25, $schema['maxLength'] ); // Should be int, not string. } + + /** + * Test validate_value with a non-scalar value and maxlength set. + * + * A crafted submission (e.g. `acf[field_key][]=x`) can deliver an array to a + * text field. The maxlength check must not run a string operation on it, which + * would emit an "Array to string conversion" warning. + */ + public function test_validate_value_array_with_maxlength_does_not_convert() { + $field = $this->get_field( array( 'maxlength' => 10 ) ); + + $caught = null; + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler -- Capturing the conversion notice deterministically for this regression test. + set_error_handler( + static function ( $errno, $errstr ) use ( &$caught ) { + if ( false !== strpos( $errstr, 'Array to string conversion' ) ) { + $caught = $errstr; + } + return true; + } + ); + + try { + $valid = $this->field_instance->validate_value( true, array( 'x' ), $field, 'acf[field_text_test]' ); + } finally { + restore_error_handler(); + } + + $this->assertNull( $caught, 'maxlength check must not trigger an array-to-string conversion' ); + // A non-scalar value is not valid text, so the passed $valid state is preserved. + $this->assertTrue( $valid ); + } } diff --git a/tests/php/includes/fields/test-class-acf-field-textarea.php b/tests/php/includes/fields/test-class-acf-field-textarea.php index 63fa1be3..eb54df86 100644 --- a/tests/php/includes/fields/test-class-acf-field-textarea.php +++ b/tests/php/includes/fields/test-class-acf-field-textarea.php @@ -135,4 +135,36 @@ public function test_get_rest_schema() { $this->assertIsArray( $schema ); $this->assertContains( 'string', $schema['type'] ); } + + /** + * Test validate_value with a non-scalar value and maxlength set. + * + * A crafted submission (e.g. `acf[field_key][]=x`) can deliver an array to a + * textarea field. The maxlength check must not run a string operation on it, + * which would emit an "Array to string conversion" warning. + */ + public function test_validate_value_array_with_maxlength_does_not_convert() { + $field = $this->get_field( array( 'maxlength' => 10 ) ); + + $caught = null; + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler -- Capturing the conversion notice deterministically for this regression test. + set_error_handler( + static function ( $errno, $errstr ) use ( &$caught ) { + if ( false !== strpos( $errstr, 'Array to string conversion' ) ) { + $caught = $errstr; + } + return true; + } + ); + + try { + $valid = $this->field_instance->validate_value( true, array( 'x' ), $field, 'acf[field_textarea_test]' ); + } finally { + restore_error_handler(); + } + + $this->assertNull( $caught, 'maxlength check must not trigger an array-to-string conversion' ); + // A non-scalar value is not valid text, so the passed $valid state is preserved. + $this->assertTrue( $valid ); + } }