1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-30 06:59:01 +01:00

Ban plugins from using file_get_contents, PDO, files

This commit is contained in:
Daniil Gentili 2023-07-09 16:55:08 +02:00
parent e4ff361ee7
commit 1f654d8f26
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
3 changed files with 60 additions and 1 deletions

View File

@ -58,7 +58,8 @@
"psr/http-factory": "^1.0", "psr/http-factory": "^1.0",
"psr/log": "^3", "psr/log": "^3",
"webmozart/assert": "^1.11", "webmozart/assert": "^1.11",
"bacon/bacon-qr-code": "^2.0" "bacon/bacon-qr-code": "^2.0",
"nikic/php-parser": "^4.16"
}, },
"require-dev": { "require-dev": {
"phpdocumentor/reflection-docblock": "dev-master", "phpdocumentor/reflection-docblock": "dev-master",

View File

@ -31,6 +31,17 @@ use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Handler; use danog\MadelineProto\EventHandler\Handler;
use danog\MadelineProto\EventHandler\Update; use danog\MadelineProto\EventHandler\Update;
use Generator; use Generator;
use mysqli;
use PDO;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name;
use PhpParser\NodeFinder;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\ParserFactory;
use PHPStan\PhpDocParser\Ast\NodeTraverser;
use ReflectionAttribute; use ReflectionAttribute;
use ReflectionClass; use ReflectionClass;
use ReflectionMethod; use ReflectionMethod;
@ -40,6 +51,7 @@ use Webmozart\Assert\Assert;
use function Amp\File\isDirectory; use function Amp\File\isDirectory;
use function Amp\File\isFile; use function Amp\File\isFile;
use function Amp\File\listFiles; use function Amp\File\listFiles;
use function Amp\File\read;
/** /**
* Event handler. * Event handler.
@ -302,8 +314,47 @@ abstract class EventHandler extends AbstractAPI
Assert::true(\is_subclass_of($plugin, PluginEventHandler::class), "$plugin must extend ".PluginEventHandler::class); Assert::true(\is_subclass_of($plugin, PluginEventHandler::class), "$plugin must extend ".PluginEventHandler::class);
Assert::notEq($plugin, PluginEventHandler::class); Assert::notEq($plugin, PluginEventHandler::class);
Assert::true(\str_contains(\ltrim($plugin, '\\'), '\\'), "$plugin must be in a namespace!"); Assert::true(\str_contains(\ltrim($plugin, '\\'), '\\'), "$plugin must be in a namespace!");
self::validatePlugin($plugin);
} }
return $plugins; return $plugins;
} }
private const BANNED_FUNCTIONS = [
'file_get_contents',
'file_put_contents',
'curl_exec',
'mysqli_connect',
'fopen',
'fsockopen'
];
private const BANNED_CLASSES = [
PDO::class,
mysqli::class,
];
private static function validatePlugin(string $class): void {
$file = read((new ReflectionClass($class))->getFileName());
$file = (new ParserFactory)->create(ParserFactory::ONLY_PHP7)->parse($file);
Assert::notNull($file);
$traverser = new NodeTraverser([new NameResolver()]);
$file = $traverser->traverse($file);
/** @var FuncCall $call */
foreach ((new NodeFinder)->findInstanceOf($file, FuncCall::class) as $call) {
if ($call->name instanceof Name
&& in_array($name = $call->name->toLowerString(), self::BANNED_FUNCTIONS, true)
) {
throw new AssertionError("An error occurred while analyzing plugin $class: plugins may not use the non-async blocking function $name!");
}
}
/** @var New_ $call */
foreach ((new NodeFinder)->findInstanceOf($file, New_::class) as $new) {
if ($new->class instanceof Name
&& in_array($name = $new->class->toLowerString(), self::BANNED_CLASSES, true)
) {
throw new AssertionError("An error occurred while analyzing plugin $class: plugins may not use the non-async blocking class $name!");
}
}
}
} }

View File

@ -20,6 +20,7 @@ declare(strict_types=1);
namespace danog\MadelineProto; namespace danog\MadelineProto;
use Amp\ByteStream\ReadableBuffer;
use ArrayAccess; use ArrayAccess;
use Closure; use Closure;
use Countable; use Countable;
@ -100,6 +101,12 @@ abstract class Tools extends AsyncTools
} }
return null; return null;
} }
/**
* Converts a string into an async amphp stream.
*/
public static function stringToStream(string $str): ReadableBuffer {
return new ReadableBuffer($str);
}
/** /**
* Sanify TL obtained from JSON for TL serialization. * Sanify TL obtained from JSON for TL serialization.
* *