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

Support taint flows in more functions

This commit is contained in:
Brown 2020-06-22 17:53:03 -04:00
parent 7f05b3c530
commit e8be2c500e
7 changed files with 84 additions and 21 deletions

View File

@ -468,8 +468,9 @@ class CommentAnalyzer
}
if (isset($parsed_docblock->tags['psalm-flow'])) {
$flow = trim(reset($parsed_docblock->tags['psalm-flow']));
$info->flow = $flow;
foreach ($parsed_docblock->tags['psalm-flow'] as $param) {
$info->flows[] = trim($param);
}
}
if (isset($parsed_docblock->tags['psalm-taint-sink'])) {

View File

@ -1092,7 +1092,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
}
}
foreach ($function_storage->return_source_params as $i) {
foreach ($function_storage->return_source_params as $i => $path_type) {
if (!isset($stmt->args[$i])) {
continue;
}
@ -1115,7 +1115,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
$codebase->taint->addPath(
$function_param_sink,
$function_return_sink,
'arg',
$path_type,
$function_storage->added_taints,
$removed_taints
);

View File

@ -2515,21 +2515,35 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
$storage->added_taints = $docblock_info->added_taints;
$storage->removed_taints = $docblock_info->removed_taints;
if ($docblock_info->flow) {
$flow_parts = explode('->', $docblock_info->flow);
if ($docblock_info->flows) {
foreach ($docblock_info->flows as $flow) {
$path_type = 'arg';
if (isset($flow_parts[1]) && trim($flow_parts[1]) === 'return') {
$source_param_string = trim($flow_parts[0]);
$fancy_path_regex = '/-\(([a-z\-]+)\)->/';
if ($source_param_string[0] === '(' && substr($source_param_string, -1) === ')') {
$source_params = preg_split('/, ?/', substr($source_param_string, 1, -1));
if (preg_match($fancy_path_regex, $flow, $matches)) {
if (isset($matches[1])) {
$path_type = $matches[1];
}
foreach ($source_params as $source_param) {
$source_param = substr($source_param, 1);
$flow = preg_replace($fancy_path_regex, '->', $flow);
}
foreach ($storage->params as $i => $param_storage) {
if ($param_storage->name === $source_param) {
$storage->return_source_params[] = $i;
$flow_parts = explode('->', $flow);
if (isset($flow_parts[1]) && trim($flow_parts[1]) === 'return') {
$source_param_string = trim($flow_parts[0]);
if ($source_param_string[0] === '(' && substr($source_param_string, -1) === ')') {
$source_params = preg_split('/, ?/', substr($source_param_string, 1, -1));
foreach ($source_params as $source_param) {
$source_param = substr($source_param, 1);
foreach ($storage->params as $i => $param_storage) {
if ($param_storage->name === $source_param) {
$storage->return_source_params[$i] = $path_type;
}
}
}
}

View File

@ -91,9 +91,9 @@ class FunctionDocblockComment
/**
* Represents the flow from function params to return type
*
* @var ?string
* @var array<string>
*/
public $flow;
public $flows = [];
/**
* @var array<string>

View File

@ -480,6 +480,27 @@ function strtolower(string $str) : string {}
*/
function strtoupper(string $str) : string {}
/**
* @psalm-pure
*
* @psalm-flow ($str) -> return
*/
function trim(string $str, string $character_mask = " \t\n\r\0\x0B") : string {}
/**
* @psalm-pure
*
* @psalm-flow ($str) -> return
*/
function ltrim(string $str, string $character_mask = " \t\n\r\0\x0B") : string {}
/**
* @psalm-pure
*
* @psalm-flow ($str) -> return
*/
function rtrim(string $str, string $character_mask = " \t\n\r\0\x0B") : string {}
/**
* @psalm-pure
*
@ -493,10 +514,18 @@ function strtoupper(string $str) : string {}
* : string
* )
*
* @psalm-flow ($glue, $pieces) -> return
* @psalm-flow ($glue) -> return
* @psalm-flow ($pieces) -(array-fetch)-> return
*/
function implode($glue, array $pieces = []) : string {}
/**
* @psalm-pure
*
* @psalm-flow ($string) -(array-assignment)-> return
*/
function explode(string $delimiter, string $string, int $limit = -1) : array {}
/**
* @param array $input
*
@ -514,7 +543,7 @@ function array_sum(array $input) {}
/**
* @psalm-pure
*
* @psalm-taint-remove html
* @psalm-taint-escape html
* @psalm-flow ($str) -> return
*/
function strip_tags(string $str, ?string $allowable_tags = null) : string {}
@ -522,7 +551,7 @@ function strip_tags(string $str, ?string $allowable_tags = null) : string {}
/**
* @psalm-pure
*
* @psalm-taint-remove html
* @psalm-taint-escape html
*
* @psalm-flow ($string) -> return
*/

View File

@ -205,7 +205,7 @@ abstract class FunctionLikeStorage
public $removed_taints = [];
/**
* @var list<int>
* @var array<int, string>
*/
public $return_source_params = [];

View File

@ -1698,4 +1698,23 @@ class TaintTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testImplodeExplode() : void
{
$this->expectException(\Psalm\Exception\CodeException::class);
$this->expectExceptionMessage('TaintedInput');
$this->project_analyzer->trackTaintedInputs();
$this->addFile(
'somefile.php',
'<?php
$a = $_GET["name"];
$b = explode(" ", $a);
$c = implode(" ", $b);
echo $c;'
);
$this->analyzeFile('somefile.php', new Context());
}
}