Skip to content

PHPStan rule to forbid dynamic object instantiation #19877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

cedric-anne
Copy link
Member

@cedric-anne cedric-anne commented Jun 3, 2025

Checklist before requesting a review

  • I have read the CONTRIBUTING document.
  • I have performed a self-review of my code.

Description

Instanciating new objects using a dynamic value can cause unexpected issues.

  1. The value can be an invalid class name, for instance, a value stored in the DB can corresponds to an itemtype from a plugin that is not active anymore. Trying to instanciate this invalid class name would result in a fatal error.
  2. In the past, we already had to security issues related to this, because it may permit to instanciate a class that is not a CommonDBTM class, and execute an unexpected code sequence (see 3b6bc1b).

I propose to add a PHPStan rule to detect and consider as errors these dynamic objects instantiations.

For the moment, the detected errors are not fixed. We could either try to fix them in this PR, or add them to the baseline and fix them later, or even use this rule to detect errors and fix them in separated PRs, and wait to be sure that there is no false positive issues before merging this PR.

Here is the test file I used to validate this rule:

<?php

// safe
$object = new class extends CommonDBTM {};

// safe
$class = null;
if (rand(0, 100) > 50) {
    $class = Computer::class;
} else {
    $class = Monitor::class;
}
$object = new $class();
unset($class);

// safe
$class = $_GET['itemtype'];
if ($class instanceof Computer) {
    $object = new $class();
}
unset($class);

// safe
$class = $_GET['itemtype'];
if (is_a($class, Computer::class, true)) {
    $object = new $class();
}
unset($class);

// safe
$class = $_GET['itemtype'];
if (is_subclass_of($class, Computer::class, true)) {
    $object = new $class();
}
unset($class);

// safe
$class = $_GET['itemtype'];
if (is_a($class, 'Computer', true)) {
    $object = new $class();
}
unset($class);

// safe, unless PHPDoc ignored
/** @var Computer $class */
$class = $_GET['itemtype'];
$object = new $class();
unset($class);

// safe, unless PHPDoc ignored
/** @var class-string<CommonDBTM>|Computer $class */
$class = $_GET['itemtype'];
$object = new $class();
unset($class);

// safe, unless PHPDoc ignored
/** @var Computer&\Symfony\Component\String\Inflector\InflectorInterface $class */
$class = $_GET['itemtype'];
$object = new $class();
unset($class);

// unsafe
$class = null;
if (rand(0, 100) > 50) {
    $class = Computer::class;
} else {
    $class = $_GET["itemtype"];
}
$object = new $class();
unset($class);

// unsafe
/** @var class-string<CommonDBTM>|class-string $class */
$class = $_GET['itemtype'];
$object = new $class();
unset($class);

and the corresponding results:

 ------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
  Line   test.php                                                                                                                                                                
 ------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
  47     Instantiating an object from an unrestricted dynamic string is forbidden. To safely instantiate a `CommonDBTM` object, please use the `getItemForItemtype()` function,  
         otherwise, you have to limit the possible values.                                                                                                                       
         🪪 glpi.forbidDynamicInstantiation                                                                                                                                      
  53     Instantiating an object from an unrestricted dynamic string is forbidden. To safely instantiate a `CommonDBTM` object, please use the `getItemForItemtype()` function,  
         otherwise, you have to limit the possible values.                                                                                                                       
         🪪 glpi.forbidDynamicInstantiation                                                                                                                                      
  59     Instantiating an object from an unrestricted dynamic string is forbidden. To safely instantiate a `CommonDBTM` object, please use the `getItemForItemtype()` function,  
         otherwise, you have to limit the possible values.                                                                                                                       
         🪪 glpi.forbidDynamicInstantiation                                                                                                                                      
  69     Instantiating an object from an unrestricted dynamic string is forbidden. To safely instantiate a `CommonDBTM` object, please use the `getItemForItemtype()` function,  
         otherwise, you have to limit the possible values.                                                                                                                       
         🪪 glpi.forbidDynamicInstantiation                                                                                                                                      
  75     Instantiating an object from an unrestricted dynamic string is forbidden. To safely instantiate a `CommonDBTM` object, please use the `getItemForItemtype()` function,  
         otherwise, you have to limit the possible values.                                                                                                                       
         🪪 glpi.forbidDynamicInstantiation                                                                                                                                      
 ------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 

@cedric-anne cedric-anne requested a review from trasher June 3, 2025 13:29
@cedric-anne cedric-anne self-assigned this Jun 3, 2025
@cedric-anne cedric-anne force-pushed the 11.0/forbid-dynamic-object-instantiation branch 2 times, most recently from 6898722 to 71d5922 Compare June 4, 2025 09:35
@cedric-anne cedric-anne marked this pull request as draft June 4, 2025 09:36
@cedric-anne cedric-anne force-pushed the 11.0/forbid-dynamic-object-instantiation branch 2 times, most recently from 2cb70b2 to efec22b Compare June 11, 2025 15:17
@cedric-anne cedric-anne force-pushed the 11.0/forbid-dynamic-object-instantiation branch from efec22b to 7f8f0a1 Compare June 17, 2025 14:18
@cedric-anne cedric-anne mentioned this pull request Jun 18, 2025
4 tasks
@cedric-anne
Copy link
Member Author

Replaced by #20104.

@cedric-anne cedric-anne deleted the 11.0/forbid-dynamic-object-instantiation branch June 24, 2025 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant