Merge branch '14.0' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur 2021-07-06 02:11:28 +02:00
commit c901e518a7
6 changed files with 125 additions and 50 deletions

View File

@ -251,7 +251,7 @@ print '<br>';
if (empty($conf->global->SECURITY_DISABLE_TEST_ON_OBFUSCATED_CONF)) {
print '<strong>$dolibarr_main_db_pass</strong>: ';
if (!empty($dolibarr_main_db_pass) && empty($dolibarr_main_db_encrypted_pass)) {
print img_picto('', 'warning').' '.$langs->trans("DatabasePasswordNotObfuscated").' &nbsp; <span class="opacitymedium">('.$langs->trans("Recommanded").': '.$langs->trans("SetOptionTo", $langs->transnoentitiesnoconv("MainDbPasswordFileConfEncrypted"), yn(1)).')</span>';
print img_picto('', 'warning').' '.$langs->trans("DatabasePasswordNotObfuscated").' &nbsp; <span class="opacitymedium">('.$langs->trans("Recommended").': '.$langs->trans("SetOptionTo", $langs->transnoentitiesnoconv("MainDbPasswordFileConfEncrypted"), yn(1)).')</span>';
//print ' <span class="opacitymedium">('.$langs->trans("RecommendedValueIs", $langs->transnoentitiesnoconv("IPsOfUsers")).')</span>';
} else {
print img_picto('', 'tick').' '.$langs->trans("DatabasePasswordObfuscated");
@ -267,49 +267,14 @@ if (empty($conf->global->SECURITY_DISABLE_TEST_ON_OBFUSCATED_CONF)) {
print '<br>';
print '<br>';
print '<br>';
print load_fiche_titre($langs->trans("Menu").' '.$langs->trans("SecuritySetup").' + '.$langs->trans("OtherSetup"), '', 'folder');
//print '<strong>'.$langs->trans("PasswordEncryption").'</strong>: ';
print '<strong>MAIN_SECURITY_HASH_ALGO</strong> = '.(empty($conf->global->MAIN_SECURITY_HASH_ALGO) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>' : $conf->global->MAIN_SECURITY_HASH_ALGO)." &nbsp; ";
if (empty($conf->global->MAIN_SECURITY_HASH_ALGO)) {
print '<span class="opacitymedium"> &nbsp; &nbsp; If unset: \'md5\'</span>';
}
if ($conf->global->MAIN_SECURITY_HASH_ALGO != 'password_hash') {
print '<br><strong>MAIN_SECURITY_SALT</strong> = '.(empty($conf->global->MAIN_SECURITY_SALT) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>' : $conf->global->MAIN_SECURITY_SALT).'<br>';
} else {
print '<span class="opacitymedium">('.$langs->trans("Recommanded").': password_hash)</span>';
print '<br>';
}
if ($conf->global->MAIN_SECURITY_HASH_ALGO != 'password_hash') {
print '<div class="info">The recommanded value for MAIN_SECURITY_HASH_ALGO is now \'password_hash\' but setting it now will make ALL existing passwords of all users not valid, so update is not possible.<br>';
print 'If you really want to switch, you must:<br>';
print '- Go on home - setup - other and add constant MAIN_SECURITY_HASH_ALGO to value \'password_hash\'<br>';
print '- In same session, WITHOUT LOGGING OUT, go into your admin user record and set a new password<br>';
print '- You can now logout and login with this new password. You must now reset password of all other users.<br>';
print '</div><br>';
}
print load_fiche_titre($langs->trans("Menu").' '.$langs->trans("SecuritySetup"), '', 'folder');
print '<strong>'.$langs->trans("UseCaptchaCode").'</strong>: ';
print empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA) ? '' : img_picto('', 'tick').' ';
print yn(empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA) ? 0 : 1);
print '<br>';
print '<strong>MAIN_SECURITY_ANTI_SSRF_SERVER_IP</strong> = '.(empty($conf->global->MAIN_SECURITY_ANTI_SSRF_SERVER_IP) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span> &nbsp; <span class="opacitymedium">('.$langs->trans("Example").': static-ips-of-server - '.$langs->trans("Note").': common loopback ip like 127.*.*.*, [::1] are already added)</span>' : $conf->global->MAIN_SECURITY_ANTI_SSRF_SERVER_IP)."<br>";
print '<br>';
print '<strong>MAIN_ALLOW_SVG_FILES_AS_IMAGES</strong> = '.(empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES) ? '0 &nbsp; <span class="opacitymedium">('.$langs->trans("Recommanded").': 0)</span>' : $conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)."<br>";
print '<br>';
print '<strong>MAIN_EXEC_USE_POPEN</strong> = ';
if (empty($conf->global->MAIN_EXEC_USE_POPEN)) {
print '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>';
} else {
print $conf->global->MAIN_EXEC_USE_POPEN;
}
if ($execmethod == 1) {
print ' &nbsp; <span class="opacitymedium">("exec" PHP method will be used for shell commands)</span>';
}
if ($execmethod == 2) {
print ' &nbsp; <span class="opacitymedium">("popen" PHP method will be used for shell commands)</span>';
}
print "<br>";
print '<br>';
@ -354,6 +319,62 @@ if (empty($out)) {
}
print '<br>';
print '<br>';
print '<br>';
print '<br>';
print load_fiche_titre($langs->trans("OtherSetup").' ('.$langs->trans("Experimental").')', '', 'folder');
//print '<strong>'.$langs->trans("PasswordEncryption").'</strong>: ';
print '<strong>MAIN_SECURITY_HASH_ALGO</strong> = '.(empty($conf->global->MAIN_SECURITY_HASH_ALGO) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>' : $conf->global->MAIN_SECURITY_HASH_ALGO)." &nbsp; ";
if (empty($conf->global->MAIN_SECURITY_HASH_ALGO)) {
print '<span class="opacitymedium"> &nbsp; &nbsp; If unset: \'md5\'</span>';
}
if ($conf->global->MAIN_SECURITY_HASH_ALGO != 'password_hash') {
print '<br><strong>MAIN_SECURITY_SALT</strong> = '.(empty($conf->global->MAIN_SECURITY_SALT) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>' : $conf->global->MAIN_SECURITY_SALT).'<br>';
} else {
print '<span class="opacitymedium">('.$langs->trans("Recommanded").': password_hash)</span>';
print '<br>';
}
if ($conf->global->MAIN_SECURITY_HASH_ALGO != 'password_hash') {
print '<div class="info">The recommanded value for MAIN_SECURITY_HASH_ALGO is now \'password_hash\' but setting it now will make ALL existing passwords of all users not valid, so update is not possible.<br>';
print 'If you really want to switch, you must:<br>';
print '- Go on home - setup - other and add constant MAIN_SECURITY_HASH_ALGO to value \'password_hash\'<br>';
print '- In same session, WITHOUT LOGGING OUT, go into your admin user record and set a new password<br>';
print '- You can now logout and login with this new password. You must now reset password of all other users.<br>';
print '</div><br>';
}
print '<br>';
print '<strong>MAIN_SECURITY_ANTI_SSRF_SERVER_IP</strong> = '.(empty($conf->global->MAIN_SECURITY_ANTI_SSRF_SERVER_IP) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span> &nbsp; <span class="opacitymedium">('.$langs->trans("Example").': static-ips-of-server - '.$langs->trans("Note").': common loopback ip like 127.*.*.*, [::1] are already added)</span>' : $conf->global->MAIN_SECURITY_ANTI_SSRF_SERVER_IP)."<br>";
print '<br>';
print '<strong>MAIN_ALLOW_SVG_FILES_AS_IMAGES</strong> = '.(empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES) ? '0 &nbsp; <span class="opacitymedium">('.$langs->trans("Recommanded").': 0)</span>' : $conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)."<br>";
print '<br>';
print '<strong>MAIN_RESTRICTHTML_ONLY_VALID_HTML</strong> = '.(empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) ? '<span class="opacitymedium">'.$langs->trans("Undefined").' &nbsp; ('.$langs->trans("Recommanded").': 1)</span>' : '')."<br>";
print '<br>';
print '<strong>MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES</strong> = '.(empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES) ? '<span class="opacitymedium">'.$langs->trans("Undefined").' &nbsp; ('.$langs->trans("Recommanded").': 1)</span>' : '')."<br>";
print '<br>';
print '<strong>MAIN_EXEC_USE_POPEN</strong> = ';
if (empty($conf->global->MAIN_EXEC_USE_POPEN)) {
print '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>';
} else {
print $conf->global->MAIN_EXEC_USE_POPEN;
}
if ($execmethod == 1) {
print ' &nbsp; <span class="opacitymedium">("exec" PHP method will be used for shell commands)</span>';
}
if ($execmethod == 2) {
print ' &nbsp; <span class="opacitymedium">("popen" PHP method will be used for shell commands)</span>';
}
print "<br>";
print '<br>';
// Modules/Applications
@ -405,7 +426,7 @@ if (empty($conf->api->enabled) && empty($conf->webservices->enabled)) {
print '<br>';
}
if (!empty($conf->api->enabled)) {
print '<strong>API_ENDPOINT_RULES</strong> = '.(empty($conf->global->API_ENDPOINT_RULES) ? '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>' : $conf->global->API_ENDPOINT_RULES)."<br>\n";
print '<strong>API_ENDPOINT_RULES</strong> = '.(empty($conf->global->API_ENDPOINT_RULES) ? '<span class="opacitymedium">'.$langs->trans("Undefined").' &nbsp; ('.$langs->trans("Example").': endpoint1:1,endpoint2:1,...)</span>' : $conf->global->API_ENDPOINT_RULES)."<br>\n";
print '<br>';
}
}

View File

@ -577,7 +577,6 @@ if (empty($reshook)) {
if (!$isupload) {
$mesgs = array();
$object->sujet = (string) GETPOST("sujet");
$object->body = (string) GETPOST("bodyemail", 'restricthtml');
$object->bgcolor = (string) GETPOST("bgcolor");
@ -744,7 +743,7 @@ if ($action == 'create') {
print '<div style="padding-top: 10px">';
// wysiwyg editor
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
$doleditor = new DolEditor('bodyemail', GETPOST('bodyemail', 'restricthtml'), '', 600, 'dolibarr_mailings', '', true, true, $conf->global->FCKEDITOR_ENABLE_MAILING, 20, '90%');
$doleditor = new DolEditor('bodyemail', GETPOST('bodyemail', 'restricthtmlallowunvalid'), '', 600, 'dolibarr_mailings', '', true, true, $conf->global->FCKEDITOR_ENABLE_MAILING, 20, '90%');
$doleditor->Create();
print '</div>';

View File

@ -208,6 +208,12 @@ class Mailing extends CommonObject
{
global $conf, $langs;
// Check properties
if ($this->body === 'InvalidHTMLString') {
$this->error = 'InvalidHTMLString';
return -1;
}
$this->db->begin();
$this->title = trim($this->title);
@ -257,6 +263,12 @@ class Mailing extends CommonObject
*/
public function update($user)
{
// Check properties
if ($this->body === 'InvalidHTMLString') {
$this->error = 'InvalidHTMLString';
return -1;
}
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing ";
$sql .= " SET titre = '".$this->db->escape($this->title)."'";
$sql .= ", sujet = '".$this->db->escape($this->sujet)."'";

View File

@ -775,9 +775,22 @@ function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options =
}
break;
case 'restricthtml': // Recommended for most html textarea
case 'restricthtmlallowunvalid':
do {
$oldstringtoclean = $out;
if (!empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
try {
$dom = new DOMDocument;
$dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
} catch (Exception $e) {
//print $e->getMessage();
return 'InvalidHTMLString';
}
$out = $dom->saveHTML();
}
//var_dump($oldstringtoclean);var_dump($out);
// Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are correctly
// encoded using text entities). This is a fix for CKeditor (CKeditor still encode in HTML4 instead of HTML5).
$out = preg_replace('/&#39;/i', '&apos;', $out);
@ -794,7 +807,8 @@ function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options =
// We should also exclude non expected attributes
if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
$out = dol_string_onlythesehtmlattributes($out);
// Warning, the function may add a LF so we are forced to trim to compare with old $out without having always a difference and an infinit loop.
$out = trim(dol_string_onlythesehtmlattributes($out));
}
} while ($oldstringtoclean != $out);
break;
@ -6311,7 +6325,7 @@ function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1,
$stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
$stringtoclean = preg_replace('/javascript\s*:/i', '', $stringtoclean);
$temp = strip_tags($stringtoclean, $allowed_tags_string);
$temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </> changing string obfuscated with </> that pass injection detection into harmfull string
if ($cleanalsosomestyles) { // Clean for remaining html tags
$temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $temp); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless
@ -6361,8 +6375,8 @@ function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes
}
$return = $dom->saveHTML();
//$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
$return = preg_replace('/^<html><body>/', '', $return);
$return = preg_replace('/<\/body><\/html>$/', '', $return);
return $return;

View File

@ -2135,7 +2135,8 @@ IfCLINotRequiredYouShouldDisablePHPFunctions=Except if you need to run system co
PHPFunctionsRequiredForCLI=For shell purpose (like scheduled job backup or running an anitivurs program), you must keep PHP functions
NoWritableFilesFoundIntoRootDir=No writable files or directories of the common programs were found into your root directory (Good)
RecommendedValueIs=Recommended: %s
NotRecommended=Not recommanded
Recommended=Recommended
NotRecommended=Not recommended
ARestrictedPath=A restricted path
CheckForModuleUpdate=Check for external modules updates
CheckForModuleUpdateHelp=This action will connect to editors of external modules to check if a new version is available.

View File

@ -340,6 +340,10 @@ class SecurityTest extends PHPUnit\Framework\TestCase
$langs=$this->savlangs;
$db=$this->savdb;
// Force default mode
$conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML = 0;
$conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES = 0;
$_COOKIE["id"]=111;
$_GET["param1"]="222";
$_POST["param1"]="333";
@ -363,6 +367,7 @@ class SecurityTest extends PHPUnit\Framework\TestCase
$_POST["param13"]='&#110; &#x6E; &gt; &lt; &quot; <a href=\"j&#x61;vascript:alert(document.domain)\">XSS</a>';
$_POST["param13b"]='&#110; &#x6E; &gt; &lt; &quot; <a href=\"j&#x61vascript:alert(document.domain)\">XSS</a>';
$_POST["param14"]="Text with ' encoded with the numeric html entity converted into text entity &#39; (like when submited by CKEditor)";
$_POST["param15"]="<img onerror<=alert(document.domain)> src=>0xbeefed";
//$_POST["param13"]='javascript%26colon%26%23x3B%3Balert(1)';
//$_POST["param14"]='javascripT&javascript#x3a alert(1)';
@ -480,7 +485,7 @@ class SecurityTest extends PHPUnit\Framework\TestCase
// Test with restricthtml we must remove html open/close tag and content but not htmlentities (we can decode html entities for ascii chars like &#110;)
$result=GETPOST("param6", 'restricthtml');
print __METHOD__." result=".$result."\n";
print __METHOD__." result param6=".$result."\n";
$this->assertEquals('&quot;&gt;', $result);
$result=GETPOST("param7", 'restricthtml');
@ -503,6 +508,29 @@ class SecurityTest extends PHPUnit\Framework\TestCase
print __METHOD__." result=".$result."\n";
$this->assertEquals("Text with ' encoded with the numeric html entity converted into text entity &apos; (like when submited by CKEditor)", $result, 'Test 14');
$result=GETPOST("param15", 'restricthtml'); // <img onerror<=alert(document.domain)> src=>0xbeefed
print __METHOD__." result=".$result."\n";
$this->assertEquals("<img onerror=alert(document.domain) src=>0xbeefed", $result, 'Test 15a'); // The GETPOST return a harmull string
// Test with restricthtml + MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES to test disabling of bad atrributes
$conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML = 1;
$result=GETPOST("param15", 'restricthtml');
print __METHOD__." result=".$result."\n";
$this->assertEquals('InvalidHTMLString', $result, 'Test 15b');
unset($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML);
// Test with restricthtml + MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES to test disabling of bad atrributes
$conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES = 1;
$result=GETPOST("param15", 'restricthtml');
print __METHOD__." result=".$result."\n";
$this->assertEquals('<img src="">0xbeefed', $result, 'Test 15b');
unset($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES);
// Special test for GETPOST of backtopage, backtolist or backtourl parameter
$_POST["backtopage"]='//www.google.com';