Vulnerability: An error-based SQL injection vulnerability in product/card.php in Dolibarr version 8.0.2 allows remote authenticated users to execute arbitrary SQL commands via the desiredstock parameter.
Vulnerability ID: CVE-2018-19994
CWE: CWE-89
Severity score: 6.4
Project: https://github.com/Dolibarr/dolibarr
Published: 2019-01-03
File: htdocs/product/class/product.class.php
Commit message: FIX CVE-2018-19994
Commit author: Laurent Destailleur

Vulnerable version (before fix):
 function update($id, $user, $notrigger=false, $action='update') 			$sql.= ", barcode = ". (empty($this->barcode)?"null":"'".$this->db->escape($this->barcode)."'"); 			$sql.= ", fk_barcode_type = ". (empty($this->barcode_type)?"null":$this->db->escape($this->barcode_type));  			$sql.= ", tosell = " . $this->status; 			$sql.= ", tobuy = " . $this->status_buy; 			$sql.= ", tobatch = " . ((empty($this->status_batch) || $this->status_batch < 0) ? '0' : $this->status_batch); 			$sql.= ", finished = " . ((! isset($this->finished) || $this->finished < 0) ? "null" : (int) $this->finished); 			$sql.= ", weight = " . ($this->weight!='' ? "'".$this->db->escape($this->weight)."'" : 'null'); 			$sql.= ", weight_units = " . ($this->weight_units!='' ? "'".$this->db->escape($this->weight_units)."'": 'null'); function update($id, $user, $notrigger=false, $action='update') 			$sql.= ", description = '" . $this->db->escape($this->description) ."'"; 			$sql.= ", url = " . ($this->url?"'".$this->db->escape($this->url)."'":'null'); 			$sql.= ", customcode = '" .        $this->db->escape($this->customcode) ."'"; 	        $sql.= ", fk_country = " . ($this->country_id > 0 ? $this->country_id : 'null'); 	        $sql.= ", note = ".(isset($this->note) ? "'" .$this->db->escape($this->note)."'" : 'null'); 			$sql.= ", duration = '" . $this->db->escape($this->duration_value . $this->duration_unit) ."'"; 			$sql.= ", accountancy_code_buy = '" . $this->db->escape($this->accountancy_code_buy)."'"; 			$sql.= ", accountancy_code_sell= '" . $this->db->escape($this->accountancy_code_sell)."'"; 			$sql.= ", accountancy_code_sell_intra= '" . $this->db->escape($this->accountancy_code_sell_intra)."'"; 			$sql.= ", accountancy_code_sell_export= '" . $this->db->escape($this->accountancy_code_sell_export)."'"; 			$sql.= ", desiredstock = " . ((isset($this->desiredstock) && $this->desiredstock != '') ? $this->desiredstock : "null"); 			$sql.= ", cost_price = " . ($this->cost_price != '' ? $this->db->escape($this->cost_price) : 'null'); 	        $sql.= ", fk_unit= " . (!$this->fk_unit ? 'NULL' : $this->fk_unit); 	        $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1); 			$sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? $this->fk_price_expression : 'NULL'); 			$sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL'); 			// stock field is not here because it is a denormalized value from product_stock. 			$sql.= " WHERE rowid = " . $id;

Fixed version (after fix):
 function update($id, $user, $notrigger=false, $action='update') 			$sql.= ", barcode = ". (empty($this->barcode)?"null":"'".$this->db->escape($this->barcode)."'"); 			$sql.= ", fk_barcode_type = ". (empty($this->barcode_type)?"null":$this->db->escape($this->barcode_type));  			$sql.= ", tosell = " . (int) $this->status; 			$sql.= ", tobuy = " . (int) $this->status_buy; 			$sql.= ", tobatch = " . ((empty($this->status_batch) || $this->status_batch < 0) ? '0' : (int) $this->status_batch); 			$sql.= ", finished = " . ((! isset($this->finished) || $this->finished < 0) ? "null" : (int) $this->finished); 			$sql.= ", weight = " . ($this->weight!='' ? "'".$this->db->escape($this->weight)."'" : 'null'); 			$sql.= ", weight_units = " . ($this->weight_units!='' ? "'".$this->db->escape($this->weight_units)."'": 'null'); function update($id, $user, $notrigger=false, $action='update') 			$sql.= ", description = '" . $this->db->escape($this->description) ."'"; 			$sql.= ", url = " . ($this->url?"'".$this->db->escape($this->url)."'":'null'); 			$sql.= ", customcode = '" .        $this->db->escape($this->customcode) ."'"; 	        $sql.= ", fk_country = " . ($this->country_id > 0 ? (int) $this->country_id : 'null'); 	        $sql.= ", note = ".(isset($this->note) ? "'" .$this->db->escape($this->note)."'" : 'null'); 			$sql.= ", duration = '" . $this->db->escape($this->duration_value . $this->duration_unit) ."'"; 			$sql.= ", accountancy_code_buy = '" . $this->db->escape($this->accountancy_code_buy)."'"; 			$sql.= ", accountancy_code_sell= '" . $this->db->escape($this->accountancy_code_sell)."'"; 			$sql.= ", accountancy_code_sell_intra= '" . $this->db->escape($this->accountancy_code_sell_intra)."'"; 			$sql.= ", accountancy_code_sell_export= '" . $this->db->escape($this->accountancy_code_sell_export)."'"; 			$sql.= ", desiredstock = " . ((isset($this->desiredstock) && $this->desiredstock != '') ? (int) $this->desiredstock : "null"); 			$sql.= ", cost_price = " . ($this->cost_price != '' ? $this->db->escape($this->cost_price) : 'null'); 	        $sql.= ", fk_unit= " . ((! $this->fk_unit) ? 'NULL' : (int) $this->fk_unit); 	        $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1); 			$sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? (int) $this->fk_price_expression : 'NULL'); 			$sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL'); 			// stock field is not here because it is a denormalized value from product_stock. 			$sql.= " WHERE rowid = " . $id;

Patch:
patch: @@ -859,9 +859,9 @@ function update($id, $user, $notrigger=false, $action='update')
 			$sql.= ", barcode = ". (empty($this->barcode)?"null":"'".$this->db->escape($this->barcode)."'");
 			$sql.= ", fk_barcode_type = ". (empty($this->barcode_type)?"null":$this->db->escape($this->barcode_type));
 
-			$sql.= ", tosell = " . $this->status;
-			$sql.= ", tobuy = " . $this->status_buy;
-			$sql.= ", tobatch = " . ((empty($this->status_batch) || $this->status_batch < 0) ? '0' : $this->status_batch);
+			$sql.= ", tosell = " . (int) $this->status;
+			$sql.= ", tobuy = " . (int) $this->status_buy;
+			$sql.= ", tobatch = " . ((empty($this->status_batch) || $this->status_batch < 0) ? '0' : (int) $this->status_batch);
 			$sql.= ", finished = " . ((! isset($this->finished) || $this->finished < 0) ? "null" : (int) $this->finished);
 			$sql.= ", weight = " . ($this->weight!='' ? "'".$this->db->escape($this->weight)."'" : 'null');
 			$sql.= ", weight_units = " . ($this->weight_units!='' ? "'".$this->db->escape($this->weight_units)."'": 'null');
@@ -880,18 +880,18 @@ function update($id, $user, $notrigger=false, $action='update')
 			$sql.= ", description = '" . $this->db->escape($this->description) ."'";
 			$sql.= ", url = " . ($this->url?"'".$this->db->escape($this->url)."'":'null');
 			$sql.= ", customcode = '" .        $this->db->escape($this->customcode) ."'";
-	        $sql.= ", fk_country = " . ($this->country_id > 0 ? $this->country_id : 'null');
+	        $sql.= ", fk_country = " . ($this->country_id > 0 ? (int) $this->country_id : 'null');
 	        $sql.= ", note = ".(isset($this->note) ? "'" .$this->db->escape($this->note)."'" : 'null');
 			$sql.= ", duration = '" . $this->db->escape($this->duration_value . $this->duration_unit) ."'";
 			$sql.= ", accountancy_code_buy = '" . $this->db->escape($this->accountancy_code_buy)."'";
 			$sql.= ", accountancy_code_sell= '" . $this->db->escape($this->accountancy_code_sell)."'";
 			$sql.= ", accountancy_code_sell_intra= '" . $this->db->escape($this->accountancy_code_sell_intra)."'";
 			$sql.= ", accountancy_code_sell_export= '" . $this->db->escape($this->accountancy_code_sell_export)."'";
-			$sql.= ", desiredstock = " . ((isset($this->desiredstock) && $this->desiredstock != '') ? $this->desiredstock : "null");
+			$sql.= ", desiredstock = " . ((isset($this->desiredstock) && $this->desiredstock != '') ? (int) $this->desiredstock : "null");
 			$sql.= ", cost_price = " . ($this->cost_price != '' ? $this->db->escape($this->cost_price) : 'null');
-	        $sql.= ", fk_unit= " . (!$this->fk_unit ? 'NULL' : $this->fk_unit);
+	        $sql.= ", fk_unit= " . ((! $this->fk_unit) ? 'NULL' : (int) $this->fk_unit);
 	        $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1);
-			$sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? $this->fk_price_expression : 'NULL');
+			$sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? (int) $this->fk_price_expression : 'NULL');
 			$sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL');
 			// stock field is not here because it is a denormalized value from product_stock.
 			$sql.= " WHERE rowid = " . $id;
path: htdocs/product/class/product.class.php