1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Perform string casting for taints in ArgumentAnalyzer

This commit is contained in:
Brown 2020-06-29 13:21:33 -04:00
parent 1a582fa636
commit f6e2e0a84a
5 changed files with 62 additions and 15 deletions

View File

@ -41,6 +41,7 @@ class EchoAnalyzer
$expr_type = CastAnalyzer::castStringAttempt(
$statements_analyzer,
$context,
$expr_type,
$expr,
false
);

View File

@ -8,6 +8,7 @@ use Psalm\Internal\Analyzer\MethodAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
use Psalm\Internal\Analyzer\Statements\Expression\CastAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Analyzer\TypeAnalyzer;
use Psalm\Internal\Taint\Sink;
@ -346,7 +347,7 @@ class ArgumentAnalyzer
if ($cased_method_id) {
$arg_location = new CodeLocation($statements_analyzer->getSource(), $arg->value);
self::processTaintedness(
$arg_type = self::processTaintedness(
$statements_analyzer,
$cased_method_id,
$argument_offset,
@ -354,6 +355,8 @@ class ArgumentAnalyzer
$function_call_location,
$function_param,
$arg_type,
$arg->value,
$context,
$specialize_taint
);
}
@ -479,7 +482,7 @@ class ArgumentAnalyzer
}
if ($cased_method_id) {
self::processTaintedness(
$input_type = self::processTaintedness(
$statements_analyzer,
$cased_method_id,
$argument_offset,
@ -487,6 +490,8 @@ class ArgumentAnalyzer
$function_call_location,
$function_param,
$input_type,
$input_expr,
$context,
$specialize_taint
);
}
@ -542,7 +547,7 @@ class ArgumentAnalyzer
}
if ($cased_method_id) {
self::processTaintedness(
$input_type = self::processTaintedness(
$statements_analyzer,
$cased_method_id,
$argument_offset,
@ -550,6 +555,8 @@ class ArgumentAnalyzer
$function_call_location,
$function_param,
$input_type,
$input_expr,
$context,
$specialize_taint
);
}
@ -628,7 +635,7 @@ class ArgumentAnalyzer
if ($cased_method_id) {
$old_input_type = $input_type;
self::processTaintedness(
$input_type = self::processTaintedness(
$statements_analyzer,
$cased_method_id,
$argument_offset,
@ -636,6 +643,8 @@ class ArgumentAnalyzer
$function_call_location,
$function_param,
$input_type,
$input_expr,
$context,
$specialize_taint
);
@ -1135,13 +1144,25 @@ class ArgumentAnalyzer
CodeLocation $arg_location,
CodeLocation $function_call_location,
FunctionLikeParameter $function_param,
Type\Union &$input_type,
Type\Union $input_type,
PhpParser\Node\Expr $expr,
Context $context,
bool $specialize_taint
) : void {
) : Type\Union {
$codebase = $statements_analyzer->getCodebase();
if (!$codebase->taint || !$codebase->config->trackTaintsInPath($statements_analyzer->getFilePath())) {
return;
return $input_type;
}
if ($function_param->type && $function_param->type->isString()) {
$input_type = CastAnalyzer::castStringAttempt(
$statements_analyzer,
$context,
$input_type,
$expr,
false
);
}
if ($specialize_taint) {
@ -1243,5 +1264,7 @@ class ArgumentAnalyzer
$input_type = clone $input_type;
$input_type->parent_nodes = [];
}
return $input_type;
}
}

View File

@ -91,7 +91,13 @@ class CastAnalyzer
$stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr);
if ($stmt_expr_type) {
$stmt_type = self::castStringAttempt($statements_analyzer, $context, $stmt->expr, true);
$stmt_type = self::castStringAttempt(
$statements_analyzer,
$context,
$stmt_expr_type,
$stmt->expr,
true
);
} else {
$stmt_type = Type::getString();
}
@ -177,17 +183,12 @@ class CastAnalyzer
public static function castStringAttempt(
StatementsAnalyzer $statements_analyzer,
Context $context,
Type\Union $stmt_type,
PhpParser\Node\Expr $stmt,
bool $explicit_cast = false
) : Type\Union {
$codebase = $statements_analyzer->getCodebase();
$stmt_type = $statements_analyzer->node_data->getType($stmt);
if (!$stmt_type) {
return Type::getString();
}
$invalid_casts = [];
$valid_strings = [];
$castable_types = [];

View File

@ -27,7 +27,12 @@ class EncapsulatedStringAnalyzer
$part_type = $statements_analyzer->node_data->getType($part);
if ($part_type) {
$casted_part_type = CastAnalyzer::castStringAttempt($statements_analyzer, $context, $part);
$casted_part_type = CastAnalyzer::castStringAttempt(
$statements_analyzer,
$context,
$part_type,
$part
);
if ($codebase->taint
&& $codebase->config->trackTaintsInPath($statements_analyzer->getFilePath())

View File

@ -1251,6 +1251,23 @@ class TaintTest extends TestCase
echo $unsafe;',
'error_message' => 'TaintedInput',
],
'castToStringViaArgument' => [
'<?php
class MyClass {
public function __toString() {
return $_GET["blah"];
}
}
function doesEcho(string $s) {
echo $s;
}
$unsafe = new MyClass();
doesEcho($unsafe);',
'error_message' => 'TaintedInput',
],
'toStringTaintInSubclass' => [
'<?php // --taint-analysis
class TaintedBaseClass {