From f7bd03fb751d611aee6e8483e29c1b8eaaa779e2 Mon Sep 17 00:00:00 2001 From: VESSILLER Date: Wed, 12 Feb 2025 16:02:00 +0100 Subject: [PATCH 1/8] FIX #33038 drag and drop files are not prefixed with object reference --- htdocs/core/class/fileupload.class.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/htdocs/core/class/fileupload.class.php b/htdocs/core/class/fileupload.class.php index ec6465e42f2..c2d44f8b496 100644 --- a/htdocs/core/class/fileupload.class.php +++ b/htdocs/core/class/fileupload.class.php @@ -58,6 +58,7 @@ class FileUpload $pathname = str_replace('/class', '', $element_prop['classpath']); $filename = $element_prop['classfile']; $dir_output = $element_prop['dir_output']; + $savingDocMask = ''; //print 'fileupload.class.php: element='.$element.' pathname='.$pathname.' filename='.$filename.' dir_output='.$dir_output."\n"; @@ -73,6 +74,11 @@ class FileUpload $object_ref = dol_sanitizeFileName($object->ref); + // add object reference as file name prefix if const MAIN_DISABLE_SUGGEST_REF_AS_PREFIX is not enabled + if (!getDolGlobalInt('MAIN_DISABLE_SUGGEST_REF_AS_PREFIX')) { + $savingDocMask = $object_ref.'-__file__'; + } + // Special cases to forge $object_ref used to forge $upload_dir if ($element == 'invoice_supplier') { $object_ref = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier').$object_ref; @@ -98,6 +104,7 @@ class FileUpload 'script_url' => $_SERVER['PHP_SELF'], 'upload_dir' => $dir_output.'/'.$object_ref.'/', 'upload_url' => DOL_URL_ROOT.'/document.php?modulepart='.$element.'&attachment=1&file=/'.$object_ref.'/', + 'saving_doc_mask' => $savingDocMask, 'param_name' => 'files', // Set the following option to 'POST', if your server does not support // DELETE requests. This is a parameter sent to the client: @@ -417,6 +424,14 @@ class FileUpload if ($validate) { if (dol_mkdir($this->options['upload_dir']) >= 0) { + // add object reference as file name prefix if const MAIN_DISABLE_SUGGEST_REF_AS_PREFIX is not enabled + $fileNameWithoutExt = preg_replace('/\.[^\.]+$/', '', $file->name); + $savingDocMask = $this->options['saving_doc_mask']; + if ($savingDocMask && strpos($savingDocMask, $fileNameWithoutExt) !== 0) { + $fileNameWithPrefix = preg_replace('/__file__/', $file->name, $savingDocMask); + $file->name = $fileNameWithPrefix; + } + $file_path = $this->options['upload_dir'].$file->name; $append_file = !$this->options['discard_aborted_uploads'] && is_file($file_path) && $file->size > filesize($file_path); From 7997e927a03c51abbc7776c21b7dfc6f0500b5c9 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 13 Feb 2025 14:59:15 +0100 Subject: [PATCH 2/8] fix: On Supplier Order create, if insertExtrafield failed, Dolibarr do not return error and continue --- htdocs/fourn/class/fournisseur.commande.class.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index 4f72be4c417..de27129124a 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -1610,8 +1610,13 @@ class CommandeFournisseur extends CommonOrder // End call triggers } - $this->db->commit(); - return $this->id; + if (!$error) { + $this->db->commit(); + return $this->id; + } else { + $this->db->rollback(); + return -4; + } } else { $this->error = $this->db->lasterror(); $this->db->rollback(); From 5fd5eae3b1c2b481cbb55c26409ab236e18261eb Mon Sep 17 00:00:00 2001 From: Eric Seigne Date: Fri, 14 Feb 2025 09:35:57 +0100 Subject: [PATCH 3/8] set deposit percent to zero on change --- htdocs/core/class/html.form.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 67a8dab7845..880c450f798 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -4332,7 +4332,7 @@ class Form if (depositPercent.length > 0) { $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent); } else { - $("#' . $htmlname . '_deposit_percent_container").hide(); + $("#' . $htmlname . '_deposit_percent_container").hide().find("#' . $htmlname . '_deposit_percent").val(0); } return true; From bd9bf8b5a8c9ebebfb09265beea8832b05588d79 Mon Sep 17 00:00:00 2001 From: "Laurent Destailleur (aka Eldy)" Date: Thu, 13 Feb 2025 20:29:25 +0100 Subject: [PATCH 4/8] FIX #CVE-2024-34051 --- htdocs/main.inc.php | 8 ++++---- test/phpunit/SecurityTest.php | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index e4e85ad053e..264bd10a07c 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -170,25 +170,25 @@ function testSqlAndScriptInject($val, $type) $inj += preg_match('/=data:/si', $val); // List of dom events is on https://www.w3schools.com/jsref/dom_obj_event.asp and https://developer.mozilla.org/en-US/docs/Web/Events $inj += preg_match('/on(mouse|drag|key|load|touch|pointer|select|transition)[a-z]*\s*=/i', $val); // onmousexxx can be set on img or any html tag like - $inj += preg_match('/on(abort|after|animation|auxclick|before|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', $val); + $inj += preg_match('/on(abort|after|animation|auxclick|before|blur|bounce|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', $val); $inj += preg_match('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', $val); $inj += preg_match('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', $val); $inj += preg_match('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', $val); $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', $val); // More not into the previous list - $inj += preg_match('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', $val); + $inj += preg_match('/on(repeat|begin|finish)[a-z]*\s*=/i', $val); // We refuse html into html because some hacks try to obfuscate evil strings by inserting HTML into HTML. Example: error=alert(1) to bypass test on onerror $tmpval = preg_replace('/<[^<]+>/', '', $val); // List of dom events is on https://www.w3schools.com/jsref/dom_obj_event.asp and https://developer.mozilla.org/en-US/docs/Web/Events $inj += preg_match('/on(mouse|drag|key|load|touch|pointer|select|transition)[a-z]*\s*=/i', $tmpval); // onmousexxx can be set on img or any html tag like - $inj += preg_match('/on(abort|after|animation|auxclick|before|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', $tmpval); + $inj += preg_match('/on(abort|after|animation|auxclick|before|blur|bounce|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', $tmpval); $inj += preg_match('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', $tmpval); $inj += preg_match('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', $tmpval); $inj += preg_match('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', $tmpval); $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', $tmpval); // More not into the previous list - $inj += preg_match('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', $tmpval); + $inj += preg_match('/on(repeat|begin|finish)[a-z]*\s*=/i', $tmpval); //$inj += preg_match('/on[A-Z][a-z]+\*=/', $val); // To lock event handlers onAbort(), ... $inj += preg_match('/:|:|:/i', $val); // refused string ':' encoded (no reason to have it encoded) to lock 'javascript:...' diff --git a/test/phpunit/SecurityTest.php b/test/phpunit/SecurityTest.php index fb3132a17ff..410371f46d2 100644 --- a/test/phpunit/SecurityTest.php +++ b/test/phpunit/SecurityTest.php @@ -276,6 +276,14 @@ class SecurityTest extends PHPUnit\Framework\TestCase $result=testSqlAndScriptInject($test, 0); $this->assertGreaterThanOrEqual($expectedresult, $result, 'Error on testSqlAndScriptInject aaa7'); + $test=''; + $result=testSqlAndScriptInject($test, 0); + $this->assertGreaterThanOrEqual($expectedresult, $result, 'Error on testSqlAndScriptInject onbeforeintput'); + $test=''; + $result=testSqlAndScriptInject($test, 0); + $this->assertGreaterThanOrEqual($expectedresult, $result, 'Error on testSqlAndScriptInject onbounce'); + + $test=''; $result=testSqlAndScriptInject($test, 0); $this->assertGreaterThanOrEqual($expectedresult, $result, 'Error on testSqlAndScriptInject bbb'); From 9d9d890c974939ffc175565f0de74534579a4ed5 Mon Sep 17 00:00:00 2001 From: VESSILLER Date: Tue, 18 Feb 2025 12:07:19 +0100 Subject: [PATCH 5/8] FIX accountancy export Quadratus when doc ref is less than 10 char --- htdocs/accountancy/class/accountancyexport.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php index d986e5c0d56..472f5b5ce38 100644 --- a/htdocs/accountancy/class/accountancyexport.class.php +++ b/htdocs/accountancy/class/accountancyexport.class.php @@ -966,7 +966,7 @@ class AccountancyExport // We need to keep the 10 lastest number of invoice doc_ref not the beginning part that is the unusefull almost same part // $tab['num_piece3'] = str_pad(self::trunc($line->piece_num, 10), 10); - $tab['num_piece3'] = substr(self::trunc($line->doc_ref, 20), -10); + $tab['num_piece3'] = str_pad(substr(self::trunc($line->doc_ref, 20), -10), 10); $tab['reserved'] = str_repeat(' ', 10); // position 159 $tab['currency_amount'] = str_repeat(' ', 13); // position 169 // get document file From f89aff96f7aed4967221242795977c4c1643bcb8 Mon Sep 17 00:00:00 2001 From: tnegre Date: Wed, 19 Feb 2025 11:25:26 +0100 Subject: [PATCH 6/8] FIX invoice creation : use dol_include_once instead of require_once to allow external modules --- htdocs/fourn/facture/card.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index d2699019a63..b09244a027d 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -1042,7 +1042,7 @@ if (empty($reshook)) { $object->origin_id = GETPOST('originid', 'int'); - require_once DOL_DOCUMENT_ROOT.'/'.$element.'/class/'.$subelement.'.class.php'; + dol_include_once('/'.$element.'/class/'.$subelement.'.class.php'); $classname = ucfirst($subelement); if ($classname == 'Fournisseur.commande') { $classname = 'CommandeFournisseur'; @@ -1070,7 +1070,7 @@ if (empty($reshook)) { // Add lines if ($id > 0) { - require_once DOL_DOCUMENT_ROOT.'/'.$element.'/class/'.$subelement.'.class.php'; + dol_include_once('/'.$element.'/class/'.$subelement.'.class.php'); $classname = ucfirst($subelement); if ($classname == 'Fournisseur.commande') { $classname = 'CommandeFournisseur'; @@ -2026,7 +2026,7 @@ if ($action == 'create') { $element = 'fourn'; $subelement = 'fournisseur.commande'; } - require_once DOL_DOCUMENT_ROOT.'/'.$element.'/class/'.$subelement.'.class.php'; + dol_include_once('/'.$element.'/class/'.$subelement.'.class.php'); $classname = ucfirst($subelement); if ($classname == 'Fournisseur.commande') { $classname = 'CommandeFournisseur'; From 41e54529b0e6b54bf08878a94928e8d721a23587 Mon Sep 17 00:00:00 2001 From: VESSILLER Date: Wed, 19 Feb 2025 17:26:54 +0100 Subject: [PATCH 7/8] FIX #33145 wrong label status buy on prodcut tooltip --- htdocs/core/class/commonobject.class.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index cf013497aec..807c5f37bfc 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -5213,14 +5213,10 @@ abstract class CommonObject $discount->fk_soc = $this->socid; $this->tpl['label'] .= $discount->getNomUrl(0, 'discount'); } elseif (!empty($line->fk_product)) { - $productstatic = new Product($this->db); - $productstatic->id = $line->fk_product; - $productstatic->ref = $line->ref; - $productstatic->type = $line->fk_product_type; - if (empty($productstatic->ref)) { + if (empty($line->product)) { $line->fetch_product(); - $productstatic = $line->product; } + $productstatic = $line->product; $this->tpl['label'] .= $productstatic->getNomUrl(1); $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label); From 5911d7d0c00aad720b22928789e3d8432c9aa6b3 Mon Sep 17 00:00:00 2001 From: VESSILLER Date: Thu, 20 Feb 2025 08:21:50 +0100 Subject: [PATCH 8/8] Protect product object for line label --- htdocs/core/class/commonobject.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 807c5f37bfc..085b77dd532 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -5218,7 +5218,7 @@ abstract class CommonObject } $productstatic = $line->product; - $this->tpl['label'] .= $productstatic->getNomUrl(1); + $this->tpl['label'] .= (is_object($productstatic) ? $productstatic->getNomUrl(1) : $line->ref); $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label); // Dates if ($line->product_type == 1 && ($date_start || $date_end)) {