expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); } public function deleteUser(PDO $pdo) : void { $userId = $this->getAppendedUserId(); $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputFromReturnTypeToEcho() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); } public function deleteUser(PDO $pdo) : void { $userId = $this->getAppendedUserId(); echo $userId; } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputDirectly() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputDirectlySuppressed() { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputDirectlySuppressedWithOtherUse() { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'exec("delete from users where user_id = " . $userId); } public function deleteUserSafer(PDOWrapper $pdo) : void { $userId = $this->getSafeId(); $pdo->exec("delete from users where user_id = " . $userId); } public function getSafeId() : string { return "5"; } } class PDOWrapper { /** * @psalm-taint-sink $sql */ public function exec(string $sql) : void {} }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputFromReturnTypeWithBranch() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); if (rand(0, 1)) { $userId .= "aaa"; } else { $userId .= "bb"; } return $userId; } public function deleteUser(PDO $pdo) : void { $userId = $this->getAppendedUserId(); $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testSinkAnnotation() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); } public function deleteUser(PDOWrapper $pdo) : void { $userId = $this->getAppendedUserId(); $pdo->exec("delete from users where user_id = " . $userId); } } class PDOWrapper { /** * @psalm-taint-sink $sql */ public function exec(string $sql) : void {} }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputFromParam() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput - somefile.php:17:36 - in path $_GET (somefile.php:4) -> a::getuserid (somefile.php:3) out path a::getuserid (somefile.php:8) -> a::getappendeduserid (somefile.php:12) -> a::deleteuser#2 (somefile.php:16) -> pdo::exec#1 (somefile.php:17)'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); } public function doDelete(PDO $pdo) : void { $userId = $this->getAppendedUserId(); $this->deleteUser($pdo, $userId); } public function deleteUser(PDO $pdo, string $userId) : void { $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputToParam() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'deleteUser( $pdo, $this->getAppendedUserId((string) $_GET["user_id"]) ); } public function getAppendedUserId(string $user_id) : string { return "aaa" . $user_id; } public function deleteUser(PDO $pdo, string $userId) : void { $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputToParamAfterAssignment() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'deleteUser( $pdo, $this->getAppendedUserId((string) $_GET["user_id"]) ); } public function getAppendedUserId(string $user_id) : string { return "aaa" . $user_id; } public function deleteUser(PDO $pdo, string $userId) : void { $userId2 = $userId; $pdo->exec("delete from users where user_id = " . $userId2); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputToParamButSafe() { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'deleteUser( $pdo, $this->getAppendedUserId((string) $_GET["user_id"]) ); } public function getAppendedUserId(string $user_id) : string { return "aaa" . $user_id; } public function deleteUser(PDO $pdo, string $userId) : void { $userId2 = strlen($userId); $pdo->exec("delete from users where user_id = " . $userId2); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputToParamAlternatePath() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput - somefile.php:23:40 - in path $_GET (somefile.php:7) -> a::getappendeduserid#1 (somefile.php:7) -> a::getappendeduserid (somefile.php:11) -> a::deleteuser#3 (somefile.php:7) out path a::deleteuser#3 (somefile.php:19) -> pdo::exec#1 (somefile.php:23)'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'deleteUser( $pdo, self::doFoo(), $this->getAppendedUserId((string) $_GET["user_id"]) ); } public function getAppendedUserId(string $user_id) : string { return "aaa" . $user_id; } public static function doFoo() : string { return "hello"; } public function deleteUser(PDO $pdo, string $userId, string $userId2) : void { $pdo->exec("delete from users where user_id = " . $userId); if (rand(0, 1)) { $pdo->exec("delete from users where user_id = " . $userId2); } } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInParentLoader() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput - somefile.php:16:40 - in path $_GET (somefile.php:28) -> c::foo#1 (somefile.php:28) out path c::foo#1 (somefile.php:23) -> agrandchild::loadfull#1 (somefile.php:6) -> a::loadpartial#1 (somefile.php:16) -> pdo::exec#1 (somefile.php:16)'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'exec("select * from foo where bar = " . $sink); } } class AGrandChild extends AChild {} class C { public function foo(string $user_id) : void { AGrandChild::loadFull($user_id); } } (new C)->foo((string) $_GET["user_id"]);' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testValidatedInputFromParam() { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); validateUserId($userId); $this->deleteUser($pdo, $userId); } public function deleteUser(PDO $pdo, string $userId) : void { $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testUntaintedInput() { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'getUserId(); } public function deleteUser(PDO $pdo) : void { $userId = $this->getAppendedUserId(); $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputFromProperty() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'userId = (string) $_GET["user_id"]; } public function getAppendedUserId() : string { return "aaaa" . $this->userId; } public function doDelete(PDO $pdo) : void { $userId = $this->getAppendedUserId(); $this->deleteUser($pdo, $userId); } public function deleteUser(PDO $pdo, string $userId) : void { $pdo->exec("delete from users where user_id = " . $userId); } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputViaStaticFunction() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testTaintedInputViaPureStaticFunction() { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } /** * @return void */ public function testUntaintedInputViaStaticFunction() { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintedInputFromMagicProperty() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', ' */ private $vars = []; public function __get(string $s) : string { return $this->vars[$s]; } public function __set(string $s, string $t) { $this->vars[$s] = $t; } } function getAppendedUserId() : void { $a = new A(); $a->userId = (string) $_GET["user_id"]; echo $a->userId; }' ); $this->analyzeFile('somefile.php', new Context()); } public function testTaintOverMixed() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintStrConversion() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintHtmlEntities() : void { $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintIntoExec() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintIntoExecMultipleConcat() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintIntoNestedArrayUnnestedSeparately() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintIntoArrayAndThenOutAgain() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } public function testTaintAppendedToArray() : void { $this->expectException(\Psalm\Exception\CodeException::class); $this->expectExceptionMessage('TaintedInput'); $this->project_analyzer->trackTaintedInputs(); $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } }