diff --git a/htdocs/core/db/DoliDB.class.php b/htdocs/core/db/DoliDB.class.php index 206bac677a4..a6469cf6939 100644 --- a/htdocs/core/db/DoliDB.class.php +++ b/htdocs/core/db/DoliDB.class.php @@ -159,11 +159,12 @@ abstract class DoliDB implements Database * @param string $stringtosanitize String to escape * @param int $allowsimplequote 1=Allow simple quotes in string. When string is used as a list of SQL string ('aa', 'bb', ...) * @param int $allowsequals 1=Allow equals sign + * @param int $allowsspace 1=Allow space char * @return string String escaped */ - public function sanitize($stringtosanitize, $allowsimplequote = 0, $allowsequals = 0) + public function sanitize($stringtosanitize, $allowsimplequote = 0, $allowsequals = 0, $allowsspace = 0) { - return preg_replace('/[^a-z0-9_\-\.,'.($allowsequals ? '=' : '').($allowsimplequote ? "\'" : '').']/i', '', $stringtosanitize); + return preg_replace('/[^a-z0-9_\-\.,'.($allowsequals ? '=' : '').($allowsimplequote ? "\'" : '').($allowsspace ? ' ' : '').']/i', '', $stringtosanitize); } /** diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index e3f3e0c87a2..de24f13997f 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -12594,10 +12594,21 @@ function dolForgeCriteriaCallback($matches) if ($operator == 'IN') { // IN is allowed for list of ID or code only //if (!preg_match('/^\(.*\)$/', $tmpescaped)) { - $tmpescaped = '('.$db->escape($db->sanitize($tmpescaped, 1, 0)).')'; - //} else { - // $tmpescaped = $db->escape($db->sanitize($tmpescaped, 1)); - //} + $tmpescaped2 = '('; + // Explode and sanitize each element in list + $tmpelemarray = explode(',', $tmpescaped); + foreach ($tmpelemarray as $tmpkey => $tmpelem) { + $reg = array(); + if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) { + $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'"; + } else { + $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1)); + } + } + $tmpescaped2 .= join(',', $tmpelemarray); + $tmpescaped2 .= ')'; + + $tmpescaped = $tmpescaped2; } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') { if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) { $tmpescaped = $regbis[1]; diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php index ebd42dcfa7e..54932e19276 100644 --- a/test/phpunit/FunctionsLibTest.php +++ b/test/phpunit/FunctionsLibTest.php @@ -195,14 +195,39 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase $sql = forgeSQLFromUniversalSearchCriteria($filter); $this->assertEquals(' AND ((((statut = 1) or (entity IN (__AAA__))) and (abc < 2) and (abc <> 1.23)))', $sql); + // A real search string $filter="(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.date_creation:<:'2016-01-01 12:30:00') or (t.nature:is:NULL)"; $sql = forgeSQLFromUniversalSearchCriteria($filter); $this->assertEquals(" AND ((t.ref LIKE 'SO-%') or (t.date_creation < '20160101') or (t.date_creation < 0) or (t.nature IS NULL))", $sql); - /*$filter = 't.fk_soc IN (SELECT rowid FROM llx_societe WHERE fournisseur = 1)'; + // A real search string + $filter = "(t.fieldstring:=:'aaa ttt')"; $sql = forgeSQLFromUniversalSearchCriteria($filter); - $this->assertEquals(" xxx", $sql); - */ + $this->assertEquals(" AND ((t.fieldstring = 'aaa ttt'))", $sql); + + + // Check that parenthesis are NOT allowed inside the last operand. Very important. + $filter = "(t.fieldint:=:(1,2))"; + $sql = forgeSQLFromUniversalSearchCriteria($filter); + $this->assertEquals("Filter syntax error - Bad syntax of the search string", $sql); + + // Check that ' is escaped into the last operand + $filter = "(t.fieldstring:=:'aaa'ttt')"; + $sql = forgeSQLFromUniversalSearchCriteria($filter); + $this->assertEquals(" AND ((t.fieldstring = 'aaa\'ttt'))", $sql); + + + $filter = "(t.fk_soc:IN:1,2)"; + $sql = forgeSQLFromUniversalSearchCriteria($filter); + $this->assertEquals(" AND ((t.fk_soc IN (1,2)))", $sql); + + $filter = "(t.fk_soc:IN:'1','2=b')"; + $sql = forgeSQLFromUniversalSearchCriteria($filter); + $this->assertEquals(" AND ((t.fk_soc IN ('1','2=b')))", $sql); + + $filter = "(t.fk_soc:IN:SELECT rowid FROM llx_societe WHERE fournisseur = '1')"; + $sql = forgeSQLFromUniversalSearchCriteria($filter); + $this->assertEquals(" AND ((t.fk_soc IN (SELECT rowid FROM llx_societe WHERE fournisseur = \'1\')))", $sql); return true; } @@ -231,8 +256,8 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase $this->assertEquals($db->connected, 1, 'Savdb is connected'); $this->assertNotNull($newproduct1->db->db, 'newproduct1->db is not null'); - $newproductcloned2 = dol_clone($newproduct1, 2); - var_dump($newproductcloned2); + //$newproductcloned2 = dol_clone($newproduct1, 2); + //var_dump($newproductcloned2); //print __METHOD__." newproductcloned1->db must be null\n"; //$this->assertNull($newproductcloned1->db, 'newproductcloned1->db is null'); }