1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 12:24:49 +01:00

Taint specialized calls even when not using a variable (#4940)

This commit is contained in:
Adrien LUCAS 2021-01-06 20:14:52 +01:00 committed by Daniil Gentili
parent e67315424f
commit 63ddceaf8e
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
2 changed files with 132 additions and 108 deletions

View File

@ -241,130 +241,128 @@ class MethodCallReturnTypeFetcher
$is_declaring = (string) $declaring_method_id === (string) $method_id;
if ($method_storage->specialize_call) {
$var_id = ExpressionIdentifier::getArrayVarId(
$var_expr,
null,
$statements_analyzer
$var_id = ExpressionIdentifier::getArrayVarId(
$var_expr,
null,
$statements_analyzer
);
if ($method_storage->specialize_call && $var_id && isset($context->vars_in_scope[$var_id])) {
$var_nodes = [];
$parent_nodes = $context->vars_in_scope[$var_id]->parent_nodes;
$unspecialized_parent_nodes = \array_filter(
$parent_nodes,
function ($parent_node) {
return !$parent_node->specialization_key;
}
);
if ($var_id && isset($context->vars_in_scope[$var_id])) {
$var_nodes = [];
$parent_nodes = $context->vars_in_scope[$var_id]->parent_nodes;
$unspecialized_parent_nodes = \array_filter(
$parent_nodes,
function ($parent_node) {
return !$parent_node->specialization_key;
}
);
$specialized_parent_nodes = \array_filter(
$parent_nodes,
function ($parent_node) {
return (bool) $parent_node->specialization_key;
}
);
$var_node = DataFlowNode::getForAssignment(
$var_id,
new CodeLocation($statements_analyzer, $var_expr)
);
if ($method_storage->location) {
$this_parent_node = DataFlowNode::getForAssignment(
'$this in ' . $method_id,
$method_storage->location
);
foreach ($parent_nodes as $parent_node) {
$statements_analyzer->data_flow_graph->addPath($parent_node, $this_parent_node, '=');
}
$specialized_parent_nodes = \array_filter(
$parent_nodes,
function ($parent_node) {
return (bool) $parent_node->specialization_key;
}
);
$var_nodes[$var_node->id] = $var_node;
$var_node = DataFlowNode::getForAssignment(
$var_id,
new CodeLocation($statements_analyzer, $var_expr)
);
$method_call_nodes = [];
if ($method_storage->location) {
$this_parent_node = DataFlowNode::getForAssignment(
'$this in ' . $method_id,
$method_storage->location
);
if ($unspecialized_parent_nodes) {
$method_call_node = DataFlowNode::getForMethodReturn(
(string) $method_id,
$cased_method_id,
$is_declaring ? ($method_storage->signature_return_type_location
?: $method_storage->location) : null,
$node_location
);
$method_call_nodes[$method_call_node->id] = $method_call_node;
foreach ($parent_nodes as $parent_node) {
$statements_analyzer->data_flow_graph->addPath($parent_node, $this_parent_node, '=');
}
}
foreach ($specialized_parent_nodes as $parent_node) {
$universal_method_call_node = DataFlowNode::getForMethodReturn(
(string) $method_id,
$cased_method_id,
$is_declaring ? ($method_storage->signature_return_type_location
?: $method_storage->location) : null,
null
);
$var_nodes[$var_node->id] = $var_node;
$method_call_node = new DataFlowNode(
strtolower((string) $method_id),
$cased_method_id,
$is_declaring ? ($method_storage->signature_return_type_location
?: $method_storage->location) : null,
$parent_node->specialization_key
);
$method_call_nodes = [];
if ($unspecialized_parent_nodes) {
$method_call_node = DataFlowNode::getForMethodReturn(
(string) $method_id,
$cased_method_id,
$is_declaring ? ($method_storage->signature_return_type_location
?: $method_storage->location) : null,
$node_location
);
$method_call_nodes[$method_call_node->id] = $method_call_node;
}
foreach ($specialized_parent_nodes as $parent_node) {
$universal_method_call_node = DataFlowNode::getForMethodReturn(
(string) $method_id,
$cased_method_id,
$is_declaring ? ($method_storage->signature_return_type_location
?: $method_storage->location) : null,
null
);
$method_call_node = new DataFlowNode(
strtolower((string) $method_id),
$cased_method_id,
$is_declaring ? ($method_storage->signature_return_type_location
?: $method_storage->location) : null,
$parent_node->specialization_key
);
$statements_analyzer->data_flow_graph->addPath(
$universal_method_call_node,
$method_call_node,
'='
);
$method_call_nodes[$method_call_node->id] = $method_call_node;
}
foreach ($method_call_nodes as $method_call_node) {
$statements_analyzer->data_flow_graph->addNode($method_call_node);
foreach ($var_nodes as $var_node) {
$statements_analyzer->data_flow_graph->addNode($var_node);
$statements_analyzer->data_flow_graph->addPath(
$universal_method_call_node,
$method_call_node,
'='
$var_node,
'method-call-' . $method_id->method_name
);
}
if (!$is_declaring) {
$cased_declaring_method_id = $codebase->methods->getCasedMethodId($declaring_method_id);
$declaring_method_call_node = new DataFlowNode(
strtolower((string) $declaring_method_id),
$cased_declaring_method_id,
$method_storage->signature_return_type_location ?: $method_storage->location,
$method_call_node->specialization_key
);
$method_call_nodes[$method_call_node->id] = $method_call_node;
$statements_analyzer->data_flow_graph->addNode($declaring_method_call_node);
$statements_analyzer->data_flow_graph->addPath(
$declaring_method_call_node,
$method_call_node,
'parent'
);
}
foreach ($method_call_nodes as $method_call_node) {
$statements_analyzer->data_flow_graph->addNode($method_call_node);
foreach ($var_nodes as $var_node) {
$statements_analyzer->data_flow_graph->addNode($var_node);
$statements_analyzer->data_flow_graph->addPath(
$method_call_node,
$var_node,
'method-call-' . $method_id->method_name
);
}
if (!$is_declaring) {
$cased_declaring_method_id = $codebase->methods->getCasedMethodId($declaring_method_id);
$declaring_method_call_node = new DataFlowNode(
strtolower((string) $declaring_method_id),
$cased_declaring_method_id,
$method_storage->signature_return_type_location ?: $method_storage->location,
$method_call_node->specialization_key
);
$statements_analyzer->data_flow_graph->addNode($declaring_method_call_node);
$statements_analyzer->data_flow_graph->addPath(
$declaring_method_call_node,
$method_call_node,
'parent'
);
}
}
$return_type_candidate->parent_nodes = $method_call_nodes;
$stmt_var_type = clone $context->vars_in_scope[$var_id];
$stmt_var_type->parent_nodes = $var_nodes;
$context->vars_in_scope[$var_id] = $stmt_var_type;
}
$return_type_candidate->parent_nodes = $method_call_nodes;
$stmt_var_type = clone $context->vars_in_scope[$var_id];
$stmt_var_type->parent_nodes = $var_nodes;
$context->vars_in_scope[$var_id] = $stmt_var_type;
} else {
$method_call_node = DataFlowNode::getForMethodReturn(
(string) $method_id,

View File

@ -1591,6 +1591,32 @@ class TaintTest extends TestCase
echo $a->isUnsafe();',
'error_message' => 'TaintedHtml',
],
'taintSpecializedMethodForAnonymousInstance' => [
'<?php
/** @psalm-taint-specialize */
class Unsafe {
public function isUnsafe() {
return $_GET["unsafe"];
}
}
echo (new Unsafe())->isUnsafe();',
'error_message' => 'TaintedHtml',
],
'taintSpecializedMethodForStubMadeInstance' => [
'<?php
/** @psalm-taint-specialize */
class Unsafe {
public function isUnsafe() {
return $_GET["unsafe"];
}
}
/** @psalm-suppress InvalidReturnType */
function stub(): Unsafe { }
echo stub()->isUnsafe();',
'error_message' => 'TaintedHtml',
],
'doTaintSpecializedInstanceProperty' => [
'<?php
/** @psalm-taint-specialize */