diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php index ed33ae83e..856b52e3b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php @@ -4,6 +4,7 @@ namespace Psalm\Internal\Analyzer\Statements\Expression; use PhpParser; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; +use Psalm\Internal\Taint\Sink; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Issue\ForbiddenCode; @@ -24,6 +25,28 @@ class PrintAnalyzer return false; } + if ($codebase->taint + && $codebase->config->trackTaintsInPath($statements_analyzer->getFilePath()) + ) { + $call_location = new CodeLocation($statements_analyzer->getSource(), $stmt); + + $print_param_sink = Sink::getForMethodArgument( + 'print', + 'print', + 0, + null, + $call_location + ); + + $print_param_sink->taints = [ + Type\TaintKind::INPUT_HTML, + Type\TaintKind::USER_SECRET, + Type\TaintKind::SYSTEM_SECRET + ]; + + $codebase->taint->addSink($print_param_sink); + } + if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) { if (Call\ArgumentAnalyzer::verifyType( $statements_analyzer, diff --git a/tests/TaintTest.php b/tests/TaintTest.php index d4b21e34a..9ee55d4a1 100644 --- a/tests/TaintTest.php +++ b/tests/TaintTest.php @@ -1878,4 +1878,20 @@ class TaintTest extends TestCase $this->analyzeFile('somefile.php', new Context()); } + + public function testTaintedInstancePrint() : void + { + $this->expectException(\Psalm\Exception\CodeException::class); + $this->expectExceptionMessage('TaintedInput - somefile.php:2:23 - Detected tainted html in path: $_GET -> $_GET[\'name\'] (somefile.php:2:23) -> call to print (somefile.php:2:23) -> print#1'); + + $this->project_analyzer->trackTaintedInputs(); + + $this->addFile( + 'somefile.php', + 'analyzeFile('somefile.php', new Context()); + } }