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
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<String> 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<Map<String, dynamic>> deviceDataList = [];
|
||||
|
||||
// static Future<List<Map<String, dynamic>>> getDeviceInfo() async {
|
||||
// final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||
|
||||
// try {
|
||||
// if (Platform.isAndroid) {
|
||||
// AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
|
||||
// Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> deviceData = {
|
||||
// 'platform': 'macOS',
|
||||
// 'model': macInfo.model,
|
||||
// 'version': macInfo.systemGUID,
|
||||
// };
|
||||
// deviceDataList.add(deviceData);
|
||||
// } else if (Platform.isWindows) {
|
||||
// WindowsDeviceInfo windowsInfo = await deviceInfoPlugin.windowsInfo;
|
||||
// Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> deviceData in deviceDataList) {
|
||||
// 'Version: ${deviceData['version'] ?? deviceData['versionRelease'] ?? 'N/A'}');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<void> 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<String> _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<dynamic> _makeRequest({
|
||||
required String link,
|
||||
Map<String, dynamic>? payload,
|
||||
required Map<String, String> headers,
|
||||
}) async {
|
||||
const connectTimeout = Duration(seconds: 6);
|
||||
const receiveTimeout = Duration(seconds: 10);
|
||||
const totalTimeout = Duration(seconds: 60);
|
||||
|
||||
Future<http.Response> 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<dynamic> post({
|
||||
required String link,
|
||||
Map<String, dynamic>? 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<dynamic> get({
|
||||
required String link,
|
||||
Map<String, dynamic>? 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<dynamic> getWallet({
|
||||
required String link,
|
||||
Map<String, dynamic>? 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<String, dynamic>? 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<String, dynamic> 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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user