[Twig taint analysis] Support AbstractController::render() and Symfony+custom twig extensions (#245)

* Allow analysing twig templates with custom extensions

* Add support for AbstractController::render() and ::renderView()
This commit is contained in:
Wouter de Jong 2022-02-09 08:50:14 +01:00 committed by GitHub
parent f5348e9a7c
commit 44f9a695cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 1 deletions

View File

@ -22,6 +22,7 @@ use Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesMapping;
use Psalm\SymfonyPsalmPlugin\Twig\CachedTemplatesTainter;
use Psalm\SymfonyPsalmPlugin\Twig\TemplateFileAnalyzer;
use SimpleXMLElement;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\HttpKernel\Kernel;
/**
@ -65,6 +66,21 @@ class Plugin implements PluginEntryPointInterface
$containerMeta = new ContainerMeta((array) $config->containerXml);
ContainerHandler::init($containerMeta);
try {
TemplateFileAnalyzer::initExtensions(array_filter(array_map(function (array $m) use ($containerMeta) {
if ('addExtension' !== $m[0]) {
return null;
}
try {
return $containerMeta->get($m[1][0])->getClass();
} catch (ServiceNotFoundException $e) {
return null;
}
}, $containerMeta->get('twig')->getMethodCalls())));
} catch (ServiceNotFoundException $e) {
}
require_once __DIR__.'/Handler/ParameterBagHandler.php';
ParameterBagHandler::init($containerMeta);
$api->registerHooksFromClass(ParameterBagHandler::class);

View File

@ -17,6 +17,7 @@ use Psalm\StatementsSource;
use Psalm\SymfonyPsalmPlugin\Exception\TemplateNameUnresolvedException;
use Psalm\Type\Atomic\TKeyedArray;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Twig\Environment;
/**
@ -34,7 +35,7 @@ class AnalyzedTemplatesTainter implements AfterMethodCallAnalysisInterface
if (
null === $codebase->taint_flow_graph
|| !$expr instanceof MethodCall || $method_id !== Environment::class.'::render' || empty($expr->args)
|| !$expr instanceof MethodCall || !\in_array($method_id, [Environment::class.'::render', AbstractController::class.'::render', AbstractController::class.'::renderView'], true) || empty($expr->args)
|| !isset($expr->args[0]->value)
|| !isset($expr->args[1]->value)
) {

View File

@ -7,6 +7,7 @@ namespace Psalm\SymfonyPsalmPlugin\Twig;
use Psalm\Context as PsalmContext;
use Psalm\Internal\Analyzer\FileAnalyzer;
use Twig\Environment;
use Twig\Extension\ExtensionInterface;
use Twig\Loader\FilesystemLoader;
use Twig\NodeTraverser;
@ -22,6 +23,16 @@ class TemplateFileAnalyzer extends FileAnalyzer
*/
private static $rootPath = 'templates';
/**
* @var list<class-string>
*/
private static $extensionClasses = [];
public static function initExtensions(array $extensionClasses): void
{
self::$extensionClasses = $extensionClasses;
}
public function analyze(
PsalmContext $file_context = null,
PsalmContext $global_context = null
@ -41,6 +52,11 @@ class TemplateFileAnalyzer extends FileAnalyzer
'optimizations' => 0,
'strict_variables' => false,
]);
foreach (self::$extensionClasses as $extensionClass) {
if (class_exists($extensionClass) && is_a($extensionClass, ExtensionInterface::class, true)) {
$twig->addExtension((new \ReflectionClass($extensionClass))->newInstanceWithoutConstructor());
}
}
$local_file_name = str_replace(self::$rootPath.'/', '', $this->file_name);
$twig_source = $loader->getSourceContext($local_file_name);