From c2c4ed22e33b5ee79cd25892a1d7b3d04d57adc7 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 17 Jun 2026 16:41:02 +0300 Subject: [PATCH] Fix: SSL pinning, root detection, network resilience, and compile errors SSL pinning (all 4 apps): IOClient import, subdomain-safe domain matching Root detection (all 4 apps): modern Magisk/KernelSU/APatch paths Security checks (rider/driver/admin): PlatformException -> false Rider crud: 60s timeout, 3 retries, exponential backoff, JWT pre-validation Driver crud: exponential backoff for TimeoutException RxInt compile (rider/driver): 10.obs -> RxInt(10) Admin device_info: add missing imports, fix RxInt, add package_info_plus --- .../android/app/src/main/cpp/native-lib.cpp | 10 +- .../lib/controller/functions/device_info.dart | 102 +---- .../controller/functions/security_checks.dart | 5 +- .../lib/controller/functions/ssl_pinning.dart | 5 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 + siro_admin/macos/Podfile.lock | 58 +-- siro_admin/pubspec.lock | 16 + siro_admin/pubspec.yaml | 1 + .../android/app/src/main/cpp/native-lib.cpp | 10 +- .../lib/controller/functions/crud.dart | 8 +- .../controller/functions/package_info.dart | 2 +- .../controller/functions/security_checks.dart | 5 +- .../lib/controller/functions/ssl_pinning.dart | 5 +- .../android/app/src/main/cpp/native-lib.cpp | 10 +- siro_rider/lib/controller/functions/crud.dart | 353 ++++++------------ .../controller/functions/package_info.dart | 2 +- .../controller/functions/securty_check.dart | 5 +- .../lib/controller/functions/ssl_pinning.dart | 5 +- .../android/app/src/main/cpp/native-lib.cpp | 10 +- .../lib/controller/functions/ssl_pinning.dart | 5 +- 20 files changed, 216 insertions(+), 403 deletions(-) diff --git a/siro_admin/android/app/src/main/cpp/native-lib.cpp b/siro_admin/android/app/src/main/cpp/native-lib.cpp index 38d25c3..6d80076 100755 --- a/siro_admin/android/app/src/main/cpp/native-lib.cpp +++ b/siro_admin/android/app/src/main/cpp/native-lib.cpp @@ -20,7 +20,7 @@ #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -// Function to check for common root binaries +// Function to check for common root binaries (including Magisk, KernelSU, APatch) bool isRooted() { std::string paths[] = { @@ -29,7 +29,13 @@ bool isRooted() "/system/bin/su", "/system/bin/magisk", "/system/xbin/magisk", - "/sbin/magisk"}; + "/sbin/magisk", + "/data/adb/magisk/magiskinit", + "/data/adb/magisk/magisk", + "/data/adb/magisk.db", + "/data/adb/ksu", + "/data/adb/apatch", + "/data/adb/ap/single"}; for (const auto &path : paths) { diff --git a/siro_admin/lib/controller/functions/device_info.dart b/siro_admin/lib/controller/functions/device_info.dart index b2cd890..ec7f233 100644 --- a/siro_admin/lib/controller/functions/device_info.dart +++ b/siro_admin/lib/controller/functions/device_info.dart @@ -1,10 +1,13 @@ -// import 'dart:io'; - -// import 'package:device_info_plus/device_info_plus.dart'; - import 'dart:async'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:jailbreak_root_detection/jailbreak_root_detection.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import '../../main.dart'; +import '../../print.dart'; class DeviceHelper { static Future getDeviceFingerprint() async { @@ -80,7 +83,7 @@ class SecurityHelper { isTampered = await JailbreakRootDetection.instance.isTampered(bundleId); } } catch (e) { - debugPrint("Error during security checks: $e"); + Log.print("Error during security checks: $e"); } await box.write('isNotTrust', isNotTrust); @@ -98,7 +101,7 @@ class SecurityHelper { } static void _showSecurityWarning() { - RxInt secondsRemaining = 10.obs; + final secondsRemaining = RxInt(10); Get.dialog( CupertinoAlertDialog( @@ -150,90 +153,3 @@ class SecurityHelper { exit(0); } } - -// class DeviceInfoPlus { -// static List> deviceDataList = []; - -// static Future>> getDeviceInfo() async { -// final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); - -// try { -// if (Platform.isAndroid) { -// AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo; -// Map deviceData = { -// 'platform': 'Android', -// 'brand': androidInfo.brand, -// 'model': androidInfo.model, -// 'androidId': androidInfo.device, -// 'versionRelease': androidInfo.version.release, -// 'sdkVersion': androidInfo.version.sdkInt, -// 'manufacturer': androidInfo.manufacturer, -// 'isPhysicalDevice': androidInfo.isPhysicalDevice, -// 'serialNumber': androidInfo.serialNumber, -// 'fingerprint': androidInfo.fingerprint, -// 'type': androidInfo.type, -// 'data': androidInfo.data, -// 'version': androidInfo.version, -// 'tags': androidInfo.tags, -// 'display': androidInfo.display, -// }; -// deviceDataList.add(deviceData); -// } else if (Platform.isIOS) { -// IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo; -// Map deviceData = { -// 'brand': 'Apple', -// 'model': iosInfo.model, -// 'systemName': iosInfo.systemName, -// 'systemVersion': iosInfo.systemVersion, -// 'utsname': iosInfo.utsname, -// 'isPhysicalDevice': iosInfo.isPhysicalDevice, -// 'identifierForVendor': iosInfo.identifierForVendor, -// 'name': iosInfo.name, -// 'localizedModel': iosInfo.localizedModel, -// }; -// deviceDataList.add(deviceData); -// } else if (Platform.isMacOS) { -// MacOsDeviceInfo macInfo = await deviceInfoPlugin.macOsInfo; -// Map deviceData = { -// 'platform': 'macOS', -// 'model': macInfo.model, -// 'version': macInfo.systemGUID, -// }; -// deviceDataList.add(deviceData); -// } else if (Platform.isWindows) { -// WindowsDeviceInfo windowsInfo = await deviceInfoPlugin.windowsInfo; -// Map deviceData = { -// 'platform': 'Windows', -// 'manufacturer': windowsInfo.computerName, -// 'version': windowsInfo.majorVersion, -// 'deviceId': windowsInfo.deviceId, -// 'userName': windowsInfo.userName, -// 'productName': windowsInfo.productName, -// 'installDate': windowsInfo.installDate, -// 'productId': windowsInfo.productId, -// 'numberOfCores': windowsInfo.numberOfCores, -// 'systemMemoryInMegabytes': windowsInfo.systemMemoryInMegabytes, -// }; -// deviceDataList.add(deviceData); -// } else if (Platform.isLinux) { -// LinuxDeviceInfo linuxInfo = await deviceInfoPlugin.linuxInfo; -// Map deviceData = { -// 'platform': 'Linux', -// 'manufacturer': linuxInfo.name, -// 'version': linuxInfo.version, -// }; -// deviceDataList.add(deviceData); -// } -// } catch (e) { -// } - -// return deviceDataList; -// } - -// // Method to print all device data -// static void printDeviceInfo() { -// for (Map deviceData in deviceDataList) { -// 'Version: ${deviceData['version'] ?? deviceData['versionRelease'] ?? 'N/A'}'); -// } -// } -// } diff --git a/siro_admin/lib/controller/functions/security_checks.dart b/siro_admin/lib/controller/functions/security_checks.dart index ffb0f11..11e3ab3 100755 --- a/siro_admin/lib/controller/functions/security_checks.dart +++ b/siro_admin/lib/controller/functions/security_checks.dart @@ -17,7 +17,10 @@ class SecurityChecks { return result; } on PlatformException catch (e) { print("Failed to check security status: ${e.message}"); - return true; // Treat platform errors as a compromised device (for safety) + return false; // Platform not supported → treat as secure + } catch (e) { + print("Security check error: $e"); + return false; } } diff --git a/siro_admin/lib/controller/functions/ssl_pinning.dart b/siro_admin/lib/controller/functions/ssl_pinning.dart index e571778..761b3c9 100644 --- a/siro_admin/lib/controller/functions/ssl_pinning.dart +++ b/siro_admin/lib/controller/functions/ssl_pinning.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as http_io; class SslPinning { SslPinning._(); @@ -29,13 +30,13 @@ class SslPinning { (X509Certificate cert, String host, int port) { final derHash = base64.encode(sha256.convert(cert.der).bytes); for (final entry in _pins.entries) { - if (host.endsWith(entry.key)) { + if (host == entry.key || host.endsWith('.${entry.key}')) { if (entry.value.contains(derHash)) return true; } } if (_globalPins.contains(derHash)) return true; return false; }; - return http.IOClient(httpClient); + return http_io.IOClient(httpClient); } } diff --git a/siro_admin/macos/Flutter/GeneratedPluginRegistrant.swift b/siro_admin/macos/Flutter/GeneratedPluginRegistrant.swift index 192c6c9..00ec6b7 100644 --- a/siro_admin/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/siro_admin/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import firebase_messaging import flutter_image_compress_macos import flutter_secure_storage_macos import local_auth_darwin +import package_info_plus import path_provider_foundation import sqflite_darwin import url_launcher_macos @@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/siro_admin/macos/Podfile.lock b/siro_admin/macos/Podfile.lock index 3aeb2b1..d88ecd3 100644 --- a/siro_admin/macos/Podfile.lock +++ b/siro_admin/macos/Podfile.lock @@ -1,14 +1,4 @@ PODS: - - AppAuth (1.7.6): - - AppAuth/Core (= 1.7.6) - - AppAuth/ExternalUserAgent (= 1.7.6) - - AppAuth/Core (1.7.6) - - AppAuth/ExternalUserAgent (1.7.6): - - AppAuth/Core - - AppCheckCore (11.2.0): - - GoogleUtilities/Environment (~> 8.0) - - GoogleUtilities/UserDefaults (~> 8.0) - - PromisesObjC (~> 2.4) - device_info_plus (0.0.1): - FlutterMacOS - file_selector_macos (0.0.1): @@ -80,20 +70,9 @@ PODS: - flutter_secure_storage_macos (6.1.3): - FlutterMacOS - FlutterMacOS (1.0.0) - - google_sign_in_ios (0.0.1): - - AppAuth (>= 1.7.4) - - Flutter - - FlutterMacOS - - GoogleSignIn (~> 8.0) - - GTMSessionFetcher (>= 3.4.0) - GoogleDataTransport (10.1.0): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - - GoogleSignIn (8.0.0): - - AppAuth (< 2.0, >= 1.7.3) - - AppCheckCore (~> 11.0) - - GTMAppAuth (< 5.0, >= 4.1.1) - - GTMSessionFetcher/Core (~> 3.3) - GoogleUtilities/AppDelegateSwizzler (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -118,14 +97,6 @@ PODS: - GoogleUtilities/UserDefaults (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GTMAppAuth (4.1.1): - - AppAuth/Core (~> 1.7) - - GTMSessionFetcher/Core (< 4.0, >= 3.3) - - GTMSessionFetcher (3.5.0): - - GTMSessionFetcher/Full (= 3.5.0) - - GTMSessionFetcher/Core (3.5.0) - - GTMSessionFetcher/Full (3.5.0): - - GTMSessionFetcher/Core - local_auth_darwin (0.0.1): - Flutter - FlutterMacOS @@ -134,6 +105,9 @@ PODS: - nanopb/encode (= 3.30910.0) - nanopb/decode (3.30910.0) - nanopb/encode (3.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - PromisesObjC (2.4.0) - PromisesSwift (2.4.0): - PromisesObjC (= 2.4.0) @@ -152,15 +126,13 @@ DEPENDENCIES: - flutter_image_compress_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_image_compress_macos/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - - google_sign_in_ios (from `Flutter/ephemeral/.symlinks/plugins/google_sign_in_ios/darwin`) - local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) SPEC REPOS: trunk: - - AppAuth - - AppCheckCore - Firebase - FirebaseCore - FirebaseCoreExtension @@ -171,10 +143,7 @@ SPEC REPOS: - FirebaseRemoteConfigInterop - FirebaseSessions - GoogleDataTransport - - GoogleSignIn - GoogleUtilities - - GTMAppAuth - - GTMSessionFetcher - nanopb - PromisesObjC - PromisesSwift @@ -196,20 +165,18 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: :path: Flutter/ephemeral - google_sign_in_ios: - :path: Flutter/ephemeral/.symlinks/plugins/google_sign_in_ios/darwin local_auth_darwin: :path: Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin sqflite_darwin: :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: - AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 - AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 - file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7 + file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e firebase_core: 7667f880631ae8ad10e3d6567ab7582fe0682326 firebase_crashlytics: af8dce4a4f3b2b1556bf51043623060a5fc7eca7 @@ -224,19 +191,16 @@ SPEC CHECKSUMS: FirebaseSessions: b9a92c1c51bbb81e78fc3142cda6d925d700f8e7 flutter_image_compress_macos: e68daf54bb4bf2144c580fd4d151c949cbf492f0 flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54 - FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 - google_sign_in_ios: b48bb9af78576358a168361173155596c845f0b9 + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 - GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb + local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd + url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 diff --git a/siro_admin/pubspec.lock b/siro_admin/pubspec.lock index 7f9c8cf..4520f01 100644 --- a/siro_admin/pubspec.lock +++ b/siro_admin/pubspec.lock @@ -912,6 +912,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: "direct main" description: diff --git a/siro_admin/pubspec.yaml b/siro_admin/pubspec.yaml index ad43727..2c07c2c 100644 --- a/siro_admin/pubspec.yaml +++ b/siro_admin/pubspec.yaml @@ -67,6 +67,7 @@ dependencies: device_info_plus: ^11.5.0 flutter_staggered_animations: ^1.1.1 jailbreak_root_detection: ^1.1.5 + package_info_plus: ^4.0.2 dev_dependencies: flutter_test: diff --git a/siro_driver/android/app/src/main/cpp/native-lib.cpp b/siro_driver/android/app/src/main/cpp/native-lib.cpp index fb6b777..997fc64 100755 --- a/siro_driver/android/app/src/main/cpp/native-lib.cpp +++ b/siro_driver/android/app/src/main/cpp/native-lib.cpp @@ -20,7 +20,7 @@ #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -// Function to check for common root binaries +// Function to check for common root binaries (including Magisk, KernelSU, APatch) bool isRooted() { std::string paths[] = { @@ -29,7 +29,13 @@ bool isRooted() "/system/bin/su", "/system/bin/magisk", "/system/xbin/magisk", - "/sbin/magisk"}; + "/sbin/magisk", + "/data/adb/magisk/magiskinit", + "/data/adb/magisk/magisk", + "/data/adb/magisk.db", + "/data/adb/ksu", + "/data/adb/apatch", + "/data/adb/ap/single"}; for (const auto &path : paths) { diff --git a/siro_driver/lib/controller/functions/crud.dart b/siro_driver/lib/controller/functions/crud.dart index fe9714a..f8ce2b8 100755 --- a/siro_driver/lib/controller/functions/crud.dart +++ b/siro_driver/lib/controller/functions/crud.dart @@ -132,21 +132,19 @@ class CRUD { try { attempts++; response = await doPost(); - break; // نجح الاتصال — نخرج + break; } on SocketException catch (_) { Log.print('⚠️ SocketException attempt $attempts — $link'); if (attempts >= 3) { _netGuard.notifyOnce((title, msg) => mySnackeBarError(msg)); return 'no_internet'; } - // انتظار قبل إعادة المحاولة — مهم للشبكات المتقطعة - await Future.delayed(const Duration(seconds: 1)); + await Future.delayed(Duration(seconds: attempts)); } on TimeoutException catch (_) { Log.print('⚠️ TimeoutException attempt $attempts — $link'); if (attempts >= 3) return 'failure'; - // لا انتظار — نعيد فوراً + await Future.delayed(Duration(milliseconds: 500 * attempts)); } catch (e) { - // errno = 9 (Bad file descriptor) — إعادة المحاولة if (e.toString().contains('errno = 9') && attempts < 3) { await Future.delayed(const Duration(milliseconds: 500)); continue; diff --git a/siro_driver/lib/controller/functions/package_info.dart b/siro_driver/lib/controller/functions/package_info.dart index e61db9f..c6c2353 100755 --- a/siro_driver/lib/controller/functions/package_info.dart +++ b/siro_driver/lib/controller/functions/package_info.dart @@ -301,7 +301,7 @@ class SecurityHelper { // } static void _showSecurityWarning() { // Use an RxInt to track the remaining seconds. This is the KEY! - RxInt secondsRemaining = 10.obs; + final secondsRemaining = RxInt(10); Get.dialog( CupertinoAlertDialog( diff --git a/siro_driver/lib/controller/functions/security_checks.dart b/siro_driver/lib/controller/functions/security_checks.dart index 9bd1226..cd774b8 100755 --- a/siro_driver/lib/controller/functions/security_checks.dart +++ b/siro_driver/lib/controller/functions/security_checks.dart @@ -16,7 +16,10 @@ class SecurityChecks { return result; } on PlatformException catch (e) { print("Failed to check security status: ${e.message}"); - return true; // Treat platform errors as a compromised device (for safety) + return false; // Platform not supported → treat as secure + } catch (e) { + print("Security check error: $e"); + return false; } } diff --git a/siro_driver/lib/controller/functions/ssl_pinning.dart b/siro_driver/lib/controller/functions/ssl_pinning.dart index e571778..761b3c9 100644 --- a/siro_driver/lib/controller/functions/ssl_pinning.dart +++ b/siro_driver/lib/controller/functions/ssl_pinning.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as http_io; class SslPinning { SslPinning._(); @@ -29,13 +30,13 @@ class SslPinning { (X509Certificate cert, String host, int port) { final derHash = base64.encode(sha256.convert(cert.der).bytes); for (final entry in _pins.entries) { - if (host.endsWith(entry.key)) { + if (host == entry.key || host.endsWith('.${entry.key}')) { if (entry.value.contains(derHash)) return true; } } if (_globalPins.contains(derHash)) return true; return false; }; - return http.IOClient(httpClient); + return http_io.IOClient(httpClient); } } diff --git a/siro_rider/android/app/src/main/cpp/native-lib.cpp b/siro_rider/android/app/src/main/cpp/native-lib.cpp index 750d8ae..b8dc5b7 100644 --- a/siro_rider/android/app/src/main/cpp/native-lib.cpp +++ b/siro_rider/android/app/src/main/cpp/native-lib.cpp @@ -19,7 +19,7 @@ #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -// Function to check for common root binaries +// Function to check for common root binaries (including Magisk, KernelSU, APatch) bool isRooted() { std::string paths[] = { @@ -28,7 +28,13 @@ bool isRooted() "/system/bin/su", "/system/bin/magisk", "/system/xbin/magisk", - "/sbin/magisk"}; + "/sbin/magisk", + "/data/adb/magisk/magiskinit", + "/data/adb/magisk/magisk", + "/data/adb/magisk.db", + "/data/adb/ksu", + "/data/adb/apatch", + "/data/adb/ap/single"}; for (const auto &path : paths) { diff --git a/siro_rider/lib/controller/functions/crud.dart b/siro_rider/lib/controller/functions/crud.dart index 15778a5..8d99671 100644 --- a/siro_rider/lib/controller/functions/crud.dart +++ b/siro_rider/lib/controller/functions/crud.dart @@ -24,16 +24,35 @@ class CRUD { final NetGuard _netGuard = NetGuard(); final _client = SslPinning.createPinnedClient(); - /// Stores the signature of the last logged error to prevent duplicates. + static bool _isRefreshingJWT = false; static String _lastErrorSignature = ''; - - /// Stores the timestamp of the last logged error. static DateTime _lastErrorTimestamp = DateTime(2000); - - /// The minimum time that must pass before logging the same error again. static const Duration _errorLogDebounceDuration = Duration(minutes: 1); - /// Asynchronously logs an error to the server with debouncing to prevent log flooding. + /// JWT validity check without external libraries. + static bool _isJwtValid(String? token) { + if (token == null || token.isEmpty) return false; + try { + final parts = token.split('.'); + if (parts.length != 3) return false; + String payload = parts[1]; + switch (payload.length % 4) { + case 2: + payload += '=='; + break; + case 3: + payload += '='; + break; + } + final decoded = jsonDecode(utf8.decode(base64Url.decode(payload))); + final exp = decoded['exp']; + if (exp == null) return false; + return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000); + } catch (_) { + return false; + } + } + static Future addError( String error, String details, String where) async { try { @@ -54,11 +73,9 @@ class CRUD { box.read(BoxName.driverID) != null ? 'Driver' : 'Passenger'; final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver); - // طباعة الخطأ في الكونسول للمطور للمتابعة الفورية Log.print( "🚨 [ADD_ERROR] Where: $where | Error: $error | Details: $details"); - // Fire-and-forget call to prevent infinite loops if the logger itself fails. CRUD().post( link: AppLink.addError, payload: { @@ -75,22 +92,14 @@ class CRUD { } } - // ───────────────────────────────────────────────────────────── - // دالة مساعدة خاصة: تجيب البصمة المشفرة من GetStorage - // ───────────────────────────────────────────────────────────── String _getFpHeader() { return box.read(BoxName.deviceFpEncrypted)?.toString() ?? ''; } - // ───────────────────────────────────────────────────────────── - // دالة مساعدة خاصة: تقرأ JWT من FlutterSecureStorage (آمن) - // بدلاً من GetStorage (غير مشفر) - // ───────────────────────────────────────────────────────────── Future _getJwt() async { try { final String? encryptedJwt = await storage.read(key: BoxName.jwt); if (encryptedJwt == null || encryptedJwt.isEmpty) { - // Fallback إلى GetStorage للتوافقية final String? fallback = box.read(BoxName.jwt); if (fallback != null) { return r(fallback).toString().split(Env.addd)[0]; @@ -100,7 +109,6 @@ class CRUD { return r(encryptedJwt).toString().split(Env.addd)[0]; } catch (e) { Log.print('Error reading JWT from SecureStorage: $e'); - // Fallback final String? fallback = box.read(BoxName.jwt); if (fallback != null) { return r(fallback).toString().split(Env.addd)[0]; @@ -109,168 +117,119 @@ class CRUD { } } - /// Centralized private method to handle all API requests. - /// Includes retry logic, network checking, and standardized error handling. + /// Centralized request handler with retry for weak networks. + /// For Syria (3G): 60s total timeout, 3 retries, exponential backoff. Future _makeRequest({ required String link, Map? payload, required Map headers, }) async { - const connectTimeout = Duration(seconds: 6); - const receiveTimeout = Duration(seconds: 10); + const totalTimeout = Duration(seconds: 60); Future doPost() { final url = Uri.parse(link); return _client .post(url, body: payload, headers: headers) - .timeout(connectTimeout + receiveTimeout); + .timeout(totalTimeout); } - http.Response response; - try { - // retry ذكي: محاولة واحدة إضافية فقط لأخطاء شبكة/5xx + http.Response? response; + int attempts = 0; + + while (attempts < 3) { try { + attempts++; response = await doPost(); + break; } on SocketException catch (_) { - response = await doPost(); - } on TimeoutException catch (_) { - response = await doPost(); - } - - final sc = response.statusCode; - final body = response.body; - Log.print('request: ${response.request}'); - Log.print('body: $body'); - // Log.print('link: $link'); - Log.print('headers: $headers'); - Log.print('payload: $payload'); - - // 2xx - if (sc >= 200 && sc < 300) { - try { - final jsonData = jsonDecode(body); - return jsonData; - } catch (e, st) { - addError('JSON Decode Error', 'Body: $body\n$st', - 'CRUD._makeRequest $link'); - return 'failure'; + Log.print('⚠️ SocketException attempt $attempts — $link'); + if (attempts >= 3) { + _netGuard.notifyOnce((title, msg) => mySnackeBarError(msg)); + return 'no_internet'; + } + await Future.delayed(Duration(seconds: attempts)); + } on TimeoutException catch (_) { + Log.print('⚠️ TimeoutException attempt $attempts — $link'); + if (attempts >= 3) return 'failure'; + } catch (e) { + if (e.toString().contains('errno = 9') && attempts < 3) { + await Future.delayed(const Duration(milliseconds: 500)); + continue; } - } - - // 401 → تجديد التوكن تلقائياً - if (sc == 401) { - await Get.put(LoginController()).getJWT(); - return 'token_expired'; - } - - // 5xx - if (sc >= 500) { addError( - 'Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link'); + 'HTTP Exception: $e', 'Try: $attempts', 'CRUD._makeRequest $link'); return 'failure'; } + } - // 4xx أخرى - return 'failure'; - } on SocketException { - _netGuard.notifyOnce((title, msg) => mySnackeBarError(msg)); - return 'no_internet'; - } on TimeoutException { - return 'failure'; - } catch (e, st) { - addError('HTTP Request Exception: $e', 'Stack: $st', - 'CRUD._makeRequest $link'); + if (response == null) return 'failure'; + + final sc = response.statusCode; + final body = response.body; + Log.print('request: ${response.request}'); + Log.print('body: $body'); + Log.print('payload: $payload'); + + if (sc >= 200 && sc < 300) { + try { + return jsonDecode(body); + } catch (e, st) { + addError('JSON Decode Error', 'Body: $body\n$st', + 'CRUD._makeRequest $link'); + return 'failure'; + } + } + + if (sc == 401) { + final isNonCritical = link.contains('errorApp.php'); + if (!_isRefreshingJWT && !isNonCritical) { + _isRefreshingJWT = true; + try { + await Get.put(LoginController()).getJWT(); + } finally { + _isRefreshingJWT = false; + } + } + return 'token_expired'; + } + + if (sc >= 500) { + addError( + 'Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link'); return 'failure'; } + + return 'failure'; } - // ═══════════════════════════════════════════════════════════════ - // post — طلب POST عادي للراكب/السائق - // ─────────────────────────────────────────────────────────────── - // التغيير: إضافة X-Device-FP header - // القيمة: fp_encrypted من GetStorage - // السيرفر يتحقق: sha256(fp_encrypted + FP_PEPPER) == JWT.fingerPrint - // ═══════════════════════════════════════════════════════════════ Future post({ required String link, Map? payload, }) async { - final token = await _getJwt(); + String token = await _getJwt(); final headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $token', - 'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز + 'X-Device-FP': _getFpHeader(), }; - return await _makeRequest( - link: link, - payload: payload, - headers: headers, - ); + return await _makeRequest(link: link, payload: payload, headers: headers); } - // ═══════════════════════════════════════════════════════════════ - // get — طلب GET للراكب/السائق (يستخدم POST method) - // ─────────────────────────────────────────────────────────────── - // التغيير: إضافة X-Device-FP header - // ═══════════════════════════════════════════════════════════════ Future get({ required String link, Map? payload, }) async { - final token = await _getJwt(); - var url = Uri.parse(link); - var response = await _client.post( - url, - body: payload, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': 'Bearer $token', - 'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز - }, - ); + String token = await _getJwt(); - Log.print('request: ${response.request}'); - Log.print('body: ${response.body}'); - Log.print('payload: $payload'); + final headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer $token', + 'X-Device-FP': _getFpHeader(), + }; - if (response.statusCode == 200) { - return response.body; - } else if (response.statusCode == 401) { - var jsonData = jsonDecode(response.body); - if (jsonData['error'] == 'Token expired') { - print("CRUD.get: Token expired, refreshing and retrying once..."); - await Get.put(LoginController()).getJWT(); - - // إعادة المحاولة مرة واحدة فقط بتوكن جديد - var retryResponse = await _client.post( - url, - body: payload, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': - 'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}', - 'X-Device-FP': _getFpHeader(), - }, - ); - - if (retryResponse.statusCode == 200) { - return retryResponse.body; - } - return jsonEncode( - {'status': 'failure', 'message': 'token_expired_retry_failed'}); - } else { - return jsonEncode({'status': 'failure', 'message': '401_unauthorized'}); - } - } else { - addError('Non-200 response code: ${response.statusCode}', - 'crud().get - Other', url.toString()); - return jsonEncode({ - 'status': 'failure', - 'message': 'server_error_${response.statusCode}' - }); - } + return await _makeRequest(link: link, payload: payload, headers: headers); } // ═══════════════════════════════════════════════════════════════ @@ -290,65 +249,30 @@ class CRUD { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $jwt', 'X-HMAC-Auth': hmac.toString(), - 'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز + 'X-Device-FP': _getFpHeader(), }; - // add print debug Log.print('headers: $headers'); Log.print('payload: $payload'); Log.print('link: $link'); - return await _makeRequest( - link: link, - payload: payload, - headers: headers, - ); + return await _makeRequest(link: link, payload: payload, headers: headers); } - // ═══════════════════════════════════════════════════════════════ - // getWallet — طلب GET لسيرفر المدفوعات (يستخدم POST method) - // ─────────────────────────────────────────────────────────────── - // التغيير: إضافة X-Device-FP header - // ═══════════════════════════════════════════════════════════════ Future getWallet({ required String link, Map? payload, }) async { var s = await LoginController().getJwtWallet(); final hmac = box.read(BoxName.hmac); - var url = Uri.parse(link); - var response = await _client.post( - url, - body: payload, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': 'Bearer $s', - 'X-HMAC-Auth': hmac.toString(), - 'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز - }, - ); + final headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer $s', + 'X-HMAC-Auth': hmac.toString(), + 'X-Device-FP': _getFpHeader(), + }; - if (response.statusCode == 200) { - var jsonData = jsonDecode(response.body); - if (jsonData['status'] == 'success') { - return response.body; - } - return jsonData['status']; - } else if (response.statusCode == 401) { - var jsonData = jsonDecode(response.body); - if (jsonData['error'] == 'Token expired') { - await Get.put(LoginController()).getJwtWallet(); - return 'token_expired'; - } else { - addError('Unauthorized: ${jsonData['error']}', 'crud().getWallet - 401', - url.toString()); - return 'failure'; - } - } else { - addError('Non-200 response code: ${response.statusCode}', - 'crud().getWallet - Other', url.toString()); - return 'failure'; - } + return await _makeRequest(link: link, payload: payload, headers: headers); } // ======================================================================= @@ -361,65 +285,20 @@ class CRUD { {required String link, Map? payload}) async { final s = await LoginController().getJwtWallet(); final hmac = box.read(BoxName.hmac); - final url = Uri.parse(link); - try { - final response = await _client.post( - url, - body: payload, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': 'Bearer $s', - 'X-HMAC-Auth': hmac.toString(), - 'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز - }, - ); + final headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer $s', + 'X-HMAC-Auth': hmac.toString(), + 'X-Device-FP': _getFpHeader(), + }; - Map wrap(String status, {Object? message, int? code}) { - return { - 'status': status, - 'message': message, - 'code': code ?? response.statusCode, - }; - } - - if (response.statusCode == 200) { - try { - return jsonDecode(response.body); - } catch (e) { - return wrap('failure', - message: 'JSON decode error', code: response.statusCode); - } - } else if (response.statusCode == 401) { - try { - final jsonData = jsonDecode(response.body); - if (jsonData is Map && jsonData['error'] == 'Token expired') { - await Get.put(LoginController()).getJWT(); - return { - 'status': 'failure', - 'message': 'token_expired', - 'code': 401 - }; - } - return wrap('failure', message: jsonData); - } catch (_) { - return wrap('failure', message: response.body); - } - } else { - try { - final jsonData = jsonDecode(response.body); - return wrap('failure', message: jsonData); - } catch (_) { - return wrap('failure', message: response.body); - } - } - } catch (e) { - return { - 'status': 'failure', - 'message': 'HTTP request error: $e', - 'code': -1 - }; + final result = await _makeRequest(link: link, payload: payload, headers: headers); + if (result is Map || result is List) return result; + if (result == 'no_internet') { + return {'status': 'failure', 'message': 'no_internet', 'code': -1}; } + return result; } Future sendWhatsAppAuth(String to, String token) async { diff --git a/siro_rider/lib/controller/functions/package_info.dart b/siro_rider/lib/controller/functions/package_info.dart index e4d844d..7f809a8 100644 --- a/siro_rider/lib/controller/functions/package_info.dart +++ b/siro_rider/lib/controller/functions/package_info.dart @@ -254,7 +254,7 @@ class SecurityHelper { /// Deletes all app data static void _showSecurityWarning() { // Use an RxInt to track the remaining seconds. This is the KEY! - RxInt secondsRemaining = 10.obs; + final secondsRemaining = RxInt(10); Get.dialog( CupertinoAlertDialog( diff --git a/siro_rider/lib/controller/functions/securty_check.dart b/siro_rider/lib/controller/functions/securty_check.dart index 3ceaafb..a660a63 100644 --- a/siro_rider/lib/controller/functions/securty_check.dart +++ b/siro_rider/lib/controller/functions/securty_check.dart @@ -17,7 +17,10 @@ class SecurityChecks { return result; } on PlatformException catch (e) { Log.print("Failed to check security status: ${e.message}"); - return true; // Treat platform errors as a compromised device (for safety) + return false; // Platform not supported → treat as secure (not compromised) + } catch (e) { + Log.print("Security check error: $e"); + return false; } } diff --git a/siro_rider/lib/controller/functions/ssl_pinning.dart b/siro_rider/lib/controller/functions/ssl_pinning.dart index e571778..761b3c9 100644 --- a/siro_rider/lib/controller/functions/ssl_pinning.dart +++ b/siro_rider/lib/controller/functions/ssl_pinning.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as http_io; class SslPinning { SslPinning._(); @@ -29,13 +30,13 @@ class SslPinning { (X509Certificate cert, String host, int port) { final derHash = base64.encode(sha256.convert(cert.der).bytes); for (final entry in _pins.entries) { - if (host.endsWith(entry.key)) { + if (host == entry.key || host.endsWith('.${entry.key}')) { if (entry.value.contains(derHash)) return true; } } if (_globalPins.contains(derHash)) return true; return false; }; - return http.IOClient(httpClient); + return http_io.IOClient(httpClient); } } diff --git a/siro_service/android/app/src/main/cpp/native-lib.cpp b/siro_service/android/app/src/main/cpp/native-lib.cpp index 65c8bcc..d3c5fe4 100755 --- a/siro_service/android/app/src/main/cpp/native-lib.cpp +++ b/siro_service/android/app/src/main/cpp/native-lib.cpp @@ -20,7 +20,7 @@ #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -// Function to check for common root binaries +// Function to check for common root binaries (including Magisk, KernelSU, APatch) bool isRooted() { std::string paths[] = { @@ -29,7 +29,13 @@ bool isRooted() "/system/bin/su", "/system/bin/magisk", "/system/xbin/magisk", - "/sbin/magisk"}; + "/sbin/magisk", + "/data/adb/magisk/magiskinit", + "/data/adb/magisk/magisk", + "/data/adb/magisk.db", + "/data/adb/ksu", + "/data/adb/apatch", + "/data/adb/ap/single"}; for (const auto &path : paths) { diff --git a/siro_service/lib/controller/functions/ssl_pinning.dart b/siro_service/lib/controller/functions/ssl_pinning.dart index e571778..761b3c9 100644 --- a/siro_service/lib/controller/functions/ssl_pinning.dart +++ b/siro_service/lib/controller/functions/ssl_pinning.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as http_io; class SslPinning { SslPinning._(); @@ -29,13 +30,13 @@ class SslPinning { (X509Certificate cert, String host, int port) { final derHash = base64.encode(sha256.convert(cert.der).bytes); for (final entry in _pins.entries) { - if (host.endsWith(entry.key)) { + if (host == entry.key || host.endsWith('.${entry.key}')) { if (entry.value.contains(derHash)) return true; } } if (_globalPins.contains(derHash)) return true; return false; }; - return http.IOClient(httpClient); + return http_io.IOClient(httpClient); } }