From d22982f32b26ff16cfdd4980bb57f866f015abde Mon Sep 17 00:00:00 2001 From: Sean Madsen Date: Tue, 24 Apr 2018 16:38:23 -0400 Subject: [PATCH] Add 'Alphanumeric' rule type This type is now available when reading GET parameters with `CRM_Utils_Request::retrieve()` and it offers improved security over the widely used 'String' type by being strict enough to reject just about any conceivable attack payload, while still accepting relatively simple strings. --- CRM/Utils/Rule.php | 21 +++++++++++++ CRM/Utils/Type.php | 7 +++++ tests/phpunit/CRM/Utils/RuleTest.php | 46 ++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index 060967d8b9..61a467451f 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -505,6 +505,27 @@ class CRM_Utils_Rule { return preg_match('/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/', $value) ? TRUE : FALSE; } + /** + * Test whether $value is alphanumeric. + * + * Underscores and dashes are also allowed! + * + * This is the type of string you could expect to see in URL parameters + * like `?mode=live` vs `?mode=test`. This function exists so that we can be + * strict about what we accept for such values, thus mitigating against + * potential security issues. + * + * @see \CRM_Utils_RuleTest::alphanumericData + * for examples of vales that give TRUE/FALSE here + * + * @param $value + * + * @return bool + */ + public static function alphanumeric($value) { + return preg_match('/^[a-zA-Z0-9_-]*$/', $value) ? TRUE : FALSE; + } + /** * @param $value * @param $noOfDigit diff --git a/CRM/Utils/Type.php b/CRM/Utils/Type.php index 3cd8085a2d..7ddc8dd9d3 100644 --- a/CRM/Utils/Type.php +++ b/CRM/Utils/Type.php @@ -423,6 +423,7 @@ class CRM_Utils_Type { 'MysqlOrderBy', 'ExtensionKey', 'Json', + 'Alphanumeric', ); if (!in_array($type, $possibleTypes)) { if ($isThrowException) { @@ -537,6 +538,12 @@ class CRM_Utils_Type { return $data; } break; + + case 'Alphanumeric': + if (CRM_Utils_Rule::alphanumeric($data)) { + return $data; + } + break; } if ($abort) { diff --git a/tests/phpunit/CRM/Utils/RuleTest.php b/tests/phpunit/CRM/Utils/RuleTest.php index 36e91e59b7..edf81083b2 100644 --- a/tests/phpunit/CRM/Utils/RuleTest.php +++ b/tests/phpunit/CRM/Utils/RuleTest.php @@ -132,4 +132,50 @@ class CRM_Utils_RuleTest extends CiviUnitTestCase { $this->assertEquals($expectedResult, CRM_Utils_Rule::checkExtensionKeyIsValid($key)); } + /** + * @return array + */ + public function alphanumericData() { + $expectTrue = [ + 0, + 999, + -5, + '', + 'foo', + '0', + '-', + '_foo', + 'one-two', + 'f00' + ]; + $expectFalse = [ + ' ', + 5.7, + 'one two', + 'one.two', + 'Aalert('XSS');", + '(foo)', + 'foo;', + '[foo]' + ]; + $data = []; + foreach ($expectTrue as $value) { + $data[] = [$value, TRUE]; + } + foreach ($expectFalse as $value) { + $data[] = [$value, FALSE]; + } + return $data; + } + + /** + * @dataProvider alphanumericData + * @param $value + * @param $expected + */ + public function testAlphanumeric($value, $expected) { + $this->assertEquals($expected, CRM_Utils_Rule::alphanumeric($value)); + } + } -- 2.25.1