Skip to content

Commit a658c29

Browse files
committed
review with Romain: use distinct whitelists in setup time/runtime + move ModuleFileParser internal logic into ModuleFileReader
1 parent 91279a0 commit a658c29

7 files changed

Lines changed: 174 additions & 176 deletions

File tree

setup/modulediscovery/ModuleFileParser.php

Lines changed: 0 additions & 155 deletions
This file was deleted.

setup/modulediscovery/ModuleFileReader.php

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
<?php
22

3-
require_once __DIR__ . '/ModuleFileParser.php';
3+
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
4+
45
require_once __DIR__ . '/ModuleFileReaderException.php';
6+
require_once APPROOT . 'sources/PhpParser/Evaluation/PhpExpressionEvaluator.php';
57

68
class ModuleFileReader {
79
private static ModuleFileReader $oInstance;
810
private static int $iDummyClassIndex = 0;
911

12+
const FUNC_CALL_WHITELIST=[
13+
"function_exists",
14+
"class_exists",
15+
"method_exists"
16+
];
17+
18+
const STATIC_CALLWHITELIST=[
19+
"utils::GetItopVersionWikiSyntax"
20+
];
21+
1022
protected function __construct() {
1123
}
1224

@@ -32,24 +44,28 @@ public function ReadModuleFileInformation(string $sModuleFilePath) : array
3244
{
3345
try
3446
{
35-
$aNodes = ModuleFileParser::GetInstance()->ParsePhpCode(file_get_contents($sModuleFilePath));
47+
$oParser = (new \PhpParser\ParserFactory())->createForNewestSupportedVersion();
48+
$aNodes = $oParser->parse(file_get_contents($sModuleFilePath));
3649
}
3750
catch (PhpParser\Error $e) {
3851
throw new \ModuleFileReaderException($e->getMessage(), 0, $e, $sModuleFilePath);
3952
}
4053

54+
PhpExpressionEvaluator::GetInstance()->SetFunctionsWhitelist(static::FUNC_CALL_WHITELIST);
55+
PhpExpressionEvaluator::GetInstance()->SetStaticCallsWhitelist(static::STATIC_CALLWHITELIST);
56+
4157
try {
4258
foreach ($aNodes as $sKey => $oNode) {
4359
if ($oNode instanceof \PhpParser\Node\Stmt\Expression) {
44-
$aModuleInfo = ModuleFileParser::GetInstance()->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oNode);
60+
$aModuleInfo = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oNode);
4561
if (! is_null($aModuleInfo)){
4662
$this->CompleteModuleInfoWithFilePath($aModuleInfo);
4763
return $aModuleInfo;
4864
}
4965
}
5066

5167
if ($oNode instanceof PhpParser\Node\Stmt\If_) {
52-
$aModuleInfo = ModuleFileParser::GetInstance()->GetModuleInformationFromIf($sModuleFilePath, $oNode);
68+
$aModuleInfo = $this->GetModuleInformationFromIf($sModuleFilePath, $oNode);
5369
if (! is_null($aModuleInfo)){
5470
$this->CompleteModuleInfoWithFilePath($aModuleInfo);
5571
return $aModuleInfo;
@@ -164,4 +180,123 @@ public function GetAndCheckModuleInstallerClass($aModuleInfo) : ?string
164180

165181
return $sModuleInstallerClass;
166182
}
183+
184+
/**
185+
* @param string $sModuleFilePath
186+
* @param \PhpParser\Node\Expr\Assign $oAssignation
187+
*
188+
* @return array|null
189+
* @throws \ModuleFileReaderException
190+
*/
191+
private function GetModuleInformationFromAddModuleCall(string $sModuleFilePath, \PhpParser\Node\Stmt\Expression $oExpression) : ?array
192+
{
193+
/** @var Assign $oAssignation */
194+
$oAssignation = $oExpression->expr;
195+
if (false === ($oAssignation instanceof PhpParser\Node\Expr\StaticCall)) {
196+
return null;
197+
}
198+
199+
/** @var PhpParser\Node\Expr\StaticCall $oAssignation */
200+
201+
if ("SetupWebPage" !== $oAssignation?->class?->name) {
202+
return null;
203+
}
204+
205+
if ("AddModule" !== $oAssignation?->name?->name) {
206+
return null;
207+
}
208+
209+
$aArgs = $oAssignation?->args;
210+
if (count($aArgs) != 3) {
211+
throw new ModuleFileReaderException("Not enough parameters when calling SetupWebPage::AddModule", 0, null, $sModuleFilePath);
212+
}
213+
214+
$oModuleId = $aArgs[1];
215+
if (false === ($oModuleId instanceof PhpParser\Node\Arg)) {
216+
throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule call issue: " . get_class($oModuleId), 0, null, $sModuleFilePath);
217+
}
218+
219+
/** @var PhpParser\Node\Arg $oModuleId */
220+
if (false === ($oModuleId->value instanceof PhpParser\Node\Scalar\String_)) {
221+
throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule not a string: " . get_class($oModuleId->value), 0, null, $sModuleFilePath);
222+
}
223+
224+
$sModuleId = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oModuleId->value);
225+
226+
$oModuleConfigInfo = $aArgs[2];
227+
if (false === ($oModuleConfigInfo instanceof PhpParser\Node\Arg)) {
228+
throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule call issue: " . get_class($oModuleConfigInfo), 0, null, $sModuleFilePath);
229+
}
230+
231+
/** @var PhpParser\Node\Arg $oModuleConfigInfo */
232+
if (false === ($oModuleConfigInfo->value instanceof PhpParser\Node\Expr\Array_)) {
233+
throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath);
234+
}
235+
236+
$aModuleConfig = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oModuleConfigInfo->value);
237+
238+
if (! is_array($aModuleConfig)){
239+
throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath);
240+
}
241+
242+
return [
243+
$sModuleFilePath,
244+
$sModuleId,
245+
$aModuleConfig,
246+
];
247+
}
248+
249+
/**
250+
* @param string $sModuleFilePath
251+
* @param \PhpParser\Node\Stmt\If_ $oNode
252+
*
253+
* @return array|null
254+
* @throws \ModuleFileReaderException
255+
*/
256+
private function GetModuleInformationFromIf(string $sModuleFilePath, \PhpParser\Node\Stmt\If_ $oNode) : ?array
257+
{
258+
$bCondition = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oNode->cond);
259+
if ($bCondition) {
260+
foreach ($oNode->stmts as $oSubNode) {
261+
if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) {
262+
$aModuleConfig = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oSubNode);
263+
if (!is_null($aModuleConfig)) {
264+
return $aModuleConfig;
265+
}
266+
}
267+
}
268+
269+
return null;
270+
}
271+
272+
if (! is_null($oNode->elseifs)) {
273+
foreach ($oNode->elseifs as $oElseIfSubNode) {
274+
/** @var \PhpParser\Node\Stmt\ElseIf_ $oElseIfSubNode */
275+
$bCondition = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oElseIfSubNode->cond);
276+
if ($bCondition) {
277+
return $this->GetModuleConfigurationFromStatement($sModuleFilePath, $oElseIfSubNode->stmts);
278+
}
279+
}
280+
}
281+
282+
if (! is_null($oNode->else)) {
283+
return $this->GetModuleConfigurationFromStatement($sModuleFilePath, $oNode->else->stmts);
284+
}
285+
286+
return null;
287+
}
288+
289+
private function GetModuleConfigurationFromStatement(string $sModuleFilePath, array $aStmts) : ?array
290+
{
291+
foreach ($aStmts as $oSubNode) {
292+
if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) {
293+
$aModuleConfig = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oSubNode);
294+
if (!is_null($aModuleConfig)) {
295+
return $aModuleConfig;
296+
}
297+
}
298+
}
299+
300+
return null;
301+
}
167302
}

setup/runtimeenv.class.inc.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141

4242
class RunTimeEnvironment
4343
{
44+
const STATIC_CALL_AUTOSELECT_WHITELIST=[
45+
"SetupInfo::ModuleIsSelected"
46+
];
47+
4448
/**
4549
* The name of the environment that the caller wants to build
4650
* @var string sFinalEnv
@@ -461,6 +465,7 @@ protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
461465
{
462466
SetupInfo::SetSelectedModules($aRet);
463467
try{
468+
PhpExpressionEvaluator::GetInstance()->SetStaticCallsWhitelist(RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
464469
$bSelected = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($oModule->GetAutoSelect());
465470
if ($bSelected)
466471
{

setup/unattended-install/InstallationFileService.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ public function ProcessAutoSelectModules() : void {
272272
{
273273
try {
274274
SetupInfo::SetSelectedModules($this->aSelectedModules);
275+
PhpExpressionEvaluator::GetInstance()->SetStaticCallsWhitelist(RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
275276
$bSelected = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aModule['auto_select']);
276277
if ($bSelected)
277278
{

setup/wizardsteps.class.inc.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,6 +1788,7 @@ protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sP
17881788
// Check the module selection
17891789
try {
17901790
SetupInfo::SetSelectedModules($aModules);
1791+
PhpExpressionEvaluator::GetInstance()->SetStaticCallsWhitelist(RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
17911792
$bSelected = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aInfo['auto_select']);
17921793
}
17931794
catch (ModuleFileReaderException $e) {
@@ -1866,6 +1867,7 @@ protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sP
18661867
try
18671868
{
18681869
SetupInfo::SetSelectedModules($aModules);
1870+
PhpExpressionEvaluator::GetInstance()->SetStaticCallsWhitelist(RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
18691871
$bSelected = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aModule['auto_select']);
18701872
if ($bSelected)
18711873
{

0 commit comments

Comments
 (0)