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

Break out TaintedInput issues into a lot of separate ones

This commit is contained in:
Matt Brown 2020-11-17 12:44:31 -05:00
parent fda2377812
commit 43af3b1a57
33 changed files with 477 additions and 103 deletions

View File

@ -371,7 +371,17 @@
<xs:element name="ReferenceConstraintViolation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ReservedWord" type="IssueHandlerType" minOccurs="0" />
<xs:element name="StringIncrement" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedCustom" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedEval" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedHtml" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedInclude" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedInput" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedShell" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedSql" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedSystemSecret" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedText" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedUnserialize" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TaintedUserSecret" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TooFewArguments" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="TooManyArguments" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="TooManyTemplateParams" type="FunctionIssueHandlerType" minOccurs="0" />

View File

@ -4,12 +4,12 @@
return [
'exec' => [['shell']],
'create_function' => [['text'], ['text']],
'create_function' => [['text'], ['eval']],
'file_get_contents' => [['text']],
'file_put_contents' => [['shell']],
'fopen' => [['shell']],
'header' => [['text']],
'igbinary_unserialize' => [['text']],
'igbinary_unserialize' => [['unserialize']],
'ldap_search' => [['text']],
'mysqli_query' => [[], ['sql']],
'mysqli::query' => [['sql']],
@ -38,7 +38,7 @@ return [
'setcookie' => [['text'], ['text']],
'shell_exec' => [['shell']],
'system' => [['shell']],
'unserialize' => [['text']],
'unserialize' => [['unserialize']],
'popen' => [['shell']],
'proc_open' => [['shell']],
];

View File

@ -0,0 +1,3 @@
# TaintedCustom
Emitted when tainted input detection is turned on and custom-tainted data is detected.

View File

@ -0,0 +1,17 @@
# TaintedEval
Tainted input detected to an `eval` call.
Passing untrusted user input to `eval` calls is dangerous, as it allows arbitrary data to be executed on your server.
```php
<?php
$name = $_GET["name"];
evalCode($name);
function evalCode(string $name) {
eval($name);
}
```

View File

@ -0,0 +1,15 @@
# TaintedHtml
Emitted when tainted input detection is turned on and tainted HTML is detected.
```php
<?php
$name = $_GET["name"];
printName($name);
function printName(string $name) {
echo $name;
}
```

View File

@ -0,0 +1,17 @@
# TaintedInclude
Tainted input detected to an `include` or `require` call.
Passing untrusted user input to `include` calls is dangerous, as it can allow an attacker to execute arbitrary scripts on your server.
```php
<?php
$name = $_GET["name"];
includeCode($name);
function includeCode(string $name) : void {
include($name . '.php');
}
```

View File

@ -0,0 +1,15 @@
# TaintedShell
Emitted when tainted input detection is turned on and tainted shell code is detected.
```php
<?php
$command = $_GET["command"];
runCode($command);
function runCode(string $command) {
exec($command);
}
```

View File

@ -0,0 +1,18 @@
# TaintedSql
Emitted when tainted input detection is turned on and tainted SQL is detected.
```php
<?php
class A {
public function deleteUser(PDO $pdo) : void {
$userId = self::getUserId();
$pdo->exec("delete from users where user_id = " . $userId);
}
public static function getUserId() : string {
return (string) $_GET["user_id"];
}
}
```

View File

@ -0,0 +1,16 @@
# TaintedSystemSecret
Emitted when tainted input detection is turned on and data marked as a system secret is detected somewhere it shouldnt be.
```php
<?php
/**
* @psalm-taint-source system_secret
*/
function getConfigValue(string $data) {
return "$omePa$$word";
}
echo getConfigValue("secret");
```

View File

@ -0,0 +1,19 @@
# TaintedText
Emitted when tainted input detection is turned on and tainted text is detected somewhere unexpected.
This can lead to dangerous situations, like running arbitrary functions.
```php
<?php
$name = $_GET["name"];
evalCode($name);
function evalCode(string $name) {
if (is_callable($name)) {
$name();
}
}
```

View File

@ -0,0 +1,19 @@
# TaintedUnserialize
Tainted input detected to an `unserialize` call.
Passing untrusted user input to `unserialize` calls is dangerous from the [PHP documentation on unserialize](https://www.php.net/manual/en/function.unserialize.php):
> Do not pass untrusted user input to unserialize() regardless of the options value of allowed_classes. Unserialization can result in code being loaded and executed due to object instantiation and autoloading, and a malicious user may be able to exploit this. Use a safe, standard data interchange format such as JSON (via json_decode() and json_encode()) if you need to pass serialized data to the user.
```php
<?php
$command = $_GET["data"];
getObject($command);
function getObject(string $data) : object {
return unserialize($data);
}
```

View File

@ -0,0 +1,20 @@
# TaintedUserSecret
Emitted when tainted input detection is turned on and data marked as a user secret is detected somewhere it shouldnt be.
```php
<?php
class User {
/**
* @psalm-taint-source user_secret
*/
public function getPassword() : string {
return "$omePa$$word";
}
}
function showUserPassword(User $user) {
echo $user->getPassword();
}
```

View File

@ -741,6 +741,29 @@ class FunctionCallAnalyzer extends CallAnalyzer
return [false, null, null, null];
}
if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
&& $stmt_name_type->parent_nodes
&& !\in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
) {
$arg_location = new CodeLocation($statements_analyzer->getSource(), $function_name);
$custom_call_sink = \Psalm\Internal\DataFlow\TaintSink::getForMethodArgument(
'variable-call',
'variable-call',
0,
$arg_location,
$arg_location
);
$custom_call_sink->taints = [\Psalm\Type\TaintKind::INPUT_TEXT];
$statements_analyzer->data_flow_graph->addSink($custom_call_sink);
foreach ($stmt_name_type->parent_nodes as $parent_node) {
$statements_analyzer->data_flow_graph->addPath($parent_node, $custom_call_sink, 'call');
}
}
}
if (!$statements_analyzer->node_data->getType($real_stmt)) {

View File

@ -38,7 +38,7 @@ class EvalAnalyzer
$arg_location
);
$eval_param_sink->taints = [\Psalm\Type\TaintKind::INPUT_TEXT];
$eval_param_sink->taints = [\Psalm\Type\TaintKind::INPUT_EVAL];
$statements_analyzer->data_flow_graph->addSink($eval_param_sink);

View File

@ -116,7 +116,7 @@ class IncludeAnalyzer
$arg_location
);
$include_param_sink->taints = [\Psalm\Type\TaintKind::INPUT_TEXT];
$include_param_sink->taints = [\Psalm\Type\TaintKind::INPUT_INCLUDE];
$statements_analyzer->data_flow_graph->addSink($include_param_sink);

View File

@ -3,11 +3,21 @@
namespace Psalm\Internal\Codebase;
use Psalm\CodeLocation;
use Psalm\Internal\DataFlow\DataFlowNode;
use Psalm\Internal\DataFlow\TaintSink;
use Psalm\Internal\DataFlow\TaintSource;
use Psalm\Internal\DataFlow\DataFlowNode;
use Psalm\Issue\TaintedCustom;
use Psalm\Issue\TaintedEval;
use Psalm\Issue\TaintedHtml;
use Psalm\Issue\TaintedInclude;
use Psalm\Issue\TaintedShell;
use Psalm\Issue\TaintedSql;
use Psalm\Issue\TaintedSystemSecret;
use Psalm\Issue\TaintedText;
use Psalm\Issue\TaintedUnserialize;
use Psalm\Issue\TaintedUserSecret;
use Psalm\IssueBuffer;
use Psalm\Issue\TaintedInput;
use Psalm\Type\TaintKind;
use function array_merge;
use function count;
use function implode;
@ -242,16 +252,103 @@ class TaintFlowGraph extends DataFlowGraph
$issue_location = $generated_source->code_location;
}
if (IssueBuffer::accepts(
new TaintedInput(
'Detected tainted ' . implode(', ', $matching_taints),
$issue_location,
$this->getIssueTrace($generated_source),
$this->getPredecessorPath($generated_source)
. ' -> ' . $this->getSuccessorPath($sinks[$to_id])
)
)) {
// fall through
$issue_trace = $this->getIssueTrace($generated_source);
$path = $this->getPredecessorPath($generated_source)
. ' -> ' . $this->getSuccessorPath($sinks[$to_id]);
foreach ($matching_taints as $matching_taint) {
switch ($matching_taint) {
case TaintKind::INPUT_TEXT:
$issue = new TaintedText(
'Detected tainted text',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::INPUT_UNSERIALIZE:
$issue = new TaintedUnserialize(
'Detected tainted code passed to unserialize or similar',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::INPUT_INCLUDE:
$issue = new TaintedInclude(
'Detected tainted code passed to include or similar',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::INPUT_EVAL:
$issue = new TaintedEval(
'Detected tainted code passed to eval or similar',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::INPUT_SQL:
$issue = new TaintedSql(
'Detected tainted SQL',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::INPUT_HTML:
$issue = new TaintedHtml(
'Detected tainted HTML',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::INPUT_SHELL:
$issue = new TaintedShell(
'Detected tainted shell code',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::USER_SECRET:
$issue = new TaintedUserSecret(
'Detected tainted user secret leaking',
$issue_location,
$issue_trace,
$path
);
break;
case TaintKind::SYSTEM_SECRET:
$issue = new TaintedSystemSecret(
'Detected tainted system secret leaking',
$issue_location,
$issue_trace,
$path
);
break;
default:
$issue = new TaintedCustom(
'Detected tainted ' . $matching_taint,
$issue_location,
$issue_trace,
$path
);
}
IssueBuffer::accepts($issue);
}
continue;

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedCustom extends TaintedInput
{
public const SHORTCODE = 249;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedEval extends TaintedInput
{
public const SHORTCODE = 252;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedHtml extends TaintedInput
{
public const SHORTCODE = 245;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedInclude extends TaintedInput
{
public const SHORTCODE = 251;
}

View File

@ -4,7 +4,7 @@ namespace Psalm\Issue;
use Psalm\Internal\Analyzer\DataFlowNodeData;
use Psalm\CodeLocation;
class TaintedInput extends CodeIssue
abstract class TaintedInput extends CodeIssue
{
public const ERROR_LEVEL = -2;
public const SHORTCODE = 205;

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedShell extends TaintedInput
{
public const SHORTCODE = 246;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedSql extends TaintedInput
{
public const SHORTCODE = 244;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedSystemSecret extends TaintedInput
{
public const SHORTCODE = 248;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedText extends TaintedInput
{
public const SHORTCODE = 243;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedUnserialize extends TaintedInput
{
public const SHORTCODE = 250;
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class TaintedUserSecret extends TaintedInput
{
public const SHORTCODE = 247;
}

View File

@ -104,7 +104,7 @@ class IssueBuffer
public static function addUnusedSuppression(string $file_path, int $offset, string $issue_type) : void
{
if ($issue_type === 'TaintedInput') {
if (\substr($issue_type, 0, 7) === 'Tainted') {
return;
}
@ -204,7 +204,9 @@ class IssueBuffer
return false;
}
if ($project_analyzer->getCodebase()->taint_flow_graph && $issue_type !== 'TaintedInput') {
$is_tainted = \substr($issue_type, 0, 7) === 'Tainted';
if ($project_analyzer->getCodebase()->taint_flow_graph && !$is_tainted) {
return false;
}
@ -227,7 +229,7 @@ class IssueBuffer
. ' ' . $e->dupe_key;
if ($reporting_level === Config::REPORT_INFO) {
if ($issue_type === 'TaintedInput' || !self::alreadyEmitted($emitted_key)) {
if ($is_tainted || !self::alreadyEmitted($emitted_key)) {
self::$issues_data[$e->getFilePath()][] = $e->toIssueData(Config::REPORT_INFO);
if ($is_fixable) {
@ -253,7 +255,7 @@ class IssueBuffer
);
}
if ($issue_type === 'TaintedInput' || !self::alreadyEmitted($emitted_key)) {
if ($is_tainted || !self::alreadyEmitted($emitted_key)) {
++self::$error_count;
self::$issues_data[$e->getFilePath()][] = $e->toIssueData(Config::REPORT_ERROR);

View File

@ -8,6 +8,9 @@ namespace Psalm\Type;
class TaintKind
{
public const INPUT_TEXT = 'text';
public const INPUT_UNSERIALIZE = 'unserialize';
public const INPUT_INCLUDE = 'include';
public const INPUT_EVAL = 'eval';
public const INPUT_SQL = 'sql';
public const INPUT_HTML = 'html';
public const INPUT_SHELL = 'shell';

View File

@ -12,5 +12,8 @@ class TaintKindGroup
TaintKind::INPUT_SHELL,
TaintKind::INPUT_SQL,
TaintKind::INPUT_TEXT,
TaintKind::INPUT_EVAL,
TaintKind::INPUT_UNSERIALIZE,
TaintKind::INPUT_INCLUDE,
];
}

View File

@ -131,6 +131,7 @@ class DocumentationTest extends TestCase
$code_blocks['UnrecognizedStatement'] = true;
$code_blocks['PluginIssue'] = true;
$code_blocks['TaintedInput'] = true;
$code_blocks['TaintedCustom'] = true;
$documented_issues = array_keys($code_blocks);
sort($documented_issues);
@ -159,6 +160,8 @@ class DocumentationTest extends TestCase
$this->project_analyzer->trackUnusedSuppressions();
}
$is_taint_test = strpos($error_message, 'Tainted') !== false;
$is_array_offset_test = strpos($error_message, 'ArrayOffset') && strpos($error_message, 'PossiblyUndefined') !== false;
$this->project_analyzer->getConfig()->ensure_array_string_offsets_exist = $is_array_offset_test;
@ -178,6 +181,10 @@ class DocumentationTest extends TestCase
$this->addFile($file_path, $code);
if ($is_taint_test) {
$this->project_analyzer->trackTaintedInputs();
}
$this->analyzeFile($file_path, new Context());
if ($check_references) {

View File

@ -155,7 +155,7 @@ class PsalmEndToEndTest extends TestCase
$this->runPsalmInit(1);
$result = $this->runPsalm(['--taint-analysis'], self::$tmpDir, true);
$this->assertStringContainsString('TaintedInput', $result['STDOUT']);
$this->assertStringContainsString('TaintedHtml', $result['STDOUT']);
$this->assertStringContainsString('1 errors', $result['STDOUT']);
$this->assertSame(1, $result['CODE']);
}

View File

@ -506,7 +506,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputFromFunctionReturnType' => [
'<?php
@ -515,7 +515,7 @@ class TaintTest extends TestCase
}
echo getName();',
'error_message' => 'TaintedInput - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:26 - Detected tainted html in path: $_GET -> $_GET[\'name\'] (src/somefile.php:3:32) -> coalesce (src/somefile.php:3:32) -> getName (src/somefile.php:2:42) -> call to echo (src/somefile.php:6:26) -> echo#1',
'error_message' => 'TaintedHtml - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:26 - Detected tainted HTML in path: $_GET -> $_GET[\'name\'] (src/somefile.php:3:32) -> coalesce (src/somefile.php:3:32) -> getName (src/somefile.php:2:42) -> call to echo (src/somefile.php:6:26) -> echo#1',
],
'taintedInputFromExplicitTaintSource' => [
'<?php
@ -527,7 +527,7 @@ class TaintTest extends TestCase
}
echo getName();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputFromExplicitTaintSourceStaticMethod' => [
'<?php
@ -542,7 +542,7 @@ class TaintTest extends TestCase
echo Request::getName();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputFromGetArray' => [
'<?php
@ -553,20 +553,20 @@ class TaintTest extends TestCase
$name = getName($_GET);
echo $name;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputFromReturnToInclude' => [
'<?php
$a = (string) $_GET["file"];
$b = "hello" . $a;
include str_replace("a", "b", $b);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedInclude',
],
'taintedInputFromReturnToEval' => [
'<?php
$a = $_GET["file"];
eval("<?php" . $a);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedEval',
],
'taintedInputFromReturnTypeToEcho' => [
'<?php
@ -584,7 +584,7 @@ class TaintTest extends TestCase
echo $userId;
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputInCreatedArrayIsEchoed' => [
'<?php
@ -593,7 +593,7 @@ class TaintTest extends TestCase
$data = ["name" => $name];
echo "<h1>" . $data["name"] . "</h1>";',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'testTaintedInputInAssignedArrayIsEchoed' => [
'<?php
@ -603,7 +603,7 @@ class TaintTest extends TestCase
$data["name"] = $name;
echo "<h1>" . $data["name"] . "</h1>";',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputDirectly' => [
'<?php
@ -613,7 +613,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputFromReturnTypeWithBranch' => [
'<?php
@ -639,7 +639,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'sinkAnnotation' => [
'<?php
@ -664,7 +664,7 @@ class TaintTest extends TestCase
*/
public function exec(string $sql) : void {}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputFromParam' => [
'<?php
@ -686,7 +686,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput - src' . DIRECTORY_SEPARATOR . 'somefile.php:17:40 - Detected tainted sql in path: $_GET -> $_GET[\'user_id\'] (src/somefile.php:4:45) -> A::getUserId (src/somefile.php:3:55) -> concat (src/somefile.php:8:36) -> A::getAppendedUserId (src/somefile.php:7:63) -> $userId (src/somefile.php:12:29) -> call to A::deleteUser (src/somefile.php:13:53) -> A::deleteUser#2 (src/somefile.php:16:69) -> $userId (src/somefile.php:16:69) -> concat (src/somefile.php:17:40) -> call to PDO::exec (src/somefile.php:17:40) -> PDO::exec#1',
'error_message' => 'TaintedSql - src' . DIRECTORY_SEPARATOR . 'somefile.php:17:40 - Detected tainted SQL in path: $_GET -> $_GET[\'user_id\'] (src/somefile.php:4:45) -> A::getUserId (src/somefile.php:3:55) -> concat (src/somefile.php:8:36) -> A::getAppendedUserId (src/somefile.php:7:63) -> $userId (src/somefile.php:12:29) -> call to A::deleteUser (src/somefile.php:13:53) -> A::deleteUser#2 (src/somefile.php:16:69) -> $userId (src/somefile.php:16:69) -> concat (src/somefile.php:17:40) -> call to PDO::exec (src/somefile.php:17:40) -> PDO::exec#1',
],
'taintedInputToParam' => [
'<?php
@ -706,7 +706,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputToParamAfterAssignment' => [
'<?php
@ -727,7 +727,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId2);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputToParamAlternatePath' => [
'<?php
@ -756,7 +756,7 @@ class TaintTest extends TestCase
}
}
}',
'error_message' => 'TaintedInput - src' . DIRECTORY_SEPARATOR . 'somefile.php:23:44 - Detected tainted sql in path: $_GET -> $_GET[\'user_id\'] (src/somefile.php:7:67) -> call to A::getAppendedUserId (src/somefile.php:7:58) -> A::getAppendedUserId#1 (src/somefile.php:11:66) -> $user_id (src/somefile.php:11:66) -> concat (src/somefile.php:12:36) -> A::getAppendedUserId (src/somefile.php:11:78) -> call to A::deleteUser (src/somefile.php:7:33) -> A::deleteUser#3 (src/somefile.php:19:85) -> $userId2 (src/somefile.php:19:85) -> concat (src/somefile.php:23:44) -> call to PDO::exec (src/somefile.php:23:44) -> PDO::exec#1',
'error_message' => 'TaintedSql - src' . DIRECTORY_SEPARATOR . 'somefile.php:23:44 - Detected tainted SQL in path: $_GET -> $_GET[\'user_id\'] (src/somefile.php:7:67) -> call to A::getAppendedUserId (src/somefile.php:7:58) -> A::getAppendedUserId#1 (src/somefile.php:11:66) -> $user_id (src/somefile.php:11:66) -> concat (src/somefile.php:12:36) -> A::getAppendedUserId (src/somefile.php:11:78) -> call to A::deleteUser (src/somefile.php:7:33) -> A::deleteUser#3 (src/somefile.php:19:85) -> $userId2 (src/somefile.php:19:85) -> concat (src/somefile.php:23:44) -> call to PDO::exec (src/somefile.php:23:44) -> PDO::exec#1',
],
'taintedInParentLoader' => [
'<?php
@ -787,7 +787,7 @@ class TaintTest extends TestCase
}
(new C)->foo((string) $_GET["user_id"]);',
'error_message' => 'TaintedInput - src' . DIRECTORY_SEPARATOR . 'somefile.php:16:44 - Detected tainted sql in path: $_GET -> $_GET[\'user_id\'] (src/somefile.php:28:43) -> call to C::foo (src/somefile.php:28:34) -> C::foo#1 (src/somefile.php:23:52) -> $user_id (src/somefile.php:23:52) -> call to AGrandChild::loadFull (src/somefile.php:24:51) -> AGrandChild::loadFull#1 (src/somefile.php:5:64) -> A::loadFull#1 (src/somefile.php:24:51) -> $sink (src/somefile.php:5:64) -> call to A::loadPartial (src/somefile.php:6:49) -> A::loadPartial#1 (src/somefile.php:3:76) -> AChild::loadPartial#1 (src/somefile.php:6:49) -> $sink (src/somefile.php:15:67) -> concat (src/somefile.php:16:44) -> call to PDO::exec (src/somefile.php:16:44) -> PDO::exec#1',
'error_message' => 'TaintedSql - src' . DIRECTORY_SEPARATOR . 'somefile.php:16:44 - Detected tainted SQL in path: $_GET -> $_GET[\'user_id\'] (src/somefile.php:28:43) -> call to C::foo (src/somefile.php:28:34) -> C::foo#1 (src/somefile.php:23:52) -> $user_id (src/somefile.php:23:52) -> call to AGrandChild::loadFull (src/somefile.php:24:51) -> AGrandChild::loadFull#1 (src/somefile.php:5:64) -> A::loadFull#1 (src/somefile.php:24:51) -> $sink (src/somefile.php:5:64) -> call to A::loadPartial (src/somefile.php:6:49) -> A::loadPartial#1 (src/somefile.php:3:76) -> AChild::loadPartial#1 (src/somefile.php:6:49) -> $sink (src/somefile.php:15:67) -> concat (src/somefile.php:16:44) -> call to PDO::exec (src/somefile.php:16:44) -> PDO::exec#1',
],
'taintedInputFromProperty' => [
'<?php
@ -811,7 +811,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputFromPropertyViaMixin' => [
'<?php
@ -856,7 +856,7 @@ class TaintTest extends TestCase
$pdo->exec("delete from users where user_id = " . $userId);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintedInputViaStaticFunction' => [
'<?php
@ -871,7 +871,7 @@ class TaintTest extends TestCase
echo(Utils::shorten((string) $_GET["user_id"]));
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputViaPureStaticFunction' => [
'<?php
@ -889,7 +889,7 @@ class TaintTest extends TestCase
echo(Utils::shorten((string) $_GET["user_id"]));
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'untaintedInputViaStaticFunctionWithoutSafePath' => [
'<?php
@ -911,7 +911,7 @@ class TaintTest extends TestCase
echo(Utils::shorten("hello"));
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInputFromMagicProperty' => [
'<?php
@ -936,7 +936,7 @@ class TaintTest extends TestCase
$a->userId = (string) $_GET["user_id"];
echo $a->userId;
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintOverMixed' => [
'<?php
@ -948,7 +948,7 @@ class TaintTest extends TestCase
$a = $_GET["bad"];
echo $a;
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintStrConversion' => [
'<?php
@ -956,7 +956,7 @@ class TaintTest extends TestCase
$a = strtoupper(strtolower((string) $_GET["bad"]));
echo $a;
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintIntoExec' => [
'<?php
@ -964,7 +964,7 @@ class TaintTest extends TestCase
$a = (string) $_GET["bad"];
exec($a);
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
'taintIntoExecMultipleConcat' => [
'<?php
@ -972,7 +972,7 @@ class TaintTest extends TestCase
$a = "9" . "a" . "b" . "c" . ((string) $_GET["bad"]) . "d" . "e" . "f";
exec($a);
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
'taintIntoNestedArrayUnnestedSeparately' => [
'<?php
@ -980,7 +980,7 @@ class TaintTest extends TestCase
$a = [[(string) $_GET["bad"]]];
exec($a[0][0]);
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
'taintIntoArrayAndThenOutAgain' => [
'<?php
@ -994,7 +994,7 @@ class TaintTest extends TestCase
exec(self::foo()[0]);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
'taintAppendedToArray' => [
'<?php
@ -1009,7 +1009,7 @@ class TaintTest extends TestCase
exec(self::foo()[0]);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
'taintOnSubstrCall' => [
'<?php
@ -1035,7 +1035,7 @@ class TaintTest extends TestCase
echo U::shorten($o->s);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintOnStrReplaceCallSimple' => [
'<?php
@ -1061,7 +1061,7 @@ class TaintTest extends TestCase
echo U::shorten($o->s);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintOnPregReplaceCall' => [
'<?php
@ -1087,7 +1087,7 @@ class TaintTest extends TestCase
echo U::shorten($o->s);
}
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'IndirectGetAssignment' => [
'<?php
@ -1152,7 +1152,7 @@ class TaintTest extends TestCase
}
echo (new InputFilter("hello"))->getArg("get", "string");',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintPropertyPassingObject' => [
'<?php
@ -1177,7 +1177,7 @@ class TaintTest extends TestCase
$userObj = new User((string) $_GET["user_id"]);
UserUpdater::doDelete(new PDO(), $userObj);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'taintPropertyPassingObjectSettingValueLater' => [
'<?php
@ -1207,7 +1207,7 @@ class TaintTest extends TestCase
$userObj = new User("5");
$userObj->setId((string) $_GET["user_id"]);
UserUpdater::doDelete(new PDO(), $userObj);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedSql',
],
'ImplodeExplode' => [
'<?php
@ -1215,14 +1215,14 @@ class TaintTest extends TestCase
$b = explode(" ", $a);
$c = implode(" ", $b);
echo $c;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'ImplodeIndirect' => [
'<?php
/** @var array $unsafe */
$unsafe = $_GET[\'unsafe\'];
echo implode(" ", $unsafe);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintThroughPregReplaceCallback' => [
'<?php
@ -1237,7 +1237,7 @@ class TaintTest extends TestCase
);
echo $b;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedFunctionWithNoTypes' => [
'<?php
@ -1246,7 +1246,7 @@ class TaintTest extends TestCase
}
echo rawinput();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedStaticCallWithNoTypes' => [
'<?php
@ -1257,7 +1257,7 @@ class TaintTest extends TestCase
}
echo A::rawinput();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintedInstanceCallWithNoTypes' => [
'<?php
@ -1268,25 +1268,25 @@ class TaintTest extends TestCase
}
echo (new A())->rawinput();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintStringObtainedUsingStrval' => [
'<?php
$unsafe = strval($_GET[\'unsafe\']);
echo $unsafe',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintStringObtainedUsingSprintf' => [
'<?php
$unsafe = sprintf("%s", strval($_GET[\'unsafe\']));
echo $unsafe;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'encapsulatedString' => [
'<?php
$unsafe = $_GET[\'unsafe\'];
echo "$unsafe";',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'encapsulatedToStringMagic' => [
'<?php
@ -1297,7 +1297,7 @@ class TaintTest extends TestCase
}
$unsafe = new MyClass();
echo "unsafe: $unsafe";',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'castToStringMagic' => [
'<?php
@ -1308,7 +1308,7 @@ class TaintTest extends TestCase
}
$unsafe = new MyClass();
echo $unsafe;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'castToStringViaArgument' => [
'<?php
@ -1325,7 +1325,7 @@ class TaintTest extends TestCase
$unsafe = new MyClass();
doesEcho($unsafe);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'toStringTaintInSubclass' => [
'<?php // --taint-analysis
@ -1338,7 +1338,7 @@ class TaintTest extends TestCase
class TaintedSubclass extends TaintedBaseClass {}
$x = new TaintedSubclass();
echo "Caught: $x\n";',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'implicitToStringMagic' => [
'<?php
@ -1349,7 +1349,7 @@ class TaintTest extends TestCase
}
$unsafe = new MyClass();
echo $unsafe;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'namespacedFunction' => [
'<?php
@ -1360,12 +1360,12 @@ class TaintTest extends TestCase
}
echo identity($_GET[\'userinput\']);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'print' => [
'<?php
print($_GET["name"]);',
'error_message' => 'TaintedInput - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:27 - Detected tainted html in path: $_GET -> $_GET[\'name\'] (src/somefile.php:2:27) -> call to print (src/somefile.php:2:27) -> print#1',
'error_message' => 'TaintedHtml - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:27 - Detected tainted HTML in path: $_GET -> $_GET[\'name\'] (src/somefile.php:2:27) -> call to print (src/somefile.php:2:27) -> print#1',
],
'unpackArgs' => [
'<?php
@ -1373,7 +1373,7 @@ class TaintTest extends TestCase
echo $args[0];
}
test(...$_GET["other"]);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'foreachArg' => [
'<?php
@ -1382,7 +1382,7 @@ class TaintTest extends TestCase
foreach ($a as $arg) {
echo $arg;
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'magicPropertyType' => [
'<?php
@ -1401,7 +1401,7 @@ class TaintTest extends TestCase
$m = new Magic();
$m->taint = $_GET["input"];
echo $m->taint;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintNestedArrayWithOffsetAccessedInForeach' => [
'<?php
@ -1411,7 +1411,7 @@ class TaintTest extends TestCase
foreach ($a as $m) {
echo $m["a"];
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintNestedArrayWithOffsetAccessedExplicitly' => [
'<?php
@ -1419,39 +1419,39 @@ class TaintTest extends TestCase
$a[] = ["a" => $_GET["name"], "b" => "foo"];
echo $a[0]["a"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintThroughArrayMapExplicitClosure' => [
'<?php
$get = array_map(function($str) { return trim($str);}, $_GET);
echo $get["test"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintThroughArrayMapExplicitTypedClosure' => [
'<?php
$get = array_map(function(string $str) : string { return trim($str);}, $_GET);
echo $get["test"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintThroughArrayMapExplicitArrowFunction' => [
'<?php
$get = array_map(fn($str) => trim($str), $_GET);
echo $get["test"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintThroughArrayMapImplicitFunctionCall' => [
'<?php
$a = ["test" => $_GET["name"]];
$get = array_map("trim", $a);
echo $get["test"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintFilterVar' => [
'<?php
$get = filter_var($_GET, FILTER_CALLBACK, ["options" => "trim"]);
echo $get["test"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintAfterReconciledType' => [
'<?php
@ -1459,7 +1459,7 @@ class TaintTest extends TestCase
if (is_string($input)) {
echo "$input";
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintExit' => [
'<?php
@ -1468,7 +1468,7 @@ class TaintTest extends TestCase
} else {
die($_GET[\'b\']);
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintSpecializedMethod' => [
'<?php
@ -1480,7 +1480,7 @@ class TaintTest extends TestCase
}
$a = new Unsafe();
echo $a->isUnsafe();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintSpecializedInstanceProperty' => [
'<?php
@ -1496,23 +1496,23 @@ class TaintTest extends TestCase
$b = new StringHolder($_GET["x"]);
echo $b->x;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintUnserialize' => [
'<?php
$cb = unserialize($_POST[\'x\']);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedUnserialize',
],
'taintCreateFunction' => [
'<?php
$cb = create_function(\'$a\', $_GET[\'x\']);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedEval',
],
'taintException' => [
'<?php
$e = new Exception();
echo $e;',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintError' => [
'<?php
@ -1522,7 +1522,7 @@ class TaintTest extends TestCase
} catch (TypeError $e) {
echo "Caught: {$e->getTraceAsString()}\n";
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintThrowable' => [
'<?php
@ -1532,7 +1532,7 @@ class TaintTest extends TestCase
} catch (Throwable $e) {
echo "Caught: $e"; // TODO: ("Caught" . $e) does not work.
}',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintReturnedArray' => [
'<?php
@ -1547,7 +1547,7 @@ class TaintTest extends TestCase
$params = processParams($_GET);
echo $params["foo"];',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintFlow' => [
'<?php
@ -1560,7 +1560,7 @@ class TaintTest extends TestCase
$r = $_GET["untrusted"];
echo some_stub($r);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintFlowProxy' => [
'<?php
@ -1578,7 +1578,7 @@ class TaintTest extends TestCase
$r = $_GET["untrusted"];
some_stub($r);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedText',
],
'taintFlowProxyAndReturn' => [
'<?php
@ -1595,7 +1595,7 @@ class TaintTest extends TestCase
$r = $_GET["untrusted"];
echo some_stub($r);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintFlowMethodProxyAndReturn' => [
'<?php
@ -1614,17 +1614,17 @@ class TaintTest extends TestCase
$r = $_GET["untrusted"];
echo some_stub($r);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
'taintPopen' => [
'<?php
$cb = popen($_POST[\'x\'], \'r\');',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
'taintProcOpen' => [
'<?php
$cb = proc_open($_POST[\'x\'], [], []);',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedShell',
],
/*
// TODO: Stubs do not support this type of inference even with $this->message = $message.
@ -1633,7 +1633,7 @@ class TaintTest extends TestCase
'<?php
$x = new Exception($_GET["x"]);
echo $x->getMessage();',
'error_message' => 'TaintedInput',
'error_message' => 'TaintedHtml',
],
*/
];