From a97e38dc9222beeb929d403207a0552efbcc858e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 09:50:13 +0000 Subject: [PATCH 1/5] Initial plan From 7d17108d62c04f2f56367d5e0c0b82903999a0d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:06:16 +0000 Subject: [PATCH 2/5] Fix date input validation bugs and add comprehensive tests - Fix checkInput for 'date' type to validate parts are integers using ctype_digit() and checkdate() - Fix getInput for 'date' type to cast parts to (int) before mktime() - Update composer.json dependencies for PHP 8.3 compatibility (PHPUnit 10/11, Carbon 2/3) - Update phpunit.xml to modern PHPUnit format - Update all existing tests to use PHPUnit\Framework\TestCase - Add comprehensive tests for date validation, SQL injection patterns, all input types - Add tests for Collection, ErrorCollection, SimpleCrypt, URLBuilder, TokenGenerator Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com> --- .gitignore | 3 +- composer.json | 6 +- phpunit.xml | 12 +- src/Neuron/Core/Tools.php | 13 +- tests/CollectionTest.php | 195 ++++++++++++++ tests/DbQueryTest.php | 4 +- tests/ErrorCollectionTest.php | 55 ++++ tests/FilterTest.php | 4 +- tests/SimpleCryptTest.php | 81 ++++++ tests/TokenGeneratorTest.php | 4 +- tests/ToolsTest.php | 487 +++++++++++++++++++++++++++++++++- tests/URLBuilderTest.php | 59 ++++ 12 files changed, 903 insertions(+), 20 deletions(-) create mode 100644 tests/CollectionTest.php create mode 100644 tests/ErrorCollectionTest.php create mode 100644 tests/SimpleCryptTest.php create mode 100644 tests/URLBuilderTest.php diff --git a/.gitignore b/.gitignore index cdb3d20..9df1b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ vendor/ -composer.lock \ No newline at end of file +composer.lock +.phpunit.cache \ No newline at end of file diff --git a/composer.json b/composer.json index 0afb7da..65eed88 100644 --- a/composer.json +++ b/composer.json @@ -15,13 +15,13 @@ ], "require" : { - "php": ">=5.5.0", + "php": ">=8.1.0", "ext-gettext" : "*", - "nesbot/carbon": "~1.18" + "nesbot/carbon": "^2.0||^3.0" }, "require-dev": { - "phpunit/phpunit": "5.1.*" + "phpunit/phpunit": "^10.0||^11.0" }, "autoload": { diff --git a/phpunit.xml b/phpunit.xml index 7362ee9..216c033 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,11 +1,13 @@ - + - - + + src/Neuron - - + + diff --git a/src/Neuron/Core/Tools.php b/src/Neuron/Core/Tools.php index 4c88481..740af75 100755 --- a/src/Neuron/Core/Tools.php +++ b/src/Neuron/Core/Tools.php @@ -34,7 +34,7 @@ public static function getInput ($dat, $key, $type, $default = null) // For date's return timestamp. case 'date': $time = explode ('-', $dat[$key]); - return mktime (0, 0, 1, $time[1], $time[2], $time[0]); + return mktime (0, 0, 1, (int)$time[1], (int)$time[2], (int)$time[0]); case 'datetime': return new DateTime ($dat[$key]); @@ -106,8 +106,15 @@ public static function checkInput ($value, $type) elseif ($type == 'date') { - $time = explode ('-', $value); - return self::isValidUTF8 ($value) && (count ($time) == 3); + if (!self::isValidUTF8($value)) { + return false; + } + $time = explode('-', $value); + return count($time) === 3 + && ctype_digit($time[0]) + && ctype_digit($time[1]) + && ctype_digit($time[2]) + && checkdate((int)$time[1], (int)$time[2], (int)$time[0]); } elseif ($type == 'datetime') { diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php new file mode 100644 index 0000000..b13d453 --- /dev/null +++ b/tests/CollectionTest.php @@ -0,0 +1,195 @@ +assertCount (0, $collection); + + $collection->add ('a'); + $collection->add ('b'); + $collection->add ('c'); + $this->assertCount (3, $collection); + } + + public function testFirstAndLast () + { + $collection = new Collection (); + $collection->add ('first'); + $collection->add ('middle'); + $collection->add ('last'); + + $this->assertEquals ('first', $collection->first ()); + $this->assertEquals ('last', $collection->last ()); + } + + public function testFirstAndLastEmpty () + { + $collection = new Collection (); + $this->assertNull ($collection->first ()); + $this->assertNull ($collection->last ()); + } + + public function testIterator () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + $collection->add ('c'); + + $values = []; + foreach ($collection as $key => $value) { + $values[$key] = $value; + } + + $this->assertEquals ([0 => 'a', 1 => 'b', 2 => 'c'], $values); + } + + public function testRewind () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + + // Iterate to end + foreach ($collection as $v) {} + + // Rewind and verify + $collection->rewind (); + $this->assertEquals ('a', $collection->current ()); + } + + public function testArrayAccess () + { + $collection = new Collection (); + $collection[] = 'value1'; + $collection[] = 'value2'; + + $this->assertTrue (isset ($collection[0])); + $this->assertTrue (isset ($collection[1])); + $this->assertFalse (isset ($collection[2])); + + $this->assertEquals ('value1', $collection[0]); + $this->assertEquals ('value2', $collection[1]); + } + + public function testOffsetSet () + { + $collection = new Collection (); + $collection[5] = 'value5'; + + $this->assertTrue (isset ($collection[5])); + $this->assertEquals ('value5', $collection[5]); + } + + public function testOffsetUnset () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + + unset ($collection[0]); + $this->assertFalse (isset ($collection[0])); + } + + public function testRemove () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + $collection->add ('c'); + + $result = $collection->remove ('b'); + $this->assertTrue ($result); + $this->assertCount (2, $collection); + } + + public function testRemoveNonExistent () + { + $collection = new Collection (); + $collection->add ('a'); + + $result = $collection->remove ('nonexistent'); + $this->assertFalse ($result); + } + + public function testClear () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + + $collection->clear (); + $this->assertCount (0, $collection); + } + + public function testPeek () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + $collection->add ('c'); + + $collection->rewind (); + $this->assertEquals ('a', $collection->current ()); + $this->assertEquals ('b', $collection->peek ()); + // Position should not have changed + $this->assertEquals ('a', $collection->current ()); + } + + public function testPeekAtEnd () + { + $collection = new Collection (); + $collection->add ('a'); + + $collection->rewind (); + $this->assertNull ($collection->peek ()); + } + + public function testReverse () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + $collection->add ('c'); + + $collection->reverse (); + $this->assertEquals ('c', $collection->first ()); + $this->assertEquals ('a', $collection->last ()); + } + + public function testCurrentAtInvalidPosition () + { + $collection = new Collection (); + $this->assertNull ($collection->current ()); + } + + public function testValid () + { + $collection = new Collection (); + $collection->add ('a'); + + $collection->rewind (); + $this->assertTrue ($collection->valid ()); + $collection->next (); + $this->assertFalse ($collection->valid ()); + } + + public function testKey () + { + $collection = new Collection (); + $collection->add ('a'); + $collection->add ('b'); + + $collection->rewind (); + $this->assertEquals (0, $collection->key ()); + $collection->next (); + $this->assertEquals (1, $collection->key ()); + } +} diff --git a/tests/DbQueryTest.php b/tests/DbQueryTest.php index f145e6c..7c4035c 100644 --- a/tests/DbQueryTest.php +++ b/tests/DbQueryTest.php @@ -2,14 +2,14 @@ namespace Neuron\Tests; -use PHPUnit_Framework_TestCase; +use PHPUnit\Framework\TestCase; use Neuron\DB\Query; /** * Class DbQueryTest * @package Neuron\Tests */ -class DbQueryTest extends PHPUnit_Framework_TestCase +class DbQueryTest extends TestCase { /** * @test diff --git a/tests/ErrorCollectionTest.php b/tests/ErrorCollectionTest.php new file mode 100644 index 0000000..5b8786e --- /dev/null +++ b/tests/ErrorCollectionTest.php @@ -0,0 +1,55 @@ +addError ('Something went wrong'); + + $this->assertCount (1, $collection); + $this->assertInstanceOf (\Neuron\Models\Error::class, $error); + } + + public function testGetData () + { + $collection = new ErrorCollection (); + $collection->addError ('Error %s', ['one']); + $collection->addError ('Error %s', ['two']); + + $data = $collection->getData (); + $this->assertCount (2, $data); + $this->assertEquals ('Error one', $data[0]); + $this->assertEquals ('Error two', $data[1]); + } + + public function testGetDetailedData () + { + $collection = new ErrorCollection (); + $error = $collection->addError ('Error %s in %s', ['field', 'form']); + $error->setSubject ('test_subject'); + $error->setCode ('ERR001'); + + $detailed = $collection->getDetailedData (); + $this->assertCount (1, $detailed); + $this->assertEquals ('Error field in form', $detailed[0]['message']); + $this->assertEquals ('Error %s in %s', $detailed[0]['template']); + $this->assertEquals (['field', 'form'], $detailed[0]['arguments']); + $this->assertEquals ('test_subject', $detailed[0]['subject']); + $this->assertEquals ('ERR001', $detailed[0]['code']); + } + + public function testAddErrorWithNoArguments () + { + $collection = new ErrorCollection (); + $collection->addError ('Simple error message'); + + $data = $collection->getData (); + $this->assertEquals ('Simple error message', $data[0]); + } +} diff --git a/tests/FilterTest.php b/tests/FilterTest.php index e79f8f6..6fd1993 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -10,13 +10,13 @@ use Neuron\Filter\Context; use Neuron\Filter\Field; -use PHPUnit_Framework_TestCase; +use PHPUnit\Framework\TestCase; use Neuron\Filter\Parser; use Neuron\Filter\Scanner; class FilterTest - extends PHPUnit_Framework_TestCase + extends TestCase { public function testFilter () diff --git a/tests/SimpleCryptTest.php b/tests/SimpleCryptTest.php new file mode 100644 index 0000000..34851bc --- /dev/null +++ b/tests/SimpleCryptTest.php @@ -0,0 +1,81 @@ +encrypt ($original); + $this->assertNotEquals ($original, $encrypted); + + $decrypted = $crypt->decrypt ($encrypted); + $this->assertEquals ($original, $decrypted); + } + + public function testDifferentPasswordsFail () + { + $crypt1 = new SimpleCrypt ('password1'); + $crypt2 = new SimpleCrypt ('password2'); + + $encrypted = $crypt1->encrypt ('secret'); + $decrypted = $crypt2->decrypt ($encrypted); + + $this->assertNotEquals ('secret', $decrypted); + } + + public function testEncryptProducesDifferentOutput () + { + $crypt = new SimpleCrypt ('password'); + + $encrypted1 = $crypt->encrypt ('same text'); + $encrypted2 = $crypt->encrypt ('same text'); + + // Due to random salt, encrypted values should differ + $this->assertNotEquals ($encrypted1, $encrypted2); + } + + public function testEncryptDecryptEmptyString () + { + $crypt = new SimpleCrypt ('password'); + $encrypted = $crypt->encrypt (''); + $decrypted = $crypt->decrypt ($encrypted); + $this->assertEquals ('', $decrypted); + } + + public function testEncryptDecryptSpecialCharacters () + { + $crypt = new SimpleCrypt ('password'); + $original = "Special chars: !@#\$%^&*()_+-=[]{}|;':\",./<>?"; + + $encrypted = $crypt->encrypt ($original); + $decrypted = $crypt->decrypt ($encrypted); + $this->assertEquals ($original, $decrypted); + } + + public function testEncryptDecryptUTF8 () + { + $crypt = new SimpleCrypt ('password'); + $original = 'Héllo Wörld 日本語'; + + $encrypted = $crypt->encrypt ($original); + $decrypted = $crypt->decrypt ($encrypted); + $this->assertEquals ($original, $decrypted); + } + + public function testEncryptDecryptWithSaltMarkerInContent () + { + $crypt = new SimpleCrypt ('password'); + $original = 'Text with |||CWSALT inside it'; + + $encrypted = $crypt->encrypt ($original); + $decrypted = $crypt->decrypt ($encrypted); + $this->assertEquals ($original, $decrypted); + } +} diff --git a/tests/TokenGeneratorTest.php b/tests/TokenGeneratorTest.php index 2f301e9..a2f1693 100644 --- a/tests/TokenGeneratorTest.php +++ b/tests/TokenGeneratorTest.php @@ -3,11 +3,11 @@ namespace Neuron\Tests; -use PHPUnit_Framework_TestCase; +use PHPUnit\Framework\TestCase; class TokenGeneratorTest - extends PHPUnit_Framework_TestCase + extends TestCase { public function testLengthSimplified () { diff --git a/tests/ToolsTest.php b/tests/ToolsTest.php index 26cae51..6c92292 100644 --- a/tests/ToolsTest.php +++ b/tests/ToolsTest.php @@ -3,12 +3,12 @@ namespace Neuron\Tests; -use PHPUnit_Framework_TestCase; +use PHPUnit\Framework\TestCase; use Neuron\Core\Tools; class ToolsTest - extends PHPUnit_Framework_TestCase + extends TestCase { public function testEmailInputCheck () { @@ -102,4 +102,487 @@ public function testDateInput () { $this->assertTrue (Tools::checkInput ('2015-06-01T10:00', 'datetime')); $this->assertFalse (Tools::checkInput ('06-01-2015T10:00', 'datetime')); } + + // --------------------------------------------------------------- + // Date validation tests (the bug fix) + // --------------------------------------------------------------- + + public function testDateCheckInputValidDates () + { + $this->assertTrue (Tools::checkInput ('2015-06-01', 'date')); + $this->assertTrue (Tools::checkInput ('2000-01-01', 'date')); + $this->assertTrue (Tools::checkInput ('1999-12-31', 'date')); + $this->assertTrue (Tools::checkInput ('2024-02-29', 'date')); // leap year + } + + public function testDateCheckInputInvalidNonIntegerParts () + { + // The original bug: "a-b-c" would pass + $this->assertFalse (Tools::checkInput ('a-b-c', 'date')); + $this->assertFalse (Tools::checkInput ('foo-bar-baz', 'date')); + $this->assertFalse (Tools::checkInput ('20xx-01-01', 'date')); + $this->assertFalse (Tools::checkInput ('2015-ab-01', 'date')); + $this->assertFalse (Tools::checkInput ('2015-01-cd', 'date')); + } + + public function testDateCheckInputInvalidDateValues () + { + $this->assertFalse (Tools::checkInput ('2015-13-01', 'date')); // month 13 + $this->assertFalse (Tools::checkInput ('2015-00-01', 'date')); // month 0 + $this->assertFalse (Tools::checkInput ('2015-02-30', 'date')); // Feb 30 + $this->assertFalse (Tools::checkInput ('2023-02-29', 'date')); // non-leap year + $this->assertFalse (Tools::checkInput ('2015-06-32', 'date')); // day 32 + $this->assertFalse (Tools::checkInput ('0000-01-01', 'date')); // year 0 + } + + public function testDateCheckInputInvalidFormats () + { + $this->assertFalse (Tools::checkInput ('', 'date')); + $this->assertFalse (Tools::checkInput ('2015', 'date')); + $this->assertFalse (Tools::checkInput ('2015-06', 'date')); + $this->assertFalse (Tools::checkInput ('2015/06/01', 'date')); + $this->assertFalse (Tools::checkInput ('01-06-2015', 'date')); // wrong order but valid checkdate would pass + $this->assertFalse (Tools::checkInput ('2015-06-01-extra', 'date')); + } + + public function testDateGetInputValidDate () + { + $dat = array ('date' => '2015-06-01'); + $result = Tools::getInput ($dat, 'date', 'date'); + $this->assertIsInt ($result); + $this->assertEquals ('2015-06-01', date ('Y-m-d', $result)); + } + + public function testDateGetInputInvalidDate () + { + $dat = array ('date' => 'a-b-c'); + $result = Tools::getInput ($dat, 'date', 'date'); + $this->assertNull ($result); + } + + public function testDateGetInputMissing () + { + $dat = array (); + $result = Tools::getInput ($dat, 'date', 'date'); + $this->assertNull ($result); + } + + public function testDateGetInputDefault () + { + $dat = array ('date' => 'invalid'); + $result = Tools::getInput ($dat, 'date', 'date', 'default_value'); + $this->assertEquals ('default_value', $result); + } + + // --------------------------------------------------------------- + // Datetime validation tests + // --------------------------------------------------------------- + + public function testDatetimeCheckInputValid () + { + $this->assertTrue (Tools::checkInput ('2015-06-01T10:00', 'datetime')); + $this->assertTrue (Tools::checkInput ('2024-12-31T23:59', 'datetime')); + } + + public function testDatetimeCheckInputInvalid () + { + $this->assertFalse (Tools::checkInput ('06-01-2015T10:00', 'datetime')); + $this->assertFalse (Tools::checkInput ('not-a-datetime', 'datetime')); + $this->assertFalse (Tools::checkInput ('2015-06-01 10:00', 'datetime')); + $this->assertFalse (Tools::checkInput ('', 'datetime')); + } + + public function testDatetimeGetInputValid () + { + $dat = array ('dt' => '2015-06-01T10:00'); + $result = Tools::getInput ($dat, 'dt', 'datetime'); + $this->assertInstanceOf (\DateTime::class, $result); + } + + // --------------------------------------------------------------- + // Text and varchar type tests + // --------------------------------------------------------------- + + public function testTextCheckInput () + { + $this->assertTrue (Tools::checkInput ('anything', 'text')); + $this->assertTrue (Tools::checkInput ('', 'text')); + $this->assertTrue (Tools::checkInput ('', 'text')); + } + + public function testVarcharCheckInput () + { + $this->assertTrue (Tools::checkInput ('valid text', 'varchar')); + $this->assertTrue (Tools::checkInput ('valid text', 'string')); + $this->assertTrue (Tools::checkInput ('valid html', 'html')); + } + + public function testNameCheckInput () + { + $this->assertTrue (Tools::checkInput ('John Doe', 'name')); + $this->assertFalse (Tools::checkInput ('John', 'name')); + $this->assertFalse (Tools::checkInput ('', 'name')); + } + + // --------------------------------------------------------------- + // Bool type tests + // --------------------------------------------------------------- + + public function testBoolCheckInput () + { + $this->assertTrue (Tools::checkInput (1, 'bool')); + $this->assertTrue (Tools::checkInput ('true', 'bool')); + $this->assertFalse (Tools::checkInput (0, 'bool')); + $this->assertFalse (Tools::checkInput ('false', 'bool')); + $this->assertFalse (Tools::checkInput ('', 'bool')); + } + + public function testBoolGetInput () + { + // When checkInput passes (value is 1 or 'true'), getInput returns strip_tags(value) + $dat = array ('flag' => 1); + $this->assertEquals ('1', Tools::getInput ($dat, 'flag', 'bool')); + + $dat = array ('flag' => 'true'); + $this->assertEquals ('true', Tools::getInput ($dat, 'flag', 'bool')); + + // When bool validation fails, getInput returns false (special case) + $dat = array ('flag' => 0); + $this->assertFalse (Tools::getInput ($dat, 'flag', 'bool')); + + $dat = array ('flag' => 'no'); + $this->assertFalse (Tools::getInput ($dat, 'flag', 'bool')); + } + + // --------------------------------------------------------------- + // Password type tests + // --------------------------------------------------------------- + + public function testPasswordCheckInput () + { + $this->assertTrue (Tools::checkInput ('password123', 'password')); + $this->assertTrue (Tools::checkInput ('12345678', 'password')); + $this->assertFalse (Tools::checkInput ('short', 'password')); + $this->assertFalse (Tools::checkInput ('1234567', 'password')); // 7 chars + $this->assertFalse (Tools::checkInput (str_repeat ('a', 257), 'password')); // too long + $this->assertTrue (Tools::checkInput (str_repeat ('a', 256), 'password')); // max allowed + } + + // --------------------------------------------------------------- + // Username type tests + // --------------------------------------------------------------- + + public function testUsernameCheckInput () + { + $this->assertTrue (Tools::checkInput ('john_doe', 'username')); + $this->assertTrue (Tools::checkInput ('User123', 'username')); + $this->assertTrue (Tools::checkInput ('abc', 'username')); // min 3 chars + $this->assertFalse (Tools::checkInput ('ab', 'username')); // too short + $this->assertFalse (Tools::checkInput ('', 'username')); + $this->assertFalse (Tools::checkInput ('user name', 'username')); // space + $this->assertFalse (Tools::checkInput ('user@name', 'username')); // special chars + $this->assertFalse (Tools::checkInput (str_repeat ('a', 21), 'username')); // too long + } + + // --------------------------------------------------------------- + // MD5 type tests + // --------------------------------------------------------------- + + public function testMd5CheckInput () + { + $this->assertTrue (Tools::checkInput (md5 ('test'), 'md5')); + $this->assertTrue (Tools::checkInput ('d41d8cd98f00b204e9800998ecf8427e', 'md5')); + $this->assertFalse (Tools::checkInput ('tooshort', 'md5')); + $this->assertFalse (Tools::checkInput ('', 'md5')); + $this->assertFalse (Tools::checkInput (str_repeat ('a', 33), 'md5')); + } + + // --------------------------------------------------------------- + // Base64 type tests + // --------------------------------------------------------------- + + public function testBase64CheckInput () + { + $this->assertTrue (Tools::checkInput (base64_encode ('test'), 'base64')); + $this->assertTrue (Tools::checkInput (base64_encode ('hello world'), 'base64')); + $this->assertFalse (Tools::checkInput ('not valid base64!!!', 'base64')); + } + + public function testBase64GetInput () + { + $dat = array ('data' => base64_encode ('hello world')); + $result = Tools::getInput ($dat, 'data', 'base64'); + $this->assertEquals ('hello world', $result); + } + + // --------------------------------------------------------------- + // Raw type tests + // --------------------------------------------------------------- + + public function testRawCheckInput () + { + $this->assertTrue (Tools::checkInput ('anything', 'raw')); + $this->assertTrue (Tools::checkInput ('', 'raw')); + } + + public function testRawGetInput () + { + $dat = array ('data' => ''); + $result = Tools::getInput ($dat, 'data', 'raw'); + $this->assertEquals ('', $result); + } + + public function testHtmlGetInput () + { + $dat = array ('data' => 'bold'); + $result = Tools::getInput ($dat, 'data', 'html'); + $this->assertEquals ('bold', $result); + } + + // --------------------------------------------------------------- + // Unknown type tests + // --------------------------------------------------------------- + + public function testUnknownTypeReturnsFalse () + { + $this->assertFalse (Tools::checkInput ('value', 'nonexistent_type')); + } + + // --------------------------------------------------------------- + // getInput default behavior tests + // --------------------------------------------------------------- + + public function testGetInputMissingKey () + { + $dat = array (); + $this->assertNull (Tools::getInput ($dat, 'missing', 'text')); + } + + public function testGetInputMissingKeyWithDefault () + { + $dat = array (); + $this->assertEquals ('fallback', Tools::getInput ($dat, 'missing', 'text', 'fallback')); + } + + public function testGetInputStripsTagsByDefault () + { + $dat = array ('name' => 'John Doe'); + $result = Tools::getInput ($dat, 'name', 'varchar'); + $this->assertEquals ('John Doe', $result); + } + + // --------------------------------------------------------------- + // SQL injection patterns via checkInput + // --------------------------------------------------------------- + + public function testSqlInjectionInEmailField () + { + $this->assertFalse (Tools::checkInput ("' OR '1'='1", 'email')); + $this->assertFalse (Tools::checkInput ("admin@example.com' OR 1=1--", 'email')); + $this->assertFalse (Tools::checkInput ("admin@example.com'; DROP TABLE users;--", 'email')); + $this->assertFalse (Tools::checkInput ("' UNION SELECT * FROM users--", 'email')); + } + + public function testSqlInjectionInUsernameField () + { + $this->assertFalse (Tools::checkInput ("' OR '1'='1", 'username')); + $this->assertFalse (Tools::checkInput ("admin'; DROP TABLE users;--", 'username')); + $this->assertFalse (Tools::checkInput ("admin' UNION SELECT * FROM users--", 'username')); + $this->assertFalse (Tools::checkInput ("1; DROP TABLE users", 'username')); + } + + public function testSqlInjectionInDateField () + { + $this->assertFalse (Tools::checkInput ("'; DROP TABLE users;--", 'date')); + $this->assertFalse (Tools::checkInput ("2015-01-01' OR '1'='1", 'date')); + $this->assertFalse (Tools::checkInput ("2015-01-01; DROP TABLE users;--", 'date')); + $this->assertFalse (Tools::checkInput ("1 OR 1=1", 'date')); + $this->assertFalse (Tools::checkInput ("' UNION SELECT * FROM users--", 'date')); + } + + public function testSqlInjectionInDatetimeField () + { + $this->assertFalse (Tools::checkInput ("'; DROP TABLE users;--", 'datetime')); + $this->assertFalse (Tools::checkInput ("not-a-datetime", 'datetime')); + } + + public function testSqlInjectionInIntField () + { + $this->assertFalse (Tools::checkInput ("1; DROP TABLE users", 'int')); + $this->assertFalse (Tools::checkInput ("1 OR 1=1", 'int')); + $this->assertFalse (Tools::checkInput ("' OR '1'='1", 'int')); + $this->assertFalse (Tools::checkInput ("1 UNION SELECT * FROM users", 'int')); + } + + public function testSqlInjectionInNumberField () + { + $this->assertFalse (Tools::checkInput ("1; DROP TABLE users", 'number')); + $this->assertFalse (Tools::checkInput ("1 OR 1=1", 'number')); + $this->assertFalse (Tools::checkInput ("' OR '1'='1", 'number')); + } + + public function testSqlInjectionInMd5Field () + { + $this->assertFalse (Tools::checkInput ("' OR '1'='1' --", 'md5')); + $this->assertFalse (Tools::checkInput ("'; DROP TABLE users;--", 'md5')); + } + + public function testSqlInjectionInPasswordField () + { + // These pass checkInput because password only checks length/UTF-8 + // But they should still be properly escaped before use in queries + $this->assertTrue (Tools::checkInput ("password' OR '1'='1", 'password')); + } + + public function testSqlInjectionInUrlField () + { + $this->assertFalse (Tools::checkInput ("'; DROP TABLE users;--", 'url')); + $this->assertFalse (Tools::checkInput ("' OR '1'='1", 'url')); + } + + // --------------------------------------------------------------- + // XSS injection patterns via getInput + // --------------------------------------------------------------- + + public function testXssStrippedByGetInputDefault () + { + $dat = array ('name' => ''); + $result = Tools::getInput ($dat, 'name', 'varchar'); + $this->assertStringNotContainsString (''); + $this->assertStringNotContainsString ('