Issue #2560137 by dawehner, pwolanin, klausi, tsphethean, sun: Posting an array as value of a form element is allowed even when a string is expected (and bypasses #maxlength constraints) - first step: text fields

8.0.x
Alex Pott 2015-08-31 10:59:15 +01:00
parent 7569493d97
commit e513467c52
12 changed files with 325 additions and 6 deletions

View File

@ -104,6 +104,11 @@ class MachineName extends Textfield {
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input !== FALSE && $input !== NULL) {
// This should be a string, but allow other scalars since they might be
// valid input in programmatic form submissions.
return is_scalar($input) ? (string) $input : '';
}
return NULL;
}

View File

@ -7,6 +7,7 @@
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
@ -68,4 +69,16 @@ class Password extends FormElement {
return $element;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input !== FALSE && $input !== NULL) {
// This should be a string, but allow other scalars since they might be
// valid input in programmatic form submissions.
return is_scalar($input) ? (string) $input : '';
}
return NULL;
}
}

View File

@ -50,9 +50,20 @@ class PasswordConfirm extends FormElement {
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
$element += array('#default_value' => array());
return $element['#default_value'] + array('pass1' => '', 'pass2' => '');
$element += ['#default_value' => []];
return $element['#default_value'] + ['pass1' => '', 'pass2' => ''];
}
$value = ['pass1' => '', 'pass2' => ''];
// Throw out all invalid array keys; we only allow pass1 and pass2.
foreach ($value as $allowed_key => $default) {
// These should be strings, but allow other scalars since they might be
// valid input in programmatic form submissions. Any nested array values
// are ignored.
if (isset($input[$allowed_key]) && is_scalar($input[$allowed_key])) {
$value[$allowed_key] = (string) $input[$allowed_key];
}
}
return $value;
}
/**

View File

@ -7,6 +7,7 @@
namespace Drupal\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
@ -55,4 +56,15 @@ class Textarea extends FormElement {
);
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input !== FALSE && $input !== NULL) {
// This should be a string, but allow other scalars since they might be
// valid input in programmatic form submissions.
return is_scalar($input) ? (string) $input : '';
}
return NULL;
}
}

View File

@ -77,10 +77,14 @@ class Textfield extends FormElement {
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input !== FALSE && $input !== NULL) {
// Equate $input to the form value to ensure it's marked for
// validation.
// This should be a string, but allow other scalars since they might be
// valid input in programmatic form submissions.
if (!is_scalar($input)) {
$input = '';
}
return str_replace(array("\r", "\n"), '', $input);
}
return NULL;
}
/**

View File

@ -39,9 +39,12 @@ class Token extends Hidden {
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input !== FALSE) {
return (string) $input;
if ($input !== FALSE && $input !== NULL) {
// This should be a string, but allow other scalars since they might be
// valid input in programmatic form submissions.
return is_scalar($input) ? (string) $input : '';
}
return NULL;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\Element\MachineNameTest.
*/
namespace Drupal\Tests\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\MachineName;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Render\Element\MachineName
* @group Render
*/
class MachineNameTest extends UnitTestCase {
/**
* @covers ::valueCallback
*
* @dataProvider providerTestValueCallback
*/
public function testValueCallback($expected, $input) {
$element = [];
$form_state = $this->prophesize(FormStateInterface::class)->reveal();
$this->assertSame($expected, MachineName::valueCallback($element, $input, $form_state));
}
/**
* Data provider for testValueCallback().
*/
public function providerTestValueCallback() {
$data = [];
$data[] = [NULL, FALSE];
$data[] = [NULL, NULL];
$data[] = ['', ['test']];
$data[] = ['test', 'test'];
$data[] = ['123', 123];
return $data;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\Element\PasswordConfirmTest.
*/
namespace Drupal\Tests\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\PasswordConfirm;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Render\Element\PasswordConfirm
* @group Render
*/
class PasswordConfirmTest extends UnitTestCase {
/**
* @covers ::valueCallback
*
* @dataProvider providerTestValueCallback
*/
public function testValueCallback($expected, $element, $input) {
$form_state = $this->prophesize(FormStateInterface::class)->reveal();
$this->assertSame($expected, PasswordConfirm::valueCallback($element, $input, $form_state));
}
/**
* Data provider for testValueCallback().
*/
public function providerTestValueCallback() {
$data = [];
$data[] = [['pass1' => '', 'pass2' => ''], [], NULL];
$data[] = [['pass1' => '', 'pass2' => ''], ['#default_value' => ['pass2' => 'value']], NULL];
$data[] = [['pass2' => 'value', 'pass1' => ''], ['#default_value' => ['pass2' => 'value']], FALSE];
$data[] = [['pass1' => '123456', 'pass2' => 'qwerty'], [], ['pass1' => '123456', 'pass2' => 'qwerty']];
$data[] = [['pass1' => '123', 'pass2' => '234'], [], ['pass1' => 123, 'pass2' => 234]];
$data[] = [['pass1' => '', 'pass2' => '234'], [], ['pass1' => ['array'], 'pass2' => 234]];
return $data;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\Element\PasswordTest.
*/
namespace Drupal\Tests\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Password;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Render\Element\Password
* @group Render
*/
class PasswordTest extends UnitTestCase {
/**
* @covers ::valueCallback
*
* @dataProvider providerTestValueCallback
*/
public function testValueCallback($expected, $input) {
$element = [];
$form_state = $this->prophesize(FormStateInterface::class)->reveal();
$this->assertSame($expected, Password::valueCallback($element, $input, $form_state));
}
/**
* Data provider for testValueCallback().
*/
public function providerTestValueCallback() {
$data = [];
$data[] = [NULL, FALSE];
$data[] = [NULL, NULL];
$data[] = ['', ['test']];
$data[] = ['test', 'test'];
$data[] = ['123', 123];
return $data;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\Element\TextareaTest.
*/
namespace Drupal\Tests\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Textarea;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Render\Element\Textarea
* @group Render
*/
class TextareaTest extends UnitTestCase {
/**
* @covers ::valueCallback
*
* @dataProvider providerTestValueCallback
*/
public function testValueCallback($expected, $input) {
$element = [];
$form_state = $this->prophesize(FormStateInterface::class)->reveal();
$this->assertSame($expected, Textarea::valueCallback($element, $input, $form_state));
}
/**
* Data provider for testValueCallback().
*/
public function providerTestValueCallback() {
$data = [];
$data[] = [NULL, FALSE];
$data[] = [NULL, NULL];
$data[] = ['', ['test']];
$data[] = ['test', 'test'];
$data[] = ['123', 123];
return $data;
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\Element\TextfieldTest.
*/
namespace Drupal\Tests\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Textfield;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Render\Element\Textfield
* @group Render
*/
class TextfieldTest extends UnitTestCase {
/**
* @covers ::valueCallback
*
* @dataProvider providerTestValueCallback
*/
public function testValueCallback($expected, $input) {
$element = [];
$form_state = $this->prophesize(FormStateInterface::class)->reveal();
$this->assertSame($expected, Textfield::valueCallback($element, $input, $form_state));
}
/**
* Data provider for testValueCallback().
*/
public function providerTestValueCallback() {
$data = [];
$data[] = [NULL, FALSE];
$data[] = [NULL, NULL];
$data[] = ['', ['test']];
$data[] = ['test', 'test'];
$data[] = ['123', 123];
$data[] = ['testwithnewline', "test\nwith\rnewline"];
return $data;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\Element\TokenTest.
*/
namespace Drupal\Tests\Core\Render\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Token;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Render\Element\Token
* @group Render
*/
class TokenTest extends UnitTestCase {
/**
* @covers ::valueCallback
*
* @dataProvider providerTestValueCallback
*/
public function testValueCallback($expected, $input) {
$element = [];
$form_state = $this->prophesize(FormStateInterface::class)->reveal();
$this->assertSame($expected, Token::valueCallback($element, $input, $form_state));
}
/**
* Data provider for testValueCallback().
*/
public function providerTestValueCallback() {
$data = [];
$data[] = [NULL, FALSE];
$data[] = [NULL, NULL];
$data[] = ['', ['test']];
$data[] = ['test', 'test'];
$data[] = ['123', 123];
return $data;
}
}