2
0
forked from Wavyzz/dolibarr

dol_eval_new

This commit is contained in:
Cédric A.
2025-02-17 01:54:44 +01:00
committed by GitHub
parent 3258dd4837
commit a8dd169817

View File

@@ -10626,6 +10626,191 @@ function verifCond($strToEvaluate, $onlysimplestring = '1')
return $rights;
}
/**
* Replace eval function to add more security.
* This function is called by verifCond() or trans() and transnoentitiesnoconv().
*
* @param string $s String to evaluate
* @return void|string Nothing or return result of eval (even if type can be int, it is safer to assume string and find all potential typing issues as abs(dol_eval(...)).
* @see verifCond(), checkPHPCode() to see sanitizing rules that should be very close.
* @phan-suppress PhanPluginUnsafeEval
*/
function dol_eval_new($s)
{
// Only this global variables can be read by eval function and returned to caller
global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
$db, $langs, $user, $website, $websitepage,
$action, $mainmenu, $leftmenu,
$mysoc,
$objectoffield, // To allow the use of $objectoffield in computed fields
// Old variables used
$object,
$obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
// PHP < 7.4.0
defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
defined('T_FN') || define('T_FN', PHP_INT_MAX);
// PHP < 8.0.0
defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
// PHP < 8.1.0
defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
// PHP < 8.4.0
defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
$prohibited_token_ids = [
/*
* Prohibited int tokens
*/
// T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
'T_ABSTRACT', 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', 'T_ATTRIBUTE',
// 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
'T_BAD_CHARACTER',
// 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
// 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
'T_CALLABLE', 'T_CATCH', 'T_CLASS', 'T_CLOSE_TAG', 'T_CONST',
// 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
// 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
'T_DECLARE', 'T_DIR',
// 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
// 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
'T_ECHO', 'T_ENDDECLARE', 'T_ENUM', 'T_EVAL', 'T_EXIT', 'T_EXTENDS',
// 'T_FOR', 'T_FOREACH',
'T_FILE', 'T_FINAL', 'T_FINALLY', 'T_FN', 'T_FUNCTION', 'T_FUNC_C',
'T_GLOBAL', 'T_GOTO',
'T_HALT_COMPILER',
// 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
// 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
'T_IMPLEMENTS', 'T_INCLUDE', 'T_INCLUDE_ONCE', 'T_INSTEADOF', 'T_INTERFACE',
// 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
'T_LINE',
// 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
'T_METHOD_C',
// 'T_NEW',
// 'T_NS_SEPARATOR', 'T_NUM_STRING',
'T_NAMESPACE',
// 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
// 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
'T_OPEN_TAG', 'T_OPEN_TAG_WITH_ECHO',
// 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
'T_PRINT', 'T_PRIVATE', 'T_PROTECTED', 'T_PUBLIC',
// 'T_PROPERTY_C',
'T_READONLY', 'T_REQUIRE', 'T_REQUIRE_ONCE', 'T_RETURN',
// 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
// 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
'T_STATIC',
'T_THROW', 'T_TRAIT', 'T_TRAIT_C', 'T_TRY',
'T_UNSET', 'T_UNSET_CAST', 'T_USE',
// 'T_VARIABLE',
'T_VAR',
// 'T_WHILE', 'T_WHITESPACE',
// 'T_XOR_EQUAL',
// 'T_YIELD', 'T_YIELD_FROM',
/*
* Prohibited string tokens
*/
';',
];
$prohibited_variables = [
'$_COOKIE', '$_ENV', '$_FILES', '$GLOBALS', '$_GET', '$_POST', '$_REQUEST', '$_SERVER', '$_SESSION',
];
$prohibited_functions = [
// 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
// 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
'override_function', 'session_id', 'session_create_id', 'session_regenerate_id',
'call_user_func', 'call_user_func_array', // PREVENT calling forbidden functions
'exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen',
'dol_eval', 'executeCLI', 'verifCond', // Native Dolibarr functions
'create_function', 'assert', 'mb_ereg_replace', 'mb_eregi_replace', // function with eval capabilities
'dol_compress_dir', 'dol_decode', 'dol_delete_file', 'dol_delete_dir', 'dol_delete_dir_recursive', 'dol_copy', 'archiveOrBackupFile', // more dolibarr functions
'fopen', 'file_put_contents', 'fputs', 'fputscsv', 'fwrite', 'fpassthru', 'mkdir', 'rmdir', 'symlink', 'touch', 'unlink', 'umask', // PHP functions related to file operations
'invoke', 'invokeArgs', // Method of ReflectionFunction to execute a function
'filter_input', 'filter_input_array', 'GETPOST', // PREVENT CODE INJECTION
];
$prohibited_token_arrangements = [
// Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
' T_VARIABLE ( ', ' " ( ', ' \' ( ', ' T_CONSTANT_ENCAPSED_STRING ( ', ' ) ( ',
];
$tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
$tokens_arrangement = ' ';
for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
if (is_array($tokens[$i])) {
$token_id = $tokens[$i][0];
$token_value = $tokens[$i][1];
$token_name = token_name($tokens[$i][0]);
} else {
$token_id = $tokens[$i];
$token_value = $tokens[$i];
$token_name = $tokens[$i];
}
// Ignore whitespaces
if (T_WHITESPACE === $token_id) {
continue;
}
// Keep history to check arrangements
$tokens_arrangement .= "{$token_name} ";
// Prohibited Variables
if (T_VARIABLE === $token_id
&& in_array($token_value, $prohibited_variables, true)
) {
return "« {$token_value} » is prohibited in « {$s} »";
}
// Prohibited Functions
if (T_STRING === $token_id
&& in_array($token_value, $prohibited_functions, true)
) {
return "« {$token_value} » is prohibited in « {$s} »";
}
}
// Prohibited Token IDs
for ($i = 0; $i < count($prohibited_token_ids); ++$i) {
if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
}
}
// Prohibited token arrangements
for ($i = 0; $i < count($prohibited_token_arrangements); ++$i) {
if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
}
}
// Return result
try {
return @eval("return {$s};") ?? '';
} catch (Throwable $ex) {
return "Evaluation Error: {$ex->getMessage()} in {$s}";
}
}
/**
* Replace eval function to add more security.
* This function is called by verifCond() or trans() and transnoentitiesnoconv().