From 80cdd87be0a60bf3ff6c6a5666dd8c5c60058a44 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 9 Sep 2024 21:54:22 +0800 Subject: [PATCH 1/2] [10.x] Fixes `whereDate`, `whereDay`, `whereMonth`, `whereTime`, `whereYear` and `whereJsonLength` to ignore invalid `$operator` (#52704) * Fixes `whereDate`, `whereDay`, `whereMonth`, `whereTime`, `whereYear` and `whereJsonLength` to ignore invalid `$operator` Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki --------- Signed-off-by: Mior Muhammad Zaki --- .../Database/DBAL/TimestampType.php | 10 +- src/Illuminate/Database/Query/Builder.php | 42 ++++ .../Integration/Database/QueryBuilderTest.php | 208 ++++++++++++++++++ 3 files changed, 258 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/DBAL/TimestampType.php b/src/Illuminate/Database/DBAL/TimestampType.php index b5d9777503d9..e344dd919ae8 100644 --- a/src/Illuminate/Database/DBAL/TimestampType.php +++ b/src/Illuminate/Database/DBAL/TimestampType.php @@ -4,14 +4,17 @@ use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDb1010Platform; use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MariaDb1052Platform; use Doctrine\DBAL\Platforms\MariaDb1060Platform; use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySQL80Platform; +use Doctrine\DBAL\Platforms\MySQL84Platform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQL100Platform; +use Doctrine\DBAL\Platforms\PostgreSQL120Platform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -33,13 +36,16 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st MySQLPlatform::class, MySQL57Platform::class, MySQL80Platform::class, + MySQL84Platform::class, MariaDBPlatform::class, MariaDb1027Platform::class, MariaDb1052Platform::class, - MariaDb1060Platform::class => $this->getMySqlPlatformSQLDeclaration($column), + MariaDb1060Platform::class, + MariaDb1010Platform::class => $this->getMySqlPlatformSQLDeclaration($column), PostgreSQLPlatform::class, PostgreSQL94Platform::class, - PostgreSQL100Platform::class => $this->getPostgresPlatformSQLDeclaration($column), + PostgreSQL100Platform::class, + PostgreSQL120Platform::class => $this->getPostgresPlatformSQLDeclaration($column), SQLServerPlatform::class, SQLServer2012Platform::class => $this->getSqlServerPlatformSQLDeclaration($column), SqlitePlatform::class => 'DATETIME', diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 9f87562f01d6..52b38a6d2902 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1436,6 +1436,13 @@ public function whereDate($column, $operator, $value = null, $boolean = 'and') $value, $operator, func_num_args() === 2 ); + // If the given operator is not found in the list of valid operators we will + // assume that the developer is just short-cutting the '=' operators and + // we will set the operators to '=' and set the values appropriately. + if ($this->invalidOperator($operator)) { + [$value, $operator] = [$operator, '=']; + } + $value = $this->flattenValue($value); if ($value instanceof DateTimeInterface) { @@ -1477,6 +1484,13 @@ public function whereTime($column, $operator, $value = null, $boolean = 'and') $value, $operator, func_num_args() === 2 ); + // If the given operator is not found in the list of valid operators we will + // assume that the developer is just short-cutting the '=' operators and + // we will set the operators to '=' and set the values appropriately. + if ($this->invalidOperator($operator)) { + [$value, $operator] = [$operator, '=']; + } + $value = $this->flattenValue($value); if ($value instanceof DateTimeInterface) { @@ -1518,6 +1532,13 @@ public function whereDay($column, $operator, $value = null, $boolean = 'and') $value, $operator, func_num_args() === 2 ); + // If the given operator is not found in the list of valid operators we will + // assume that the developer is just short-cutting the '=' operators and + // we will set the operators to '=' and set the values appropriately. + if ($this->invalidOperator($operator)) { + [$value, $operator] = [$operator, '=']; + } + $value = $this->flattenValue($value); if ($value instanceof DateTimeInterface) { @@ -1563,6 +1584,13 @@ public function whereMonth($column, $operator, $value = null, $boolean = 'and') $value, $operator, func_num_args() === 2 ); + // If the given operator is not found in the list of valid operators we will + // assume that the developer is just short-cutting the '=' operators and + // we will set the operators to '=' and set the values appropriately. + if ($this->invalidOperator($operator)) { + [$value, $operator] = [$operator, '=']; + } + $value = $this->flattenValue($value); if ($value instanceof DateTimeInterface) { @@ -1608,6 +1636,13 @@ public function whereYear($column, $operator, $value = null, $boolean = 'and') $value, $operator, func_num_args() === 2 ); + // If the given operator is not found in the list of valid operators we will + // assume that the developer is just short-cutting the '=' operators and + // we will set the operators to '=' and set the values appropriately. + if ($this->invalidOperator($operator)) { + [$value, $operator] = [$operator, '=']; + } + $value = $this->flattenValue($value); if ($value instanceof DateTimeInterface) { @@ -1974,6 +2009,13 @@ public function whereJsonLength($column, $operator, $value = null, $boolean = 'a $value, $operator, func_num_args() === 2 ); + // If the given operator is not found in the list of valid operators we will + // assume that the developer is just short-cutting the '=' operators and + // we will set the operators to '=' and set the values appropriately. + if ($this->invalidOperator($operator)) { + [$value, $operator] = [$operator, '=']; + } + $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean'); if (! $value instanceof ExpressionContract) { diff --git a/tests/Integration/Database/QueryBuilderTest.php b/tests/Integration/Database/QueryBuilderTest.php index 98ae0792b212..c323ff0e1513 100644 --- a/tests/Integration/Database/QueryBuilderTest.php +++ b/tests/Integration/Database/QueryBuilderTest.php @@ -9,6 +9,9 @@ use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; +use Illuminate\Testing\Assert as PHPUnit; +use Orchestra\Testbench\Attributes\DefineEnvironment; +use PDOException; class QueryBuilderTest extends DatabaseTestCase { @@ -305,12 +308,52 @@ public function testWhereDate() $this->assertSame(1, DB::table('posts')->whereDate('created_at', new Carbon('2018-01-02'))->count()); } + #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')] + public function testWhereDateWithInvalidOperator() + { + $sql = DB::table('posts')->whereDate('created_at', '? OR 1=1', '2018-01-02'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'created_at', + 'type' => 'Date', + 'value' => '? OR 1=1', + 'boolean' => 'and', + ], + ], $sql->wheres); + + $this->assertSame(0, $sql->count()); + } + public function testOrWhereDate() { $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereDate('created_at', '2018-01-02')->count()); $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereDate('created_at', new Carbon('2018-01-02'))->count()); } + #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')] + public function testOrWhereDateWithInvalidOperator() + { + $sql = DB::table('posts')->where('id', 1)->orWhereDate('created_at', '? OR 1=1', '2018-01-02'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'id', + 'type' => 'Basic', + 'value' => 1, + 'boolean' => 'and', + ], + [ + 'column' => 'created_at', + 'type' => 'Date', + 'value' => '? OR 1=1', + 'boolean' => 'or', + ], + ], $sql->wheres); + + $this->assertSame(1, $sql->count()); + } + public function testWhereDay() { $this->assertSame(1, DB::table('posts')->whereDay('created_at', '02')->count()); @@ -318,6 +361,22 @@ public function testWhereDay() $this->assertSame(1, DB::table('posts')->whereDay('created_at', new Carbon('2018-01-02'))->count()); } + public function testWhereDayWithInvalidOperator() + { + $sql = DB::table('posts')->whereDay('created_at', '? OR 1=1', '02'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'created_at', + 'type' => 'Day', + 'value' => '00', + 'boolean' => 'and', + ], + ], $sql->wheres); + + $this->assertSame(0, $sql->count()); + } + public function testOrWhereDay() { $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereDay('created_at', '02')->count()); @@ -325,6 +384,28 @@ public function testOrWhereDay() $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereDay('created_at', new Carbon('2018-01-02'))->count()); } + public function testOrWhereDayWithInvalidOperator() + { + $sql = DB::table('posts')->where('id', 1)->orWhereDay('created_at', '? OR 1=1', '02'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'id', + 'type' => 'Basic', + 'value' => 1, + 'boolean' => 'and', + ], + [ + 'column' => 'created_at', + 'type' => 'Day', + 'value' => '00', + 'boolean' => 'or', + ], + ], $sql->wheres); + + $this->assertSame(1, $sql->count()); + } + public function testWhereMonth() { $this->assertSame(1, DB::table('posts')->whereMonth('created_at', '01')->count()); @@ -332,6 +413,22 @@ public function testWhereMonth() $this->assertSame(1, DB::table('posts')->whereMonth('created_at', new Carbon('2018-01-02'))->count()); } + public function testWhereMonthWithInvalidOperator() + { + $sql = DB::table('posts')->whereMonth('created_at', '? OR 1=1', '01'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'created_at', + 'type' => 'Month', + 'value' => '00', + 'boolean' => 'and', + ], + ], $sql->wheres); + + $this->assertSame(0, $sql->count()); + } + public function testOrWhereMonth() { $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereMonth('created_at', '01')->count()); @@ -339,6 +436,28 @@ public function testOrWhereMonth() $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereMonth('created_at', new Carbon('2018-01-02'))->count()); } + public function testOrWhereMonthWithInvalidOperator() + { + $sql = DB::table('posts')->where('id', 1)->orWhereMonth('created_at', '? OR 1=1', '01'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'id', + 'type' => 'Basic', + 'value' => 1, + 'boolean' => 'and', + ], + [ + 'column' => 'created_at', + 'type' => 'Month', + 'value' => '00', + 'boolean' => 'or', + ], + ], $sql->wheres); + + $this->assertSame(1, $sql->count()); + } + public function testWhereYear() { $this->assertSame(1, DB::table('posts')->whereYear('created_at', '2018')->count()); @@ -346,6 +465,23 @@ public function testWhereYear() $this->assertSame(1, DB::table('posts')->whereYear('created_at', new Carbon('2018-01-02'))->count()); } + #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')] + public function testWhereYearWithInvalidOperator() + { + $sql = DB::table('posts')->whereYear('created_at', '? OR 1=1', '2018'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'created_at', + 'type' => 'Year', + 'value' => '? OR 1=1', + 'boolean' => 'and', + ], + ], $sql->wheres); + + $this->assertSame(0, $sql->count()); + } + public function testOrWhereYear() { $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereYear('created_at', '2018')->count()); @@ -353,18 +489,81 @@ public function testOrWhereYear() $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereYear('created_at', new Carbon('2018-01-02'))->count()); } + #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')] + public function testOrWhereYearWithInvalidOperator() + { + $sql = DB::table('posts')->where('id', 1)->orWhereYear('created_at', '? OR 1=1', '2018'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'id', + 'type' => 'Basic', + 'value' => 1, + 'boolean' => 'and', + ], + [ + 'column' => 'created_at', + 'type' => 'Year', + 'value' => '? OR 1=1', + 'boolean' => 'or', + ], + ], $sql->wheres); + + $this->assertSame(1, $sql->count()); + } + public function testWhereTime() { $this->assertSame(1, DB::table('posts')->whereTime('created_at', '03:04:05')->count()); $this->assertSame(1, DB::table('posts')->whereTime('created_at', new Carbon('2018-01-02 03:04:05'))->count()); } + #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')] + public function testWhereTimeWithInvalidOperator() + { + $sql = DB::table('posts')->whereTime('created_at', '? OR 1=1', '03:04:05'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'created_at', + 'type' => 'Time', + 'value' => '? OR 1=1', + 'boolean' => 'and', + ], + ], $sql->wheres); + + $this->assertSame(0, $sql->count()); + } + public function testOrWhereTime() { $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereTime('created_at', '03:04:05')->count()); $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereTime('created_at', new Carbon('2018-01-02 03:04:05'))->count()); } + #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')] + public function testOrWhereTimeWithInvalidOperator() + { + $sql = DB::table('posts')->where('id', 1)->orWhereTime('created_at', '? OR 1=1', '03:04:05'); + + PHPUnit::assertArraySubset([ + [ + 'column' => 'id', + 'type' => 'Basic', + 'value' => 1, + 'boolean' => 'and', + ], + [ + 'column' => 'created_at', + 'type' => 'Time', + 'value' => '? OR 1=1', + 'boolean' => 'or', + ], + ], $sql->wheres); + + $this->assertSame(1, $sql->count()); + } + public function testWhereNested() { $results = DB::table('posts')->where('content', 'Lorem Ipsum.')->whereNested(function ($query) { @@ -436,4 +635,13 @@ public function testPluck() 'Lorem Ipsum.' => 'Bar Post', ], DB::table('posts')->pluck('title', 'content')->toArray()); } + + protected function defineEnvironmentWouldThrowsPDOException($app) + { + $this->afterApplicationCreated(function () { + if (in_array($this->driver, ['pgsql', 'sqlsrv'])) { + $this->expectException(PDOException::class); + } + }); + } } From 58c20532011fd4db5b5d6e62dd1d668b89ed2daa Mon Sep 17 00:00:00 2001 From: Will Rowe Date: Thu, 12 Sep 2024 10:51:40 -0400 Subject: [PATCH 2/2] Fix arguments passed to artisan commands that start with 'env' (#52748) * Add failing tests * Ensure that there is an equal sign so that argument names starting with 'env' are not matched --- .../Foundation/EnvironmentDetector.php | 2 +- .../FoundationEnvironmentDetectorTest.php | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/EnvironmentDetector.php b/src/Illuminate/Foundation/EnvironmentDetector.php index b2747bc6800e..8fa61bd2e983 100644 --- a/src/Illuminate/Foundation/EnvironmentDetector.php +++ b/src/Illuminate/Foundation/EnvironmentDetector.php @@ -65,7 +65,7 @@ protected function getEnvironmentArgument(array $args) return $args[$i + 1] ?? null; } - if (str_starts_with($value, '--env')) { + if (str_starts_with($value, '--env=')) { return head(array_slice(explode('=', $value), 1)); } } diff --git a/tests/Foundation/FoundationEnvironmentDetectorTest.php b/tests/Foundation/FoundationEnvironmentDetectorTest.php index d302c375bf50..c06a8ac386dd 100644 --- a/tests/Foundation/FoundationEnvironmentDetectorTest.php +++ b/tests/Foundation/FoundationEnvironmentDetectorTest.php @@ -46,4 +46,34 @@ public function testConsoleEnvironmentDetectionWithNoValue() }, ['--env']); $this->assertSame('foobar', $result); } + + public function testConsoleEnvironmentDetectionDoesNotUseArgumentThatStartsWithEnv() + { + $env = new EnvironmentDetector; + + $result = $env->detect(function () { + return 'foobar'; + }, ['--envelope=mail']); + $this->assertSame('foobar', $result); + } + + public function testConsoleEnvironmentDetectionDoesNotUseArgumentThatStartsWithEnvSeparatedWithSpace() + { + $env = new EnvironmentDetector; + + $result = $env->detect(function () { + return 'foobar'; + }, ['--envelope', 'mail']); + $this->assertSame('foobar', $result); + } + + public function testConsoleEnvironmentDetectionDoesNotUseArgumentThatStartsWithEnvWithNoValue() + { + $env = new EnvironmentDetector; + + $result = $env->detect(function () { + return 'foobar'; + }, ['--envelope']); + $this->assertSame('foobar', $result); + } }