diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php index da8e3794b7c..bc799edd290 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php @@ -32,6 +32,7 @@ use Psalm\Type\Union; use UnexpectedValueException; +use function array_merge; use function in_array; use function strlen; @@ -170,6 +171,14 @@ public static function analyze( $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event); if ($stmt_left_type && $stmt_left_type->parent_nodes) { + // numeric types can't be tainted html or has_quotes, neither can bool + if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph + && $stmt_left_type->isSingle() + && ($stmt_left_type->isInt() || $stmt_left_type->isFloat() || $stmt_left_type->isBool()) + ) { + $removed_taints = array_merge($removed_taints, array('html', 'has_quotes')); + } + foreach ($stmt_left_type->parent_nodes as $parent_node) { $statements_analyzer->data_flow_graph->addPath( $parent_node, @@ -182,6 +191,14 @@ public static function analyze( } if ($stmt_right_type && $stmt_right_type->parent_nodes) { + // numeric types can't be tainted html or has_quotes, neither can bool + if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph + && $stmt_right_type->isSingle() + && ($stmt_right_type->isInt() || $stmt_right_type->isFloat() || $stmt_right_type->isBool()) + ) { + $removed_taints = array_merge($removed_taints, array('html', 'has_quotes')); + } + foreach ($stmt_right_type->parent_nodes as $parent_node) { $statements_analyzer->data_flow_graph->addPath( $parent_node, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 43da070b4d6..2d8380ceb69 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -61,6 +61,7 @@ use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Union; +use function array_merge; use function count; use function explode; use function implode; @@ -1490,19 +1491,19 @@ private static function processTaintedness( return; } - // numeric types can't be tainted, neither can bool + $event = new AddRemoveTaintsEvent($expr, $context, $statements_analyzer, $codebase); + + $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event); + $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event); + + // numeric types can't be tainted html or has_quotes, neither can bool if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph && $input_type->isSingle() && ($input_type->isInt() || $input_type->isFloat() || $input_type->isBool()) ) { - return; + $removed_taints = array_merge($removed_taints, array('html', 'has_quotes')); } - $event = new AddRemoveTaintsEvent($expr, $context, $statements_analyzer, $codebase); - - $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event); - $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event); - if ($function_param->type && $function_param->type->isString() && !$input_type->isString()) { $input_type = CastAnalyzer::castStringAttempt( $statements_analyzer, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 83825307c32..12285e2432e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -142,14 +142,9 @@ public static function analyze( } } - if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph - ) { - $type = new Union([new TBool()], [ - 'parent_nodes' => $maybe_type->parent_nodes ?? [], - ]); - } else { - $type = Type::getBool(); - } + $type = new Union([new TBool()], [ + 'parent_nodes' => $maybe_type->parent_nodes ?? [], + ]); $statements_analyzer->node_data->setType($stmt, $type); @@ -328,11 +323,7 @@ public static function castIntAttempt( $atomic_types = $stmt_type->getAtomicTypes(); - $parent_nodes = []; - - if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) { - $parent_nodes = $stmt_type->parent_nodes; - } + $parent_nodes = $stmt_type->parent_nodes; while ($atomic_types) { $atomic_type = array_pop($atomic_types); @@ -518,11 +509,7 @@ public static function castFloatAttempt( $atomic_types = $stmt_type->getAtomicTypes(); - $parent_nodes = []; - - if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) { - $parent_nodes = $stmt_type->parent_nodes; - } + $parent_nodes = $stmt_type->parent_nodes; while ($atomic_types) { $atomic_type = array_pop($atomic_types); diff --git a/tests/TaintTest.php b/tests/TaintTest.php index d5dd0e1dc34..faa299957d4 100644 --- a/tests/TaintTest.php +++ b/tests/TaintTest.php @@ -177,23 +177,6 @@ public function deleteUser(PDO $pdo, string $userId) : void { } }', ], - 'untaintedInputAfterIntCast' => [ - 'code' => 'getUserId(); - } - - public function deleteUser(PDO $pdo) : void { - $userId = $this->getAppendedUserId(); - $pdo->exec("delete from users where user_id = " . $userId); - } - }', - ], 'specializedCoreFunctionCall' => [ 'code' => ' [ - 'code' => ' [ 'code' => ' 'TaintedSql', ], + 'taintedInputAfterIntCast' => [ + 'code' => 'getUserId(); + } + + public function deleteUser(PDO $pdo) : void { + $userId = $this->getAppendedUserId(); + $pdo->exec("delete from users where user_id = " . $userId); + } + }', + 'error_message' => 'TaintedSql', + ], + 'TaintForIntTypeCastUsingAnnotatedSink' => [ + 'code' => ' 'TaintedSql', + ], 'taintedInputFromReturnTypeWithBranch' => [ 'code' => '