2016-01-07 18:28:27 -05:00
|
|
|
|
<?php
|
2018-11-05 21:57:36 -05:00
|
|
|
|
namespace Psalm\Internal\Analyzer;
|
2016-01-07 18:28:27 -05:00
|
|
|
|
|
2016-02-04 09:22:46 -05:00
|
|
|
|
use PhpParser;
|
2018-11-12 10:46:55 -05:00
|
|
|
|
use Psalm\Internal\Codebase\CallMap;
|
2018-09-25 18:37:24 -04:00
|
|
|
|
use Psalm\Context;
|
2016-08-13 23:26:45 -04:00
|
|
|
|
use Psalm\Type;
|
2019-06-26 22:52:29 +02:00
|
|
|
|
use function strtolower;
|
|
|
|
|
use function array_values;
|
|
|
|
|
use function count;
|
2016-01-07 18:28:27 -05:00
|
|
|
|
|
2018-12-01 18:37:49 -05:00
|
|
|
|
/**
|
|
|
|
|
* @internal
|
|
|
|
|
*/
|
2018-11-05 21:57:36 -05:00
|
|
|
|
class FunctionAnalyzer extends FunctionLikeAnalyzer
|
2016-01-07 18:28:27 -05:00
|
|
|
|
{
|
2018-11-05 21:57:36 -05:00
|
|
|
|
public function __construct(PhpParser\Node\Stmt\Function_ $function, SourceAnalyzer $source)
|
2016-05-16 16:12:02 -04:00
|
|
|
|
{
|
2018-12-17 23:29:27 -05:00
|
|
|
|
$codebase = $source->getCodebase();
|
|
|
|
|
|
|
|
|
|
$file_storage_provider = $codebase->file_storage_provider;
|
|
|
|
|
|
|
|
|
|
$file_storage = $file_storage_provider->get($source->getFilePath());
|
|
|
|
|
|
|
|
|
|
$namespace = $source->getNamespace();
|
|
|
|
|
|
|
|
|
|
$function_id = ($namespace ? strtolower($namespace) . '\\' : '') . strtolower($function->name->name);
|
|
|
|
|
|
|
|
|
|
if (!isset($file_storage->functions[$function_id])) {
|
|
|
|
|
throw new \UnexpectedValueException(
|
|
|
|
|
'Function ' . $function_id . ' should be defined in ' . $source->getFilePath()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$storage = $file_storage->functions[$function_id];
|
|
|
|
|
|
|
|
|
|
parent::__construct($function, $source, $storage);
|
2016-01-07 18:28:27 -05:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 00:39:41 -04:00
|
|
|
|
/**
|
|
|
|
|
* @param string $function_id
|
|
|
|
|
* @param array<PhpParser\Node\Arg> $call_args
|
2017-05-26 20:16:18 -04:00
|
|
|
|
*
|
2016-11-01 00:39:41 -04:00
|
|
|
|
* @return Type\Union
|
|
|
|
|
*/
|
|
|
|
|
public static function getReturnTypeFromCallMapWithArgs(
|
2018-11-11 12:01:14 -05:00
|
|
|
|
StatementsAnalyzer $statements_analyzer,
|
2016-11-01 00:39:41 -04:00
|
|
|
|
$function_id,
|
|
|
|
|
array $call_args,
|
2019-02-15 18:00:40 -05:00
|
|
|
|
Context $context
|
2016-11-01 00:39:41 -04:00
|
|
|
|
) {
|
|
|
|
|
$call_map_key = strtolower($function_id);
|
2016-08-22 15:00:12 -04:00
|
|
|
|
|
2018-02-03 18:52:35 -05:00
|
|
|
|
$call_map = CallMap::getCallMap();
|
2016-08-30 00:05:13 -04:00
|
|
|
|
|
2019-08-12 23:16:05 -04:00
|
|
|
|
$codebase = $statements_analyzer->getCodebase();
|
|
|
|
|
|
2016-10-22 17:35:59 -04:00
|
|
|
|
if (!isset($call_map[$call_map_key])) {
|
|
|
|
|
throw new \InvalidArgumentException('Function ' . $function_id . ' was not found in callmap');
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 20:35:24 -04:00
|
|
|
|
if (!$call_args) {
|
|
|
|
|
switch ($call_map_key) {
|
|
|
|
|
case 'getenv':
|
2019-01-05 00:15:53 -05:00
|
|
|
|
return new Type\Union([new Type\Atomic\TArray([Type::getArrayKey(), Type::getString()])]);
|
2018-07-07 20:35:24 -04:00
|
|
|
|
|
|
|
|
|
case 'gettimeofday':
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TArray([
|
|
|
|
|
Type::getString(),
|
|
|
|
|
Type::getInt()
|
|
|
|
|
])
|
|
|
|
|
]);
|
2018-10-10 16:03:00 -04:00
|
|
|
|
|
|
|
|
|
case 'microtime':
|
|
|
|
|
return Type::getString();
|
2019-01-05 15:42:56 -05:00
|
|
|
|
|
2019-04-03 17:08:37 -04:00
|
|
|
|
case 'hrtime':
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\ObjectLike([
|
|
|
|
|
Type::getInt(),
|
|
|
|
|
Type::getInt()
|
|
|
|
|
])
|
|
|
|
|
]);
|
|
|
|
|
|
2019-01-05 15:42:56 -05:00
|
|
|
|
case 'get_called_class':
|
|
|
|
|
return new Type\Union([new Type\Atomic\TClassString($context->self ?: 'object')]);
|
|
|
|
|
|
|
|
|
|
case 'get_parent_class':
|
|
|
|
|
if ($context->self && $codebase->classExists($context->self)) {
|
|
|
|
|
$classlike_storage = $codebase->classlike_storage_provider->get($context->self);
|
|
|
|
|
|
|
|
|
|
if ($classlike_storage->parent_classes) {
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TClassString(
|
|
|
|
|
array_values($classlike_storage->parent_classes)[0]
|
|
|
|
|
)
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-07 20:35:24 -04:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-05-23 21:17:14 -04:00
|
|
|
|
switch ($call_map_key) {
|
|
|
|
|
case 'pathinfo':
|
|
|
|
|
if (isset($call_args[1])) {
|
|
|
|
|
return Type::getString();
|
|
|
|
|
}
|
2016-11-01 00:39:41 -04:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
return Type::getArray();
|
2016-11-01 00:39:41 -04:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
case 'count':
|
|
|
|
|
if (isset($call_args[0]->value->inferredType)) {
|
|
|
|
|
$atomic_types = $call_args[0]->value->inferredType->getTypes();
|
|
|
|
|
|
2019-08-12 16:17:55 -04:00
|
|
|
|
if (count($atomic_types) === 1) {
|
|
|
|
|
if (isset($atomic_types['array'])) {
|
|
|
|
|
if ($atomic_types['array'] instanceof Type\Atomic\TNonEmptyArray) {
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
$atomic_types['array']->count !== null
|
|
|
|
|
? new Type\Atomic\TLiteralInt($atomic_types['array']->count)
|
|
|
|
|
: new Type\Atomic\TInt
|
|
|
|
|
]);
|
|
|
|
|
} elseif ($atomic_types['array'] instanceof Type\Atomic\ObjectLike
|
|
|
|
|
&& $atomic_types['array']->sealed
|
|
|
|
|
) {
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TLiteralInt(count($atomic_types['array']->properties))
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
} elseif (isset($atomic_types['callable-array'])) {
|
|
|
|
|
return Type::getInt(false, 2);
|
2018-05-23 21:17:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'var_export':
|
|
|
|
|
case 'highlight_string':
|
|
|
|
|
case 'highlight_file':
|
|
|
|
|
if (isset($call_args[1]->value->inferredType)) {
|
|
|
|
|
$subject_type = $call_args[1]->value->inferredType;
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'true') {
|
|
|
|
|
return Type::getString();
|
|
|
|
|
}
|
2018-05-03 13:56:30 -04:00
|
|
|
|
|
|
|
|
|
return new Type\Union([
|
2018-05-23 21:17:14 -04:00
|
|
|
|
new Type\Atomic\TString,
|
|
|
|
|
$call_map_key === 'var_export' ? new Type\Atomic\TNull : new Type\Atomic\TBool
|
2018-05-03 13:56:30 -04:00
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
return $call_map_key === 'var_export' ? Type::getVoid() : Type::getBool();
|
2018-04-02 22:19:58 -04:00
|
|
|
|
|
2018-10-10 10:58:47 -04:00
|
|
|
|
case 'print_r':
|
|
|
|
|
if (isset($call_args[1]->value->inferredType)) {
|
|
|
|
|
$subject_type = $call_args[1]->value->inferredType;
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'true') {
|
|
|
|
|
return Type::getString();
|
|
|
|
|
}
|
2018-10-10 16:03:00 -04:00
|
|
|
|
}
|
2018-10-10 10:58:47 -04:00
|
|
|
|
|
2018-10-10 16:03:00 -04:00
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TString,
|
|
|
|
|
new Type\Atomic\TTrue
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
case 'microtime':
|
|
|
|
|
if (isset($call_args[0]->value->inferredType)) {
|
|
|
|
|
$subject_type = $call_args[0]->value->inferredType;
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'true') {
|
|
|
|
|
return Type::getFloat();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'false') {
|
|
|
|
|
return Type::getString();
|
|
|
|
|
}
|
2018-10-10 10:58:47 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-10 16:03:00 -04:00
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TFloat,
|
|
|
|
|
new Type\Atomic\TString
|
|
|
|
|
]);
|
2018-10-10 10:58:47 -04:00
|
|
|
|
|
2019-04-03 17:08:37 -04:00
|
|
|
|
case 'hrtime':
|
|
|
|
|
if (isset($call_args[0]->value->inferredType)) {
|
|
|
|
|
$subject_type = $call_args[0]->value->inferredType;
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'true') {
|
|
|
|
|
$int = Type::getInt();
|
|
|
|
|
$int->from_calculation = true;
|
|
|
|
|
return $int;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'false') {
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\ObjectLike([
|
|
|
|
|
Type::getInt(),
|
|
|
|
|
Type::getInt()
|
|
|
|
|
])
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\ObjectLike([
|
|
|
|
|
Type::getInt(),
|
|
|
|
|
Type::getInt()
|
|
|
|
|
]),
|
|
|
|
|
new Type\Atomic\TInt()
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$int = Type::getInt();
|
|
|
|
|
$int->from_calculation = true;
|
|
|
|
|
return $int;
|
|
|
|
|
|
2018-07-07 20:35:24 -04:00
|
|
|
|
case 'getenv':
|
|
|
|
|
return new Type\Union([new Type\Atomic\TString, new Type\Atomic\TFalse]);
|
|
|
|
|
|
|
|
|
|
case 'gettimeofday':
|
|
|
|
|
if (isset($call_args[0]->value->inferredType)) {
|
|
|
|
|
$subject_type = $call_args[0]->value->inferredType;
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'true') {
|
|
|
|
|
return Type::getFloat();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((string) $subject_type === 'false') {
|
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TArray([
|
|
|
|
|
Type::getString(),
|
|
|
|
|
Type::getInt()
|
|
|
|
|
])
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
case 'explode':
|
2019-07-17 14:13:28 -04:00
|
|
|
|
if (count($call_args) === 2) {
|
|
|
|
|
if ($call_args[0]->value instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
if ($call_args[0]->value->value === '') {
|
|
|
|
|
return Type::getFalse();
|
|
|
|
|
}
|
2016-08-30 00:05:13 -04:00
|
|
|
|
|
2019-07-17 14:13:28 -04:00
|
|
|
|
return new Type\Union([
|
|
|
|
|
new Type\Atomic\TNonEmptyArray([
|
|
|
|
|
Type::getInt(),
|
|
|
|
|
Type::getString()
|
|
|
|
|
])
|
|
|
|
|
]);
|
|
|
|
|
} elseif (isset($call_args[0]->value->inferredType)
|
|
|
|
|
&& $call_args[0]->value->inferredType->hasString()
|
|
|
|
|
) {
|
|
|
|
|
$falsable_array = new Type\Union([
|
|
|
|
|
new Type\Atomic\TNonEmptyArray([
|
|
|
|
|
Type::getInt(),
|
|
|
|
|
Type::getString()
|
|
|
|
|
]),
|
|
|
|
|
new Type\Atomic\TFalse
|
|
|
|
|
]);
|
2018-12-19 16:15:19 -05:00
|
|
|
|
|
2019-07-17 14:13:28 -04:00
|
|
|
|
if ($codebase->config->ignore_internal_falsable_issues) {
|
|
|
|
|
$falsable_array->ignore_falsable_issues = true;
|
|
|
|
|
}
|
2018-12-19 16:15:19 -05:00
|
|
|
|
|
2019-07-17 14:13:28 -04:00
|
|
|
|
return $falsable_array;
|
|
|
|
|
}
|
2018-05-23 21:17:14 -04:00
|
|
|
|
}
|
2018-04-16 16:03:04 -04:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
break;
|
2016-12-16 18:56:23 -05:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
case 'abs':
|
|
|
|
|
if (isset($call_args[0]->value)) {
|
|
|
|
|
$first_arg = $call_args[0]->value;
|
2018-03-14 18:05:36 -04:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
if (isset($first_arg->inferredType)) {
|
|
|
|
|
$numeric_types = [];
|
|
|
|
|
|
|
|
|
|
foreach ($first_arg->inferredType->getTypes() as $inner_type) {
|
|
|
|
|
if ($inner_type->isNumericType()) {
|
|
|
|
|
$numeric_types[] = $inner_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-14 18:05:36 -04:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
if ($numeric_types) {
|
|
|
|
|
return new Type\Union($numeric_types);
|
|
|
|
|
}
|
2018-03-14 18:05:36 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
break;
|
2018-03-14 18:05:36 -04:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
case 'min':
|
|
|
|
|
case 'max':
|
|
|
|
|
if (isset($call_args[0])) {
|
|
|
|
|
$first_arg = $call_args[0]->value;
|
2016-12-16 18:56:23 -05:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
if (isset($first_arg->inferredType)) {
|
|
|
|
|
if ($first_arg->inferredType->hasArray()) {
|
|
|
|
|
$array_type = $first_arg->inferredType->getTypes()['array'];
|
|
|
|
|
if ($array_type instanceof Type\Atomic\ObjectLike) {
|
|
|
|
|
return $array_type->getGenericValueType();
|
|
|
|
|
}
|
2016-12-16 18:56:23 -05:00
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
if ($array_type instanceof Type\Atomic\TArray) {
|
|
|
|
|
return clone $array_type->type_params[1];
|
|
|
|
|
}
|
|
|
|
|
} elseif ($first_arg->inferredType->hasScalarType() &&
|
2019-06-03 10:33:54 -04:00
|
|
|
|
isset($call_args[1]) &&
|
2018-05-23 21:17:14 -04:00
|
|
|
|
($second_arg = $call_args[1]->value) &&
|
|
|
|
|
isset($second_arg->inferredType) &&
|
|
|
|
|
$second_arg->inferredType->hasScalarType()
|
|
|
|
|
) {
|
|
|
|
|
return Type::combineUnionTypes($first_arg->inferredType, $second_arg->inferredType);
|
2016-12-16 18:56:23 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-23 21:17:14 -04:00
|
|
|
|
|
|
|
|
|
break;
|
2018-10-23 14:38:36 -04:00
|
|
|
|
|
2019-01-28 11:57:49 -05:00
|
|
|
|
case 'round':
|
|
|
|
|
if (isset($call_args[1])) {
|
|
|
|
|
$second_arg = $call_args[1]->value;
|
|
|
|
|
|
|
|
|
|
if (isset($second_arg->inferredType)
|
|
|
|
|
&& $second_arg->inferredType->isSingleIntLiteral()
|
|
|
|
|
) {
|
|
|
|
|
switch ($second_arg->inferredType->getSingleIntLiteral()->value) {
|
|
|
|
|
case 0:
|
|
|
|
|
return Type::getInt(true);
|
|
|
|
|
default:
|
|
|
|
|
return Type::getFloat();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Type\Union([new Type\Atomic\TInt, new Type\Atomic\TFloat]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Type::getInt(true);
|
|
|
|
|
|
2019-01-05 15:49:50 -05:00
|
|
|
|
case 'get_parent_class':
|
2019-01-05 17:10:29 -05:00
|
|
|
|
// this is unreliable, as it's hard to know exactly what's wanted - attempted this in
|
|
|
|
|
// https://github.com/vimeo/psalm/commit/355ed831e1c69c96bbf9bf2654ef64786cbe9fd7
|
|
|
|
|
// but caused problems where it didn’t know exactly what level of child we
|
|
|
|
|
// were receiving.
|
|
|
|
|
//
|
|
|
|
|
// Really this should only work on instances we've created with new Foo(),
|
|
|
|
|
// but that requires more work
|
|
|
|
|
break;
|
2016-12-16 18:56:23 -05:00
|
|
|
|
}
|
2016-11-02 09:24:36 -04:00
|
|
|
|
}
|
2016-10-26 17:51:34 -04:00
|
|
|
|
|
2016-11-02 09:24:36 -04:00
|
|
|
|
if (!$call_map[$call_map_key][0]) {
|
|
|
|
|
return Type::getMixed();
|
|
|
|
|
}
|
2016-10-26 17:51:34 -04:00
|
|
|
|
|
2018-01-25 13:07:36 -05:00
|
|
|
|
$call_map_return_type = Type::parseString($call_map[$call_map_key][0]);
|
|
|
|
|
|
2018-05-23 21:17:14 -04:00
|
|
|
|
switch ($call_map_key) {
|
|
|
|
|
case 'mb_strpos':
|
|
|
|
|
case 'mb_strrpos':
|
|
|
|
|
case 'mb_stripos':
|
|
|
|
|
case 'mb_strripos':
|
|
|
|
|
case 'strpos':
|
|
|
|
|
case 'strrpos':
|
|
|
|
|
case 'stripos':
|
|
|
|
|
case 'strripos':
|
2018-08-29 13:58:07 -04:00
|
|
|
|
case 'strstr':
|
|
|
|
|
case 'stristr':
|
|
|
|
|
case 'strrchr':
|
|
|
|
|
case 'strpbrk':
|
|
|
|
|
case 'array_search':
|
2018-05-23 21:17:14 -04:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2018-12-19 16:15:19 -05:00
|
|
|
|
if ($call_map_return_type->isFalsable()
|
|
|
|
|
&& $codebase->config->ignore_internal_falsable_issues
|
|
|
|
|
) {
|
2018-05-23 21:17:14 -04:00
|
|
|
|
$call_map_return_type->ignore_falsable_issues = true;
|
|
|
|
|
}
|
2018-01-25 13:07:36 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $call_map_return_type;
|
2016-11-02 09:24:36 -04:00
|
|
|
|
}
|
2019-08-12 23:16:05 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array<PhpParser\Node\Arg> $call_args
|
|
|
|
|
*/
|
|
|
|
|
public static function taintBuiltinFunctionReturn(
|
|
|
|
|
StatementsAnalyzer $statements_analyzer,
|
|
|
|
|
string $function_id,
|
|
|
|
|
array $call_args,
|
|
|
|
|
Type\Union $return_type
|
|
|
|
|
) : void {
|
|
|
|
|
$codebase = $statements_analyzer->getCodebase();
|
|
|
|
|
|
|
|
|
|
if (!$codebase->taint) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch ($function_id) {
|
|
|
|
|
case 'htmlspecialchars':
|
|
|
|
|
if (isset($call_args[0]->value->inferredType)
|
|
|
|
|
&& $call_args[0]->value->inferredType->tainted
|
|
|
|
|
) {
|
|
|
|
|
// input is now safe from tainted sql and html
|
|
|
|
|
$return_type->tainted = $call_args[0]->value->inferredType->tainted
|
|
|
|
|
& ~(Type\Union::TAINTED_INPUT_SQL | Type\Union::TAINTED_INPUT_HTML);
|
|
|
|
|
$return_type->sources = $call_args[0]->value->inferredType->sources;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'strtolower':
|
|
|
|
|
case 'strtoupper':
|
2019-08-13 14:53:31 -04:00
|
|
|
|
case 'sprintf':
|
|
|
|
|
case 'preg_quote':
|
2019-08-13 08:07:16 -04:00
|
|
|
|
case 'substr':
|
2019-08-12 23:16:05 -04:00
|
|
|
|
if (isset($call_args[0]->value->inferredType)
|
|
|
|
|
&& $call_args[0]->value->inferredType->tainted
|
|
|
|
|
) {
|
|
|
|
|
$return_type->tainted = $call_args[0]->value->inferredType->tainted;
|
|
|
|
|
$return_type->sources = $call_args[0]->value->inferredType->sources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2019-08-13 08:07:16 -04:00
|
|
|
|
case 'str_replace':
|
|
|
|
|
case 'preg_replace':
|
|
|
|
|
$first_arg_taint = $call_args[0]->value->inferredType->tainted ?? 0;
|
|
|
|
|
$third_arg_taint = $call_args[2]->value->inferredType->tainted ?? 0;
|
|
|
|
|
if ($first_arg_taint || $third_arg_taint) {
|
|
|
|
|
$return_type->tainted = $first_arg_taint | $third_arg_taint;
|
|
|
|
|
$return_type->sources = $call_args[0]->value->inferredType->sources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2019-08-12 23:16:05 -04:00
|
|
|
|
case 'htmlentities':
|
|
|
|
|
case 'striptags':
|
|
|
|
|
if (isset($call_args[0]->value->inferredType)
|
|
|
|
|
&& $call_args[0]->value->inferredType->tainted
|
|
|
|
|
) {
|
|
|
|
|
// input is now safe from tainted html
|
|
|
|
|
$return_type->tainted = $call_args[0]->value->inferredType->tainted
|
|
|
|
|
& ~Type\Union::TAINTED_INPUT_HTML;
|
|
|
|
|
$return_type->sources = $call_args[0]->value->inferredType->sources;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-07 18:28:27 -05:00
|
|
|
|
}
|