Browse Source

Merge branch 'master' into 3.1

Mark Story 10 years ago
parent
commit
0b7ee45c53

+ 1 - 1
Makefile

@@ -164,7 +164,7 @@ component-%:
 	- (git branch -D $* 2> /dev/null)
 	git checkout -b $*
 	git filter-branch --prune-empty --subdirectory-filter src/$(shell php -r "echo ucfirst('$*');") -f $*
-	git push $* $*:master
+	git push $* $*:$(CURRENT_BRANCH)
 	git checkout $(CURRENT_BRANCH) > /dev/null
 
 tag-component-%: component-% guard-VERSION guard-GITHUB_USER

+ 27 - 3
src/Database/Expression/QueryExpression.php

@@ -340,7 +340,7 @@ class QueryExpression implements ExpressionInterface, Countable
      */
     public function and_($conditions, $types = [])
     {
-        if (!is_string($conditions) && is_callable($conditions)) {
+        if ($this->isCallable($conditions)) {
             return $conditions(new self([], $this->typeMap()->types($types)));
         }
         return new self($conditions, $this->typeMap()->types($types));
@@ -357,7 +357,7 @@ class QueryExpression implements ExpressionInterface, Countable
      */
     public function or_($conditions, $types = [])
     {
-        if (!is_string($conditions) && is_callable($conditions)) {
+        if ($this->isCallable($conditions)) {
             return $conditions(new self([], $this->typeMap()->types($types), 'OR'));
         }
         return new self($conditions, $this->typeMap()->types($types), 'OR');
@@ -491,6 +491,30 @@ class QueryExpression implements ExpressionInterface, Countable
     }
 
     /**
+     * Check whether or not a callable is acceptable.
+     *
+     * We don't accept ['class', 'method'] style callbacks,
+     * as they often contain user input and arrays of strings
+     * are easy to sneak in.
+     *
+     * @param callable $c The callable to check.
+     * @return bool Valid callable.
+     */
+    public function isCallable($c)
+    {
+        if (is_string($c)) {
+            return false;
+        }
+        if (is_object($c) && is_callable($c)) {
+            return true;
+        }
+        if (is_array($c) && isset($c[0]) && is_object($c[0]) && is_callable($c)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Auxiliary function used for decomposing a nested array of conditions and build
      * a tree structure inside this object to represent the full SQL expression.
      * String conditions are stored directly in the conditions, while any other
@@ -513,7 +537,7 @@ class QueryExpression implements ExpressionInterface, Countable
                 continue;
             }
 
-            if (!is_string($c) && is_callable($c)) {
+            if ($this->isCallable($c)) {
                 $expr = new QueryExpression([], $typeMap);
                 $c = $c($expr, $this);
             }

+ 1 - 1
src/Database/Query.php

@@ -1653,7 +1653,7 @@ class Query implements ExpressionInterface, IteratorAggregate
             return;
         }
 
-        if (!is_string($append) && is_callable($append)) {
+        if ($expression->isCallable($append)) {
             $append = $append($this->newExpr(), $this);
         }
 

+ 7 - 1
src/Validation/Validation.php

@@ -231,8 +231,11 @@ class Validation
         if (is_array($check1)) {
             extract($check1, EXTR_OVERWRITE);
         }
-        $operator = str_replace([' ', "\t", "\n", "\r", "\0", "\x0B"], '', strtolower($operator));
+        if ((float)$check1 != $check1) {
+            return false;
+        }
 
+        $operator = str_replace([' ', "\t", "\n", "\r", "\0", "\x0B"], '', strtolower($operator));
         switch ($operator) {
             case 'isgreater':
             case '>':
@@ -725,6 +728,9 @@ class Validation
         if (!is_numeric($check)) {
             return false;
         }
+        if ((float)$check != $check) {
+            return false;
+        }
         if (isset($lower) && isset($upper)) {
             return ($check >= $lower && $check <= $upper);
         }

+ 24 - 0
tests/TestCase/Database/QueryTest.php

@@ -1180,6 +1180,30 @@ class QueryTest extends TestCase
     }
 
     /**
+     * Tests where() with callable types.
+     *
+     * @return void
+     */
+    public function testWhereCallables()
+    {
+        $query = new Query($this->connection);
+        $query->select(['id'])
+            ->from('articles')
+            ->where([
+                'id' => '\Cake\Error\Debugger::dump',
+                'title' => ['\Cake\Error\Debugger', 'dump'],
+                'author_id' => function ($exp) {
+                    return 1;
+                }
+            ]);
+        $this->assertQuotedQuery(
+            'SELECT <id> FROM <articles> WHERE \(<id> = :c0 AND <title> = :c1 AND <author_id> = :c2\)',
+            $query->sql(),
+            !$this->autoQuote
+        );
+    }
+
+    /**
      * Tests that empty values don't set where clauses.
      *
      * @return void

+ 37 - 0
tests/TestCase/Validation/ValidationTest.php

@@ -844,6 +844,26 @@ class ValidationTest extends TestCase
         $this->assertFalse(Validation::comparison(7, '==', 6));
         $this->assertFalse(Validation::comparison(7, 'not equal', 7));
         $this->assertFalse(Validation::comparison(7, '!=', 7));
+
+        $this->assertTrue(Validation::comparison('6.5', '!=', 6));
+        $this->assertTrue(Validation::comparison('6.5', '<', 7));
+    }
+
+    /**
+     * Test comparison casting values before comparisons.
+     *
+     * @return void
+     */
+    public function testComparisonTypeChecks()
+    {
+        $this->assertFalse(Validation::comparison('\x028', '>=', 1), 'hexish encoding fails');
+        $this->assertFalse(Validation::comparison('0b010', '>=', 1), 'binary string data fails');
+        $this->assertFalse(Validation::comparison('0x01', '>=', 1), 'hex string data fails');
+        $this->assertFalse(Validation::comparison('0x1', '>=', 1), 'hex string data fails');
+
+        $this->assertFalse(Validation::comparison('\x028', '>=', 1.5), 'hexish encoding fails');
+        $this->assertFalse(Validation::comparison('0b010', '>=', 1.5), 'binary string data fails');
+        $this->assertFalse(Validation::comparison('0x02', '>=', 1.5), 'hex string data fails');
     }
 
     /**
@@ -2075,6 +2095,23 @@ class ValidationTest extends TestCase
     }
 
     /**
+     * Test range type checks
+     *
+     * @return void
+     */
+    public function testRangeTypeChecks()
+    {
+        $this->assertFalse(Validation::range('\x028', 1, 5), 'hexish encoding fails');
+        $this->assertFalse(Validation::range('0b010', 1, 5), 'binary string data fails');
+        $this->assertFalse(Validation::range('0x01', 1, 5), 'hex string data fails');
+        $this->assertFalse(Validation::range('0x1', 1, 5), 'hex string data fails');
+
+        $this->assertFalse(Validation::range('\x028', 1, 5), 'hexish encoding fails');
+        $this->assertFalse(Validation::range('0b010', 1, 5), 'binary string data fails');
+        $this->assertFalse(Validation::range('0x02', 1, 5), 'hex string data fails');
+    }
+
+    /**
      * testExtension method
      *
      * @return void