From de7b65ea8a36ae6db1a6fc13eff0de95b7d16485 Mon Sep 17 00:00:00 2001 From: Oliver Hader Date: Sat, 24 Feb 2024 14:44:16 +0100 Subject: [PATCH] Use TaintKind/TaintKindGroup constants instead of string values --- .../Call/FunctionCallReturnTypeFetcher.php | 7 +++--- .../Reflector/FunctionLikeDocblockParser.php | 7 ++++-- .../Reflector/FunctionLikeDocblockScanner.php | 25 ++++++++++++------- .../AddRemoveTaints/HtmlFunctionTainter.php | 21 ++++++++-------- src/Psalm/Type/TaintKindGroup.php | 2 ++ 5 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index 55557576e6b..74400b70a2b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -39,6 +39,7 @@ use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TString; +use Psalm\Type\TaintKind; use Psalm\Type\Union; use UnexpectedValueException; @@ -646,9 +647,9 @@ private static function taintReturnType( $pattern = substr($pattern, 2, -1); if (self::simpleExclusion($pattern, $first_arg_value[0])) { - $removed_taints[] = 'html'; - $removed_taints[] = 'has_quotes'; - $removed_taints[] = 'sql'; + $removed_taints[] = TaintKind::INPUT_HTML; + $removed_taints[] = TaintKind::INPUT_HAS_QUOTES; + $removed_taints[] = TaintKind::INPUT_SQL; } } } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index 7bedbf9e27f..66fc0fafa94 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -14,6 +14,7 @@ use Psalm\Internal\Scanner\ParsedDocblock; use Psalm\Issue\InvalidDocblock; use Psalm\IssueBuffer; +use Psalm\Type\TaintKindGroup; use function array_keys; use function array_shift; @@ -264,10 +265,11 @@ public static function parse( $taint_type = substr($taint_type, 5); if ($taint_type === 'tainted') { - $taint_type = 'input'; + $taint_type = TaintKindGroup::GROUP_INPUT; } if ($taint_type === 'misc') { + // @todo `text` is semantically not defined in `TaintKind`, maybe drop it $taint_type = 'text'; } @@ -305,10 +307,11 @@ public static function parse( if ($param_parts[0]) { if ($param_parts[0] === 'tainted') { - $param_parts[0] = 'input'; + $param_parts[0] = TaintKindGroup::GROUP_INPUT; } if ($param_parts[0] === 'misc') { + // @todo `text` is semantically not defined in `TaintKind`, maybe drop it $param_parts[0] = 'text'; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index 270529ee306..cfeb0b15f30 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -50,6 +50,9 @@ use function array_filter; use function array_merge; +use function array_search; +use function array_splice; +use function array_unique; use function array_values; use function count; use function explode; @@ -352,16 +355,20 @@ public static function addDocblockInfo( } } - foreach ($docblock_info->taint_source_types as $taint_source_type) { - if ($taint_source_type === 'input') { - $storage->taint_source_types = array_merge( - $storage->taint_source_types, - TaintKindGroup::ALL_INPUT, - ); - } else { - $storage->taint_source_types[] = $taint_source_type; - } + $docblock_info->taint_source_types = array_values(array_unique($docblock_info->taint_source_types)); + // expand 'input' group to all items, e.g. `['other', 'input']` -> `['other', 'html', 'sql', 'shell', ...]` + $inputIndex = array_search(TaintKindGroup::GROUP_INPUT, $docblock_info->taint_source_types, true); + if ($inputIndex !== false) { + array_splice( + $docblock_info->taint_source_types, + $inputIndex, + 1, + TaintKindGroup::ALL_INPUT, + ); } + // merge taints from doc block to storage, enforce uniqueness and having consecutive index keys + $storage->taint_source_types = array_merge($storage->taint_source_types, $docblock_info->taint_source_types); + $storage->taint_source_types = array_values(array_unique($storage->taint_source_types)); $storage->added_taints = $docblock_info->added_taints; diff --git a/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php b/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php index d49ebcee48b..bfb973faba0 100644 --- a/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php +++ b/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php @@ -7,6 +7,7 @@ use Psalm\Plugin\EventHandler\AddTaintsInterface; use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent; use Psalm\Plugin\EventHandler\RemoveTaintsInterface; +use Psalm\Type\TaintKind; use function count; use function strtolower; @@ -47,24 +48,24 @@ public static function addTaints(AddRemoveTaintsEvent $event): array if ($second_arg === null) { if ($statements_analyzer->getCodebase()->analysis_php_version_id >= 8_01_00) { - return ['html', 'has_quotes']; + return [TaintKind::INPUT_HTML, TaintKind::INPUT_HAS_QUOTES]; } - return ['html']; + return [TaintKind::INPUT_HTML]; } $second_arg_value = $statements_analyzer->node_data->getType($second_arg); if (!$second_arg_value || !$second_arg_value->isSingleIntLiteral()) { - return ['html']; + return [TaintKind::INPUT_HTML]; } $second_arg_value = $second_arg_value->getSingleIntLiteral()->value; if (($second_arg_value & ENT_QUOTES) === ENT_QUOTES) { - return ['html', 'has_quotes']; + return [TaintKind::INPUT_HTML, TaintKind::INPUT_HAS_QUOTES]; } - return ['html']; + return [TaintKind::INPUT_HTML]; } return []; @@ -99,24 +100,24 @@ public static function removeTaints(AddRemoveTaintsEvent $event): array if ($second_arg === null) { if ($statements_analyzer->getCodebase()->analysis_php_version_id >= 8_01_00) { - return ['html', 'has_quotes']; + return [TaintKind::INPUT_HTML, TaintKind::INPUT_HAS_QUOTES]; } - return ['html']; + return [TaintKind::INPUT_HTML]; } $second_arg_value = $statements_analyzer->node_data->getType($second_arg); if (!$second_arg_value || !$second_arg_value->isSingleIntLiteral()) { - return ['html']; + return [TaintKind::INPUT_HTML]; } $second_arg_value = $second_arg_value->getSingleIntLiteral()->value; if (($second_arg_value & ENT_QUOTES) === ENT_QUOTES) { - return ['html', 'has_quotes']; + return [TaintKind::INPUT_HTML, TaintKind::INPUT_HAS_QUOTES]; } - return ['html']; + return [TaintKind::INPUT_HTML]; } return []; diff --git a/src/Psalm/Type/TaintKindGroup.php b/src/Psalm/Type/TaintKindGroup.php index eb74a2916a6..8f144569a5e 100644 --- a/src/Psalm/Type/TaintKindGroup.php +++ b/src/Psalm/Type/TaintKindGroup.php @@ -7,6 +7,8 @@ */ final class TaintKindGroup { + public const GROUP_INPUT = 'input'; + public const ALL_INPUT = [ TaintKind::INPUT_HTML, TaintKind::INPUT_HAS_QUOTES,