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

Make sure conditional escaping works for static methods too

This commit is contained in:
Matt Brown 2020-11-22 13:39:32 -05:00 committed by Daniil Gentili
parent 30ee5334a1
commit aa4be209fc
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
4 changed files with 72 additions and 4 deletions

View File

@ -240,7 +240,8 @@ class StaticCallAnalyzer extends CallAnalyzer
MethodIdentifier $method_id,
string $cased_method_id,
Type\Union $return_type_candidate,
?\Psalm\Storage\MethodStorage $method_storage
?\Psalm\Storage\MethodStorage $method_storage,
?\Psalm\Internal\Type\TemplateResult $template_result
) : void {
if (!$statements_analyzer->data_flow_graph instanceof TaintFlowGraph
|| \in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
@ -271,7 +272,54 @@ class StaticCallAnalyzer extends CallAnalyzer
$statements_analyzer->data_flow_graph->addNode($method_source);
$return_type_candidate->parent_nodes = [$method_source->id => $method_source];
$codebase = $statements_analyzer->getCodebase();
$conditionally_removed_taints = [];
if ($method_storage && $template_result) {
foreach ($method_storage->conditionally_removed_taints as $conditionally_removed_taint) {
$conditionally_removed_taint = clone $conditionally_removed_taint;
$conditionally_removed_taint->replaceTemplateTypesWithArgTypes(
$template_result,
$codebase
);
$expanded_type = \Psalm\Internal\Type\TypeExpander::expandUnion(
$statements_analyzer->getCodebase(),
$conditionally_removed_taint,
null,
null,
null,
true,
true
);
foreach ($expanded_type->getLiteralStrings() as $literal_string) {
$conditionally_removed_taints[] = $literal_string->value;
}
}
}
if ($conditionally_removed_taints && $method_location) {
$assignment_node = DataFlowNode::getForAssignment(
$method_id . '-escaped',
$method_location,
$method_source->specialization_key
);
$statements_analyzer->data_flow_graph->addPath(
$method_source,
$assignment_node,
'conditionally-escaped',
[],
$conditionally_removed_taints
);
$return_type_candidate->parent_nodes[$assignment_node->id] = $assignment_node;
} else {
$return_type_candidate->parent_nodes = [$method_source->id => $method_source];
}
if ($method_storage && $method_storage->taint_source_types) {
$method_node = TaintSource::getForMethodReturn(

View File

@ -825,7 +825,8 @@ class AtomicStaticCallAnalyzer
$method_id,
(string) $method_id,
$return_type_candidate,
$method_storage
$method_storage,
null
);
}

View File

@ -508,7 +508,8 @@ class ExistingAtomicStaticCallAnalyzer
$method_id,
$cased_method_id,
$return_type_candidate,
$method_storage
$method_storage,
$template_result
);
if ($stmt_type = $statements_analyzer->node_data->getType($stmt)) {

View File

@ -578,6 +578,24 @@ class TaintTest extends TestCase
echo foo($_GET["foo"], true);
echo foo($_GET["foo"]);'
],
'conditionallyEscapedTaintPassedTrueStaticCall' => [
'<?php
class U {
/**
* @psalm-taint-escape ($escape is true ? "html" : null)
*/
public static function foo(string $string, bool $escape = true): string {
if ($escape) {
$string = htmlspecialchars($string);
}
return $string;
}
}
echo U::foo($_GET["foo"], true);
echo U::foo($_GET["foo"]);'
],
];
}