';
- $filter = '((s.client = 1 OR s.client = 3) AND s.status = 1)';
+ $filter = '((s.client:=:1,3) AND (s.status:=:1))';
print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company($soc->id, 'socid', $filter, 'SelectThirdParty', 1, 0, null, 0, 'minwidth300 widthcentpercentminusxx maxwidth500');
// Option to reload page to retrieve customer informations.
if (empty($conf->global->RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED)) {
@@ -4320,7 +4320,7 @@ if ($action == 'create') {
// Clone confirmation
if ($action == 'clone') {
- $filter = '(s.client=1 OR s.client=2 OR s.client=3)';
+ $filter = '(s.client:IN:1,2,3)';
// Create an array for form
$formquestion = array(
array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company($object->socid, 'socid', $filter, 1)),
diff --git a/htdocs/compta/facture/stats/index.php b/htdocs/compta/facture/stats/index.php
index f8d0f1a9f41..b886c5595e1 100644
--- a/htdocs/compta/facture/stats/index.php
+++ b/htdocs/compta/facture/stats/index.php
@@ -279,10 +279,10 @@ print '
'.$langs->tra
print '
'.$langs->trans("ThirdParty").'
';
$filter = '';
if ($mode == 'customer') {
- $filter = 's.client in (1,2,3)';
+ $filter = '(s.client:IN:1,2,3)';
}
if ($mode == 'supplier') {
- $filter = 's.fournisseur = 1';
+ $filter = '(s.fournisseur:=:1)';
}
print img_picto('', 'company', 'class="pictofixedwidth"');
print $form->select_company($socid, 'socid', $filter, 1, 0, 0, array(), 0, 'widthcentpercentminusx maxwidth300');
diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php
index 0430f937d1d..0b45cb88215 100644
--- a/htdocs/contrat/card.php
+++ b/htdocs/contrat/card.php
@@ -1332,7 +1332,7 @@ if ($action == 'create') {
);
$formconfirm = $form->formconfirm($_SERVER['PHP_SELF']."?id=".$object->id, $langs->trans("ActivateAllOnContract"), $langs->trans("ConfirmActivateAllOnContract"), "confirm_activate", $formquestion, 'yes', 1, 280);
} elseif ($action == 'clone') {
- $filter = '(s.client=1 OR s.client=2 OR s.client=3)';
+ $filter = '(s.client:IN:1,2,3)';
// Clone confirmation
$formquestion = array(array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company(GETPOST('socid', 'int'), 'socid', $filter)));
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneContract', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php
index 527dba8eb3d..58e8836852b 100644
--- a/htdocs/core/class/html.form.class.php
+++ b/htdocs/core/class/html.form.class.php
@@ -1368,7 +1368,9 @@ class Form
*
* @param string $selected Preselected type
* @param string $htmlname Name of field in form
- * @param string $filter Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>()] are allowed here (example: 's.rowid <> x', 's.client IN (1,3)'). Do not use a filter coming from input of users.
+ * @param string $filter Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here, example: 's.rowid <> x'
+ * If you need parenthesis, use the Universal Filter Syntax, example: '(s.client:in:(1,3))'
+ * Do not use a filter coming from input of users.
* @param string $showempty Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
* @param int $showtype Show third party type in combolist (customer, prospect or supplier)
* @param int $forcecombo Force to use standard HTML select component without beautification
@@ -1407,8 +1409,23 @@ class Form
}
}
- //$errormsg = '';
- //$filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg);
+ if (preg_match('/[\(\)]/', $filter)) {
+ // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
+ $errormsg = '';
+ $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
+
+ // Redo clean $filter that may contains sql conditions so sql code
+ if (function_exists('testSqlAndScriptInject')) {
+ if (testSqlAndScriptInject($filter, 3) > 0) {
+ $filter = '';
+ return 'SQLInjectionTryDetected';
+ }
+ }
+ } else {
+ // If not, we do nothing. We already no that there is no parenthesis
+ // TODO Disallow this case in a future.
+ dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Syntax.", LOG_WARNING);
+ }
// We search companies
$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
@@ -1428,6 +1445,8 @@ class Form
$sql .= " AND s.rowid = " . ((int) $user->socid);
}
if ($filter) {
+ // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
+ // if not, by testSqlAndScriptInject() only.
$sql .= " AND (" . $filter . ")";
}
if (empty($user->rights->societe->client->voir) && !$user->socid) {
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index fad1f6266c3..7bf81f84e2c 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -11789,9 +11789,10 @@ function jsonOrUnserialize($stringtodecode)
* aaa is a field name (with alias or not) and
* bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'.
* @param string $error Error message
+ * @param int $noand 0=Default, 1=Do not add the AND before the condition string.
* @return string Return forged SQL string
*/
-function forgeSQLFromUniversalSearchCriteria($filter, &$error = '')
+function forgeSQLFromUniversalSearchCriteria($filter, &$error = '', $noand = 0)
{
$regexstring = '\(([a-zA-Z0-9_\.]+:[<>!=insotlke]+:[^\(\)]+)\)'; // Must be (aaa:bbb:...) with aaa is a field name (with alias or not) and bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'
@@ -11808,7 +11809,7 @@ function forgeSQLFromUniversalSearchCriteria($filter, &$error = '')
return 'Filter syntax error'; // Bad syntax of the search string, we force a SQL not found
}
- return " AND (".preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).")";
+ return ($noand ? "" : " AND ")."(".preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).")";
}
/**
diff --git a/htdocs/don/card.php b/htdocs/don/card.php
index f396b5d386f..9ad6f04f545 100644
--- a/htdocs/don/card.php
+++ b/htdocs/don/card.php
@@ -440,7 +440,7 @@ if ($action == 'create') {
} else {
print '
'.$langs->trans('ThirdParty').'
';
print '
';
- $filter = '(s.client = 1 OR s.client = 3) AND status=1';
+ $filter = '((s.client:IN:1,3) AND (status:=:1))';
print $form->select_company($soc->id, 'socid', $filter, 'SelectThirdParty', 0, 0, null, 0, 'minwidth300');
// Option to reload page to retrieve customer informations. Note, this clear other input
if (!empty($conf->global->RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED)) {
diff --git a/htdocs/fichinter/stats/index.php b/htdocs/fichinter/stats/index.php
index cec740fdd9c..39dcdd9589d 100644
--- a/htdocs/fichinter/stats/index.php
+++ b/htdocs/fichinter/stats/index.php
@@ -220,7 +220,7 @@ print '