From 48afc9a87313d75b2b8a6e8e5015c3d48e37ccb9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 6 Nov 2021 15:08:49 +0100 Subject: [PATCH] Fix an infinite loop when parsing environment variables with float/integer values (#8293) --- CHANGELOG.md | 1 + program/lib/Roundcube/rcube_config.php | 28 +++++------ tests/Framework/Config.php | 67 ++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f48d4e8d3b..7bffab86319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Fix PHP fatal error on an undefined constant in contacts import action (#8277) - Fix fetching headers of multiple message parts at once in rcube_imap_generic::fetchMIMEHeaders() (#8282) - Fix bug where attachment download could sometimes fail with a CSRF check error (#8283) +- Fix an infinite loop when parsing environment variables with float/integer values (#8293) ## Release 1.5.0 diff --git a/program/lib/Roundcube/rcube_config.php b/program/lib/Roundcube/rcube_config.php index 089b962b8c5..1e90f235fd4 100644 --- a/program/lib/Roundcube/rcube_config.php +++ b/program/lib/Roundcube/rcube_config.php @@ -110,21 +110,21 @@ public function __construct($env = '') */ private function guess_type($value) { - $type = 'string'; - - // array requires hint to be passed. + if (preg_match('/^\d+$/', $value)) { + return 'int'; + } if (preg_match('/^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$/', $value)) { - $type = 'float'; - } - else if (preg_match('/^\d+$/', $value)) { - $type = 'integer'; + return 'float'; } - else if (preg_match('/^(t(rue)?)|(f(alse)?)$/i', $value)) { - $type = 'boolean'; + + if (preg_match('/^(t(rue)?)|(f(alse)?)$/i', $value)) { + return 'bool'; } - return $type; + // TODO: array/object + + return 'string'; } /** @@ -135,16 +135,16 @@ private function guess_type($value) * * @return mixed Appropriately typed interpretation of $string. */ - private function parse_env($string, $type) + private function parse_env($string, $type = null) { switch ($type) { - case 'boolean': + case 'bool': return (bool) $string; - case 'integer': + case 'int': return (int) $string; - case 'double': + case 'float': return (float) $string; case 'string': diff --git a/tests/Framework/Config.php b/tests/Framework/Config.php index 5c521cf09c3..700e8f3f8f0 100644 --- a/tests/Framework/Config.php +++ b/tests/Framework/Config.php @@ -26,4 +26,71 @@ function test_resolve_timezone_alias() $this->assertSame('UTC', rcube_config::resolve_timezone_alias('Etc/GMT')); $this->assertSame('UTC', rcube_config::resolve_timezone_alias('Etc/Zulu')); } + + /** + * Test get() and set() + */ + function test_get_and_set() + { + $object = new rcube_config(); + + $this->assertSame(null, $object->get('test')); + $this->assertSame('def', $object->get('test', 'def')); + + $object->set('test', 'val'); + + $this->assertSame('val', $object->get('test')); + + putenv('ROUNDCUBE_TEST_INT=4190'); + + $this->assertSame(4190, $object->get('test_int')); + + // TODO: test more code paths in get() and set() + } + + /** + * Test guess_type() + */ + function test_guess_type() + { + $object = new rcube_config(); + + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['true'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['false'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['t'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['f'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['TRUE'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['FALSE'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['T'])); + $this->assertSame('bool', invokeMethod($object, 'guess_type', ['F'])); + + $this->assertSame('float', invokeMethod($object, 'guess_type', ['1.5'])); + $this->assertSame('float', invokeMethod($object, 'guess_type', ['1.0'])); + $this->assertSame('float', invokeMethod($object, 'guess_type', ['1.2e3'])); + $this->assertSame('float', invokeMethod($object, 'guess_type', ['7E-10'])); + + $this->assertSame('int', invokeMethod($object, 'guess_type', ['1'])); + $this->assertSame('int', invokeMethod($object, 'guess_type', ['123456789'])); + + $this->assertSame('string', invokeMethod($object, 'guess_type', ['ON'])); + $this->assertSame('string', invokeMethod($object, 'guess_type', ['1-0'])); + } + + /** + * Test parse_env() + */ + function test_parse_env() + { + $object = new rcube_config(); + + $this->assertSame(true, invokeMethod($object, 'parse_env', ['true'])); + $this->assertSame(1, invokeMethod($object, 'parse_env', ['1'])); + $this->assertSame(1.5, invokeMethod($object, 'parse_env', ['1.5'])); + $this->assertSame(true, invokeMethod($object, 'parse_env', ['1', 'bool'])); + $this->assertSame(1.0, invokeMethod($object, 'parse_env', ['1', 'float'])); + $this->assertSame(1, invokeMethod($object, 'parse_env', ['1', 'int'])); + $this->assertSame('1', invokeMethod($object, 'parse_env', ['1', 'string'])); + $this->assertSame([1], invokeMethod($object, 'parse_env', ['[1]', 'array'])); + $this->assertSame(['test' => 1], (array) invokeMethod($object, 'parse_env', ['{"test":1}', 'object'])); + } }