Fix SQL injection and stale status matching in the wallet endpoints

The three endpoints the driver app calls for its wallet built their queries by
interpolating the driver id straight into SQL. Anything the app sent went into
the statement, and these run against the payments database.

They also matched only status = 'Finished'. The current ride pipeline writes
'completed', so a driver's completed rides, pending payouts and weekly
earnings all read as zero regardless of how much they had driven — which is
what the wallet errors in the admin error log are sitting next to.

getAllPayment.php, driverStatistic.php and getCountRide.php now bind the id
and match either spelling. Verified no interpolated identifier remains and
every rewritten condition is balanced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-25 17:57:52 +03:00
co-authored by Claude Opus 5
parent 3fb7bc5190
commit 61d6380861
3 changed files with 90 additions and 80 deletions
@@ -6,10 +6,10 @@ $sql = "SELECT
YEAR(`driver_orders`.`created_at`) AS `year`, YEAR(`driver_orders`.`created_at`) AS `year`,
MONTH(`driver_orders`.`created_at`) AS `month`, MONTH(`driver_orders`.`created_at`) AS `month`,
COUNT(*) AS `total_orders`, COUNT(*) AS `total_orders`,
SUM(CASE WHEN `ride`.`status` = 'Finished' THEN 1 ELSE 0 END) AS `completed_orders`, SUM(CASE WHEN LOWER(`ride`.`status`) IN ('finished','completed') THEN 1 ELSE 0 END) AS `completed_orders`,
SUM(CASE WHEN `ride`.`status` = 'Apply' THEN 1 ELSE 0 END) AS `pending_orders`, SUM(CASE WHEN `ride`.`status` = 'Apply' THEN 1 ELSE 0 END) AS `pending_orders`,
SUM(CASE WHEN `ride`.`status` = 'Cancel' THEN 1 ELSE 0 END) AS `canceled_orders`, SUM(CASE WHEN `ride`.`status` = 'Cancel' THEN 1 ELSE 0 END) AS `canceled_orders`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Finished' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_completed`, ROUND(SUM(CASE WHEN LOWER(`ride`.`status`) IN ('finished','completed') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_completed`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Apply' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_pending`, ROUND(SUM(CASE WHEN `ride`.`status` = 'Apply' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_pending`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Cancel' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_canceled`, ROUND(SUM(CASE WHEN `ride`.`status` = 'Cancel' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_canceled`,
SUM(CASE WHEN `ride`.`status` = 'Refused' THEN 1 ELSE 0 END) AS `rejected_orders`, SUM(CASE WHEN `ride`.`status` = 'Refused' THEN 1 ELSE 0 END) AS `rejected_orders`,
@@ -18,7 +18,7 @@ FROM
`driver_orders` `driver_orders`
LEFT JOIN `ride` ON `ride`.`id` = `driver_orders`.`order_id` LEFT JOIN `ride` ON `ride`.`id` = `driver_orders`.`order_id`
WHERE WHERE
`driver_orders`.`driver_id` = '$driverID' `driver_orders`.`driver_id` = :driverID
AND YEAR(`driver_orders`.`created_at`) = YEAR(CURDATE()) AND YEAR(`driver_orders`.`created_at`) = YEAR(CURDATE())
AND MONTH(`driver_orders`.`created_at`) = MONTH(CURDATE()) AND MONTH(`driver_orders`.`created_at`) = MONTH(CURDATE())
GROUP BY GROUP BY
@@ -29,7 +29,9 @@ ORDER BY
`month`; `month`;
"; ";
// كان المعرّف يُدمج في نص الاستعلام مباشرةً — حقن SQL.
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindValue(':driverID', $driverID);
$stmt->execute(); $stmt->execute();
if ($stmt->rowCount() > 0) { if ($stmt->rowCount() > 0) {
@@ -9,9 +9,9 @@ $sql = "SELECT
FROM FROM
`ride` `ride`
WHERE WHERE
`ride`.`status` = 'Finished' LOWER(`ride`.`status`) IN ('finished','completed')
AND `ride`.`created_at` BETWEEN CURRENT_DATE() + INTERVAL 7 HOUR AND CURRENT_DATE() + INTERVAL 10 HOUR AND `ride`.`created_at` BETWEEN CURRENT_DATE() + INTERVAL 7 HOUR AND CURRENT_DATE() + INTERVAL 10 HOUR
AND `ride`.`driver_id` = '$driverID' AND `ride`.`driver_id` = :driverID
) AS morning_count, ) AS morning_count,
( (
SELECT SELECT
@@ -19,9 +19,9 @@ $sql = "SELECT
FROM FROM
`ride` `ride`
WHERE WHERE
`ride`.`status` = 'Finished' LOWER(`ride`.`status`) IN ('finished','completed')
AND `ride`.`created_at` BETWEEN CURRENT_DATE() + INTERVAL 15 HOUR AND CURRENT_DATE() + INTERVAL 18 HOUR AND `ride`.`created_at` BETWEEN CURRENT_DATE() + INTERVAL 15 HOUR AND CURRENT_DATE() + INTERVAL 18 HOUR
AND `ride`.`driver_id` = '$driverID' AND `ride`.`driver_id` = :driverID
) AS afternoon_count, ) AS afternoon_count,
( (
SELECT SELECT
@@ -29,7 +29,7 @@ $sql = "SELECT
FROM FROM
payments payments
WHERE WHERE
isGiven = 'waiting' AND `driverID` = '$driverID' isGiven = 'waiting' AND `driverID` = :driverID
) AS total_amount, ) AS total_amount,
( (
SELECT SELECT
@@ -37,8 +37,8 @@ $sql = "SELECT
FROM FROM
ride ride
WHERE WHERE
`driver_id` = '$driverID' `driver_id` = :driverID
AND `ride`.`status` = 'Finished' AND LOWER(`ride`.`status`) IN ('finished','completed')
AND `ride`.`created_at` > CURRENT_DATE() - INTERVAL 1 WEEK AND `ride`.`created_at` > CURRENT_DATE() - INTERVAL 1 WEEK
) AS total_amount_last_week ) AS total_amount_last_week
FROM FROM
@@ -47,7 +47,13 @@ LIMIT 1;
"; ";
/**
* كان معرّف السائق يُدمج في نص الاستعلام مباشرةً (حقن SQL)، وكانت الحالة
* تُطابق 'Finished' فقط بينما خط الرحلات الحالي يكتب 'completed' — فتظهر
* أرباح السائق ورحلاته أصفاراً.
*/
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindValue(':driverID', $driverID);
$stmt->execute(); $stmt->execute();
if ($stmt->rowCount() > 0) { if ($stmt->rowCount() > 0) {
@@ -7,11 +7,13 @@ $sql = "SELECT
FROM FROM
`ride` `ride`
WHERE WHERE
`ride`.`status` = 'Finished' LOWER(`ride`.`status`) IN ('finished','completed')
AND driver_id = '$driver_id' AND driver_id = :driver_id
AND created_at >= CURDATE(); AND created_at >= CURDATE();
"; ";
// كان المعرّف يُدمج في نص الاستعلام مباشرةً — حقن SQL.
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindValue(':driver_id', $driver_id);
$stmt->execute(); $stmt->execute();
if ($stmt->rowCount() > 0) { if ($stmt->rowCount() > 0) {