From 01aa901f935fa6fa12e6682681470fcdf814523e Mon Sep 17 00:00:00 2001 From: ThomasNgr-OpenDSI Date: Thu, 4 Sep 2025 14:59:44 +0200 Subject: [PATCH] 18.0 fix CVE 2024 40137 (#34762) * Sec: Remove all functions that accept callable params - CVE-2024-40137 * FIX #34746 - More complete fix for CVE-2024-40137 --------- Co-authored-by: ldestailleur --- htdocs/core/lib/functions.lib.php | 81 +++++++++++++++++++++++++------ htdocs/install/upgrade.php | 10 ++++ htdocs/install/upgrade2.php | 55 +++++++++++++++++++-- test/phpunit/.gitignore | 1 + test/phpunit/SecurityTest.php | 19 +++++++- 5 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 test/phpunit/.gitignore diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 4be573045c8..3de8219199a 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -7251,8 +7251,23 @@ function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, */ function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = array("allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width")) { + if (is_null($allowed_attributes)) { + $allowed_attributes = array( + "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width", + // HTML5 + "header", "footer", "nav", "section", "menu", "menuitem" + ); + } + // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions. + if (!in_array("content", $allowed_attributes)) { + $allowed_attributes[] = "content"; + } + if (!in_array("http-equiv", $allowed_attributes)) { + $allowed_attributes[] = "http-equiv"; + } + if (class_exists('DOMDocument') && !empty($stringtoclean)) { - $stringtoclean = ''.$stringtoclean.''; + $stringtoclean = ''.$stringtoclean.''; $dom = new DOMDocument(null, 'UTF-8'); $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL); @@ -7293,12 +7308,15 @@ function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes } } + $dom->encoding = 'UTF-8'; + $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later //$return = 'aaaa

bb

ssdd

'."\n

aaa

aa

bb

"; $return = preg_replace('/^'.preg_quote('', '/').'/', '', $return); - $return = preg_replace('/^'.preg_quote('', '/').'/', '', $return); - $return = preg_replace('/'.preg_quote('', '/').'$/', '', $return); + $return = preg_replace('/^'.preg_quote('<', '/').'[^<>]*'.preg_quote('>', '/').'/', '', $return); + $return = preg_replace('/'.preg_quote('', '/').'$/', '', trim($return)); + return trim($return); } else { return $stringtoclean; @@ -7448,13 +7466,26 @@ function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = ' // Add a trick to solve pb with text without parent tag // like '

Foo

bar

' that wrongly ends up, without the trick, with '

Foo

bar

' // like 'abc' that wrongly ends up, without the trick, with '

abc

' - $out = '
'.$out.'
'; - $dom->loadHTML($out, LIBXML_HTML_NODEFDTD|LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL); + + if (dol_textishtml($out)) { + $out = '
'.$out.'
'; + } else { + $out = '
'.dol_nl2br($out).'
'; + } + + $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL); + + $dom->encoding = 'UTF-8'; + $out = trim($dom->saveHTML()); - // Remove the trick added to solve pb with text without parent tag - $out = preg_replace('/^
/', '', $out); - $out = preg_replace('/<\/div>$/', '', $out); + // Remove the trick added to solve pb with text in utf8 and text without parent tag + $out = preg_replace('/^'.preg_quote('', '/').'/', '', $out); + $out = preg_replace('/^'.preg_quote('<', '/').'[^<>]+'.preg_quote('>
', '/').'/', '', $out); + $out = preg_replace('/'.preg_quote('
', '/').'$/', '', trim($out)); + // $out = preg_replace('/^<\?xml encoding="UTF-8">
/', '', $out); + // $out = preg_replace('/<\/div>$/', '', $out); + // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out); } catch (Exception $e) { // If error, invalid HTML string with no way to clean it //print $e->getMessage(); @@ -7492,7 +7523,7 @@ function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = ' } while ($oldstringtoclean != $out); // Check the limit of external links that are automatically executed in a Rich text content. We count: - // ' + // ', we can only accept " $reg = array(); @@ -9228,7 +9259,7 @@ function verifCond($strToEvaluate) /** * Replace eval function to add more security. - * This function is called by verifCond() or trans() and transnoentitiesnoconv(). + * This function is called by verifCond() for example. * * @param string $s String to evaluate * @param int $returnvalue 0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)). @@ -9319,14 +9350,32 @@ function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1' $forbiddenphpstrings = array('$$'); $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction')); - $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"); - $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions - $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64_decode", "rawurldecode", "urldecode", "str_rot13", "hex2bin")); // decode string functions used to obfuscated function name - $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask")); + // We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all) + // We must exclude all functions that allow to execute another function. This includes all function that has a parameter with type "callable" to avoid things + // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0') + $forbiddenphpfunctions = array(); + // @phpcs:ignore + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id")); $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes")); - $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array")); + + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("array_all", "array_any", "array_diff_ukey", "array_filter", "array_find", "array_find_key", "array_map", "array_reduce", "array_intersect_uassoc", "array_intersect_ukey", "array_walk", "array_walk_recursive")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("set_error_handler", "set_exception_handler", "libxml_set_external_entity_loader", "register_shutdown_function", "register_tick_function", "unregister_tick_function")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function")); + + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once")); - $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace", "mb_ereg_replace_callback")); // function with eval capabilities + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask")); + $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include")); $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function diff --git a/htdocs/install/upgrade.php b/htdocs/install/upgrade.php index 0a0c2d63219..9299c2623bb 100644 --- a/htdocs/install/upgrade.php +++ b/htdocs/install/upgrade.php @@ -38,6 +38,11 @@ define('ALLOWED_IF_UPGRADE_UNLOCK_FOUND', 1); include_once 'inc.php'; + +/** + * @var string $conffile + */ + if (!file_exists($conffile)) { print 'Error: Dolibarr config file was not found. This may means that Dolibarr is not installed yet. Please call the page "/install/index.php" instead of "/install/upgrade.php").'; } @@ -46,6 +51,11 @@ require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php'; global $langs; +/** + * @var Conf $conf + * @var Translate $langs + */ + $grant_query = ''; $step = 2; $ok = 0; diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php index 46cff1d2bc6..c3a8b7db52e 100644 --- a/htdocs/install/upgrade2.php +++ b/htdocs/install/upgrade2.php @@ -41,6 +41,11 @@ define('ALLOWED_IF_UPGRADE_UNLOCK_FOUND', 1); include_once 'inc.php'; + +/** + * @var string $conffile + */ + if (!file_exists($conffile)) { print 'Error: Dolibarr config file was not found. This may means that Dolibarr is not installed yet. Please call the page "/install/index.php" instead of "/install/upgrade.php").'; } @@ -56,13 +61,17 @@ require_once $dolibarr_main_document_root.'/core/lib/files.lib.php'; global $langs; +/** + * @var Conf $conf + * @var Translate $langs + */ + $grant_query = ''; $step = 2; $error = 0; -// Cette page peut etre longue. On augmente le delai autorise. -// Ne fonctionne que si on est pas en safe_mode. +// This page can be long. We increase the allowed delay, but this does not work when we are in safe_mode. $err = error_reporting(); error_reporting(0); if (!empty($conf->global->MAIN_OVERRIDE_TIME_LIMIT)) { @@ -224,8 +233,11 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $db->begin(); foreach ($listofentities as $entity) { + dol_syslog("Process upgrade2 for entity ".$entity); + // Set $conf context for entity $conf->setEntityValues($db, $entity); + // Reset forced setup after the setValues if (defined('SYSLOG_FILE')) { $conf->global->SYSLOG_FILE = constant('SYSLOG_FILE'); @@ -248,10 +260,11 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $versiontoarray = explode('.', $versionto); $versionranarray = explode('.', DOL_VERSION); - $afterversionarray = explode('.', '2.0.0'); $beforeversionarray = explode('.', '2.7.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + // Script pour V2 -> V2.1 migrate_paiements($db, $langs, $conf); @@ -315,6 +328,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $beforeversionarray = explode('.', '2.8.9'); //print $versionto.' '.versioncompare($versiontoarray,$afterversionarray).' '.versioncompare($versiontoarray,$beforeversionarray); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_price_facture($db, $langs, $conf); // Code of this function works for 2.8+ because need a field tva_tx migrate_relationship_tables($db, $langs, $conf, 'co_exp', 'fk_commande', 'commande', 'fk_expedition', 'shipping'); @@ -340,6 +355,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '2.8.9'); $beforeversionarray = explode('.', '2.9.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_element_time($db, $langs, $conf); migrate_customerorder_shipping($db, $langs, $conf); @@ -360,6 +377,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '3.0.9'); $beforeversionarray = explode('.', '3.1.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_rename_directories($db, $langs, $conf, '/rss', '/externalrss'); migrate_actioncomm_element($db, $langs, $conf); @@ -369,6 +388,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '3.1.9'); $beforeversionarray = explode('.', '3.2.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_price_contrat($db, $langs, $conf); migrate_mode_reglement($db, $langs, $conf); @@ -380,6 +401,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '3.2.9'); $beforeversionarray = explode('.', '3.3.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_categorie_association($db, $langs, $conf); } @@ -390,6 +413,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '3.6.9'); // target is after this $beforeversionarray = explode('.', '3.7.9'); // target is before this if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_event_assignement($db, $langs, $conf); } @@ -404,6 +429,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '3.9.9'); $beforeversionarray = explode('.', '4.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_rename_directories($db, $langs, $conf, '/fckeditor', '/medias'); } @@ -411,6 +438,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '4.0.9'); $beforeversionarray = explode('.', '5.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + // Migrate to add entity value into llx_societe_remise migrate_remise_entity($db, $langs, $conf); @@ -427,6 +456,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ // Only if the transverse mode is not used if (empty($multicompany_transverse_mode)) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + // Migrate to add entity value into llx_user_rights migrate_user_rights_entity($db, $langs, $conf); @@ -440,6 +471,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '6.0.9'); $beforeversionarray = explode('.', '7.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + // Migrate contact association migrate_event_assignement_contact($db, $langs, $conf); @@ -450,6 +483,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '7.0.9'); $beforeversionarray = explode('.', '8.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_rename_directories($db, $langs, $conf, '/contracts', '/contract'); } @@ -464,6 +499,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '10.0.9'); $beforeversionarray = explode('.', '11.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_users_socialnetworks(); migrate_members_socialnetworks(); migrate_contacts_socialnetworks(); @@ -474,6 +511,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '13.0.9'); $beforeversionarray = explode('.', '14.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_export_import_profiles('export'); migrate_export_import_profiles('import'); } @@ -482,6 +521,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '15.0.9'); $beforeversionarray = explode('.', '16.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_user_photospath(); migrate_user_photospath2(); } @@ -490,6 +531,8 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '16.0.9'); $beforeversionarray = explode('.', '17.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_contractdet_rank(); } @@ -497,13 +540,16 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ $afterversionarray = explode('.', '17.0.9'); $beforeversionarray = explode('.', '18.0.9'); if (versioncompare($versiontoarray, $afterversionarray) >= 0 && versioncompare($versiontoarray, $beforeversionarray) <= 0) { + dol_syslog("Run migrate_... versionto is between ".json_encode($afterversionarray)." and ".json_encode($beforeversionarray)); + migrate_contractdet_rank(); } } - // Code executed only if migration is LAST ONE. Must always be done. if (versioncompare($versiontoarray, $versionranarray) >= 0 || versioncompare($versiontoarray, $versionranarray) <= -3) { + dol_syslog("Run migrate_... if migration is LAST ONE"); + // Reload modules (this must be always done and only into last targeted version, because code to reload module may need table structure of last version) $listofmodule = array( 'MAIN_MODULE_ACCOUNTING'=>'newboxdefonly', @@ -546,6 +592,7 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ if ($result < 0) { $error++; } + // Reload menus (this must be always and only into last targeted version) $result = migrate_reload_menu($db, $langs, $conf); if ($result < 0) { diff --git a/test/phpunit/.gitignore b/test/phpunit/.gitignore new file mode 100644 index 00000000000..647a82afb84 --- /dev/null +++ b/test/phpunit/.gitignore @@ -0,0 +1 @@ +/DemoTest.php diff --git a/test/phpunit/SecurityTest.php b/test/phpunit/SecurityTest.php index 3c9015d7f95..301b9128c23 100644 --- a/test/phpunit/SecurityTest.php +++ b/test/phpunit/SecurityTest.php @@ -730,7 +730,8 @@ class SecurityTest extends PHPUnit\Framework\TestCase { $stringtotest = 'eée'; $decodedstring = dol_string_onlythesehtmlattributes($stringtotest); - $this->assertEquals('eée', $decodedstring, 'Function did not sanitize correclty with test 1'); + //$this->assertEquals('eée', $decodedstring, 'Function did not sanitize correctly with test 1'); + $this->assertEquals('eée', $decodedstring, 'Function did not sanitize correctly with test 1'); $stringtotest = ''; $decodedstring = dol_string_onlythesehtmlattributes($stringtotest); @@ -986,6 +987,22 @@ class SecurityTest extends PHPUnit\Framework\TestCase print "result = ".$result."\n"; $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $result = dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '1'); // result of dol_eval may be an object Closure + print "result4a = ".json_encode($result)."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'The string was not detected as evil, it should due to the [ char and method "2"'); + + $result = dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '2'); // result of dol_eval may be an object Closure + print "result4b = ".json_encode($result)."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'The string was not detected as evil, it should due to the use of array_map'); + + $result = dol_eval('json_encode(array_map(implode("",array("ex","ec"), array("id")))', 1, 1, '1'); // result of dol_eval may be an object Closure + print "result4c = ".json_encode($result)."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'The string was not detected as evil, it should due to the use of array_map'); + + $result = dol_eval('$a=function() { }; $a', 1, 1, '0'); // result of dol_eval may be an object Closure + print "result5 = ".json_encode($result)."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'The string was not detected as evil'); + $result=dol_eval('$a=exec ("ls")', 1, 1); print "result = ".$result."\n"; $this->assertStringContainsString('Bad string syntax to evaluate', $result);