From 753497649a0261afcd9571d65c69c3722a65a478 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 8 May 2026 06:19:56 +0300 Subject: [PATCH] Update: 2026-05-08 06:19:56 --- musadaq-app/android/app/build.gradle.kts | 44 +- musadaq-app/android/app/google-services.json | 4 +- musadaq-app/android/app/proguard-rules.pro | 22 + .../android/app/src/main/AndroidManifest.xml | 2 +- .../app}/MainActivity.kt | 2 +- .../app/src/main/res/values/strings.xml | 4 + .../reports/problems/problems-report.html | 663 ++++++++++++++++++ musadaq-app/firebase.json | 2 +- .../ios/Runner/GoogleService-Info.plist | 4 +- .../services/device_security_service.dart | 237 +++++++ .../services/shorebird_update_service.dart | 147 ++++ .../settings/views/settings_view.dart | 25 + musadaq-app/lib/firebase_options.dart | 9 +- musadaq-app/lib/main.dart | 8 +- musadaq-app/pubspec.lock | 16 + musadaq-app/pubspec.yaml | 7 + musadaq-app/shorebird.yaml | 14 + public/privacy.php | 121 ++++ 18 files changed, 1310 insertions(+), 21 deletions(-) create mode 100644 musadaq-app/android/app/proguard-rules.pro rename musadaq-app/android/app/src/main/kotlin/com/{example/musadaq_app => musadaq/app}/MainActivity.kt (77%) create mode 100644 musadaq-app/android/app/src/main/res/values/strings.xml create mode 100644 musadaq-app/android/build/reports/problems/problems-report.html create mode 100644 musadaq-app/lib/core/services/device_security_service.dart create mode 100644 musadaq-app/lib/core/services/shorebird_update_service.dart create mode 100644 musadaq-app/shorebird.yaml create mode 100644 public/privacy.php diff --git a/musadaq-app/android/app/build.gradle.kts b/musadaq-app/android/app/build.gradle.kts index f68abdc..1a9e691 100644 --- a/musadaq-app/android/app/build.gradle.kts +++ b/musadaq-app/android/app/build.gradle.kts @@ -1,3 +1,6 @@ +import java.util.Properties +import java.io.FileInputStream + plugins { id("com.android.application") // START: FlutterFire Configuration @@ -8,10 +11,17 @@ plugins { id("dev.flutter.flutter-gradle-plugin") } +// Load keystore properties from key.properties file +val keystorePropertiesFile = rootProject.file("key.properties") +val keystoreProperties = Properties() +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) +} + android { - namespace = "com.example.musadaq_app" + namespace = "com.musadaq.app" compileSdk = flutter.compileSdkVersion - ndkVersion = "27.0.12077973" + ndkVersion = "28.2.13676358" compileOptions { sourceCompatibility = JavaVersion.VERSION_11 @@ -22,11 +32,19 @@ android { jvmTarget = JavaVersion.VERSION_11.toString() } + signingConfigs { + create("release") { + if (keystorePropertiesFile.exists()) { + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["keyPassword"] as String + storeFile = file(keystoreProperties["storeFile"] as String) + storePassword = keystoreProperties["storePassword"] as String + } + } + } + defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.musadaq_app" - // You can update the following values to match your application needs. - // For more information, see: https://flutter.dev/to/review-gradle-config. + applicationId = "com.musadaq.app" minSdk = 24 targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode @@ -35,9 +53,17 @@ android { buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.getByName("debug") + signingConfig = if (keystorePropertiesFile.exists()) { + signingConfigs.getByName("release") + } else { + signingConfigs.getByName("debug") + } + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) } } } diff --git a/musadaq-app/android/app/google-services.json b/musadaq-app/android/app/google-services.json index 8f590dc..78e1098 100644 --- a/musadaq-app/android/app/google-services.json +++ b/musadaq-app/android/app/google-services.json @@ -7,9 +7,9 @@ "client": [ { "client_info": { - "mobilesdk_app_id": "1:512384487867:android:eac271c0b0ea64b708749e", + "mobilesdk_app_id": "1:512384487867:android:6098e34775ad6c7708749e", "android_client_info": { - "package_name": "com.example.musadaq_app" + "package_name": "com.musadaq.app" } }, "oauth_client": [], diff --git a/musadaq-app/android/app/proguard-rules.pro b/musadaq-app/android/app/proguard-rules.pro new file mode 100644 index 0000000..fcc3198 --- /dev/null +++ b/musadaq-app/android/app/proguard-rules.pro @@ -0,0 +1,22 @@ +# Flutter ProGuard Rules for Musadaq + +# Keep Flutter engine +-keep class io.flutter.** { *; } +-keep class io.flutter.plugins.** { *; } + +# Keep Firebase +-keep class com.google.firebase.** { *; } + +# Keep Dio +-keep class io.github.nicekun.** { *; } +-dontwarn okhttp3.** +-dontwarn okio.** + +# Keep CameraAwesome +-keep class com.apparence.camerawesome.** { *; } + +# Keep ObjectBox +-keep class io.objectbox.** { *; } + +# Ignore missing Play Core classes referenced by Flutter Engine +-dontwarn com.google.android.play.core.** diff --git a/musadaq-app/android/app/src/main/AndroidManifest.xml b/musadaq-app/android/app/src/main/AndroidManifest.xml index 38f4abb..823bd76 100644 --- a/musadaq-app/android/app/src/main/AndroidManifest.xml +++ b/musadaq-app/android/app/src/main/AndroidManifest.xml @@ -16,7 +16,7 @@ diff --git a/musadaq-app/android/app/src/main/kotlin/com/example/musadaq_app/MainActivity.kt b/musadaq-app/android/app/src/main/kotlin/com/musadaq/app/MainActivity.kt similarity index 77% rename from musadaq-app/android/app/src/main/kotlin/com/example/musadaq_app/MainActivity.kt rename to musadaq-app/android/app/src/main/kotlin/com/musadaq/app/MainActivity.kt index 51c54a7..3732743 100644 --- a/musadaq-app/android/app/src/main/kotlin/com/example/musadaq_app/MainActivity.kt +++ b/musadaq-app/android/app/src/main/kotlin/com/musadaq/app/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.musadaq_app +package com.musadaq.app import io.flutter.embedding.android.FlutterFragmentActivity diff --git a/musadaq-app/android/app/src/main/res/values/strings.xml b/musadaq-app/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..a7fba72 --- /dev/null +++ b/musadaq-app/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + مُصادَق + diff --git a/musadaq-app/android/build/reports/problems/problems-report.html b/musadaq-app/android/build/reports/problems/problems-report.html new file mode 100644 index 0000000..d620e5f --- /dev/null +++ b/musadaq-app/android/build/reports/problems/problems-report.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/musadaq-app/firebase.json b/musadaq-app/firebase.json index 00ae318..ad2235f 100644 --- a/musadaq-app/firebase.json +++ b/musadaq-app/firebase.json @@ -1 +1 @@ -{"flutter":{"platforms":{"android":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:android:eac271c0b0ea64b708749e","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:ios:03bd28c6088a4aa008749e","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"musadaq-c12ca","configurations":{"android":"1:512384487867:android:eac271c0b0ea64b708749e","ios":"1:512384487867:ios:03bd28c6088a4aa008749e"}}}}}} \ No newline at end of file +{"flutter":{"platforms":{"android":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:android:6098e34775ad6c7708749e","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:ios:c743b83778682d8308749e","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"musadaq-c12ca","configurations":{"android":"1:512384487867:android:6098e34775ad6c7708749e","ios":"1:512384487867:ios:c743b83778682d8308749e"}}}}}} \ No newline at end of file diff --git a/musadaq-app/ios/Runner/GoogleService-Info.plist b/musadaq-app/ios/Runner/GoogleService-Info.plist index 2a474be..6d1661c 100644 --- a/musadaq-app/ios/Runner/GoogleService-Info.plist +++ b/musadaq-app/ios/Runner/GoogleService-Info.plist @@ -9,7 +9,7 @@ PLIST_VERSION 1 BUNDLE_ID - com.example.musadaqApp + com.musadaq.app PROJECT_ID musadaq-c12ca STORAGE_BUCKET @@ -25,6 +25,6 @@ IS_SIGNIN_ENABLED GOOGLE_APP_ID - 1:512384487867:ios:03bd28c6088a4aa008749e + 1:512384487867:ios:c743b83778682d8308749e \ No newline at end of file diff --git a/musadaq-app/lib/core/services/device_security_service.dart b/musadaq-app/lib/core/services/device_security_service.dart new file mode 100644 index 0000000..32f2637 --- /dev/null +++ b/musadaq-app/lib/core/services/device_security_service.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:freerasp/freerasp.dart'; +import 'package:get/get.dart'; + +/// Device Security Service +/// Enterprise-grade jailbreak/root/tamper detection for Musadaq using freeRASP. +/// +/// Detects: root, jailbreak, debugger, emulator, hooking frameworks, +/// tampered apps, unofficial stores, missing passcode. +/// +/// Policy: On detection → block app with security warning (no data access). +class DeviceSecurityService extends GetxService { + final isCompromised = false.obs; + final threatDetails = [].obs; + final securityChecked = false.obs; + + @override + void onInit() { + super.onInit(); + _initSecurity(); + } + + /// Initialize freeRASP + Future _initSecurity() async { + try { + await _initFreeRASP(); + } catch (e) { + debugPrint('[Security] freeRASP init failed: $e'); + } + securityChecked.value = true; + } + + /// Initialize freeRASP with full threat callbacks + Future _initFreeRASP() async { + final callback = ThreatCallback( + onAppIntegrity: () => _onThreatDetected('تلاعب بالتطبيق'), + onObfuscationIssues: () => _onThreatDetected('عدم وجود تشفير الكود'), + onDebug: () => _onThreatDetected('وضع التصحيح مفعّل'), + onDeviceBinding: () => _onThreatDetected('تغيير جهاز غير مصرح'), + onDeviceID: () => _onThreatDetected('معرّف جهاز مزوّر'), + onHooks: () => _onThreatDetected('أدوات اختراق (Hooking)'), + onPasscode: () => _onThreatDetected('الجهاز بدون قفل شاشة'), + onPrivilegedAccess: () => _onThreatDetected('جهاز مروّت (Root/Jailbreak)'), + onSecureHardwareNotAvailable: () => + _onThreatDetected('عدم توفر عتاد آمن'), + onSimulator: () => _onThreatDetected('محاكي (Emulator)'), + onUnofficialStore: () => _onThreatDetected('تثبيت من متجر غير رسمي'), + ); + + // Configure talsec + final config = TalsecConfig( + androidConfig: AndroidConfig( + packageName: 'com.musadaq.app', + signingCertHashes: ['4F:10:B4:E9:29:E0:5E:0A:3E:AE:B0:31:4C:C1:3A:DB:CB:E6:FF:DF:6A:F5:85:FC:68:FE:C1:E4:B6:29:6B:6F'], + supportedStores: ['com.android.vending'], // Google Play only + ), + iosConfig: IOSConfig( + bundleIds: ['com.musadaq.app'], + teamId: 'REPLACE_WITH_YOUR_TEAM_ID', + ), + watcherMail: 'security@musadaq.jo', + isProd: true, + ); + + await Talsec.instance.start(config); + Talsec.instance.attachListener(callback); + debugPrint('[Security] freeRASP initialized'); + } + + /// Handle threat detection + void _onThreatDetected(String threat) { + debugPrint('[Security] ⚠️ THREAT: $threat'); + threatDetails.add(threat); + isCompromised.value = true; + + // Block the app immediately + _showSecurityBlock(); + } + + /// Show a non-dismissible security warning that blocks the app + void _showSecurityBlock() { + // Only show once + if (Get.isDialogOpen == true) return; + + Get.dialog( + PopScope( + canPop: false, + child: Scaffold( + backgroundColor: const Color(0xFF1A1A2E), + body: SafeArea( + child: Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Shield icon + Container( + width: 100, + height: 100, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red.withValues(alpha: 0.15), + ), + child: const Icon( + Icons.shield_rounded, + size: 56, + color: Colors.red, + ), + ), + const SizedBox(height: 32), + + // Title + const Text( + 'تحذير أمني', + style: TextStyle( + color: Colors.white, + fontSize: 28, + fontWeight: FontWeight.bold, + fontFamily: 'El Messiri', + ), + ), + const SizedBox(height: 16), + + // Message + const Text( + 'تم اكتشاف أن هذا الجهاز غير آمن.\n' + 'لحماية بياناتك المالية والضريبية،\n' + 'لا يمكن تشغيل مُصادَق على هذا الجهاز.', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white70, + fontSize: 15, + height: 1.6, + ), + ), + const SizedBox(height: 24), + + // Threat details + Obx(() => Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.red.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.red.withValues(alpha: 0.3)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'التهديدات المكتشفة:', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + fontSize: 13, + ), + ), + const SizedBox(height: 8), + ...threatDetails.map((t) => Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + children: [ + const Icon(Icons.warning_amber, + color: Colors.red, size: 16), + const SizedBox(width: 8), + Text(t, + style: const TextStyle( + color: Colors.white60, + fontSize: 13)), + ], + ), + )), + ], + ), + )), + + const SizedBox(height: 32), + + // Close button (exits the app) + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () => SystemNavigator.pop(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + icon: const Icon(Icons.exit_to_app, + color: Colors.white), + label: const Text( + 'إغلاق التطبيق', + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + + const SizedBox(height: 16), + + // Help text + const Text( + 'إذا كنت تعتقد أن هذا خطأ، تواصل مع\nsupport@musadaq.jo', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white38, fontSize: 12), + ), + ], + ), + ), + ), + ), + ), + ), + barrierDismissible: false, + ); + } + + /// Quick check if device is safe (for use in API calls) + bool get isSafe => !isCompromised.value; + + /// Get security status for display + Map getSecurityStatus() { + return { + 'is_safe': isSafe, + 'checked': securityChecked.value, + 'threats': threatDetails.toList(), + 'threat_count': threatDetails.length, + }; + } +} diff --git a/musadaq-app/lib/core/services/shorebird_update_service.dart b/musadaq-app/lib/core/services/shorebird_update_service.dart new file mode 100644 index 0000000..3d04e82 --- /dev/null +++ b/musadaq-app/lib/core/services/shorebird_update_service.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:shorebird_code_push/shorebird_code_push.dart'; + +/// Shorebird Code Push Service +/// Handles OTA (Over-The-Air) updates for Dart code. +/// Checks for patches on app launch and notifies users when updates are available. +class ShorebirdUpdateService extends GetxService { + final _updater = ShorebirdUpdater(); + + final isUpdateAvailable = false.obs; + final isDownloading = false.obs; + final currentPatchNumber = 0.obs; + + @override + void onInit() { + super.onInit(); + _checkForUpdate(); + } + + /// Check if Shorebird is available (only works with shorebird release builds) + bool get isShorebirdAvailable => _updater.isAvailable; + + /// Get current patch number + Future loadCurrentPatch() async { + if (!isShorebirdAvailable) return; + try { + final patch = await _updater.readCurrentPatch(); + currentPatchNumber.value = patch?.number ?? 0; + } catch (_) {} + } + + /// Check for updates on launch + Future _checkForUpdate() async { + if (!isShorebirdAvailable) { + debugPrint('[Shorebird] Not available (debug build)'); + return; + } + + try { + await loadCurrentPatch(); + debugPrint('[Shorebird] Current patch: ${currentPatchNumber.value}'); + + final status = await _updater.checkForUpdate(); + if (status == UpdateStatus.outdated) { + isUpdateAvailable.value = true; + debugPrint('[Shorebird] Update available!'); + _showUpdateDialog(); + } else { + debugPrint('[Shorebird] App is up to date'); + } + } catch (e) { + debugPrint('[Shorebird] Error checking for update: $e'); + } + } + + /// Download and apply the update + Future downloadUpdate() async { + if (!isShorebirdAvailable) return; + + isDownloading.value = true; + try { + await _updater.update(); + isDownloading.value = false; + isUpdateAvailable.value = false; + + // Show restart prompt + Get.defaultDialog( + title: '✅ تم التحديث', + middleText: 'تم تحميل التحديث بنجاح.\nأعد تشغيل التطبيق لتطبيق التغييرات.', + textConfirm: 'حسناً', + confirmTextColor: Colors.white, + buttonColor: const Color(0xFF0F4C81), + onConfirm: () => Get.back(), + titleStyle: const TextStyle(fontWeight: FontWeight.bold), + radius: 14, + ); + } catch (e) { + isDownloading.value = false; + debugPrint('[Shorebird] Download failed: $e'); + Get.snackbar( + 'خطأ', + 'فشل تحميل التحديث', + backgroundColor: Colors.red, + colorText: Colors.white, + ); + } + } + + /// Show update available dialog + void _showUpdateDialog() { + Get.defaultDialog( + title: '🔄 تحديث متاح', + middleText: 'يتوفر تحديث جديد لمُصادَق.\nهل تريد تحميله الآن؟', + textConfirm: 'تحديث', + textCancel: 'لاحقاً', + confirmTextColor: Colors.white, + buttonColor: const Color(0xFF0F4C81), + onConfirm: () { + Get.back(); + downloadUpdate(); + }, + titleStyle: const TextStyle(fontWeight: FontWeight.bold), + radius: 14, + ); + } + + /// Manual check (can be called from settings) + Future manualCheckForUpdate() async { + if (!isShorebirdAvailable) { + Get.snackbar( + 'تنبيه', + 'التحديث التلقائي متاح فقط في نسخة الإصدار', + backgroundColor: Colors.orange, + colorText: Colors.white, + ); + return; + } + + Get.snackbar( + 'جارٍ الفحص...', + 'يتم البحث عن تحديثات', + backgroundColor: const Color(0xFF0F4C81), + colorText: Colors.white, + showProgressIndicator: true, + duration: const Duration(seconds: 2), + ); + + try { + final status = await _updater.checkForUpdate(); + if (status == UpdateStatus.outdated) { + isUpdateAvailable.value = true; + _showUpdateDialog(); + } else { + Get.snackbar( + '✅ محدّث', + 'التطبيق يعمل بأحدث إصدار (Patch ${currentPatchNumber.value})', + backgroundColor: const Color(0xFF10B981), + colorText: Colors.white, + ); + } + } catch (e) { + Get.snackbar('خطأ', 'فشل البحث عن تحديثات', + backgroundColor: Colors.red, colorText: Colors.white); + } + } +} diff --git a/musadaq-app/lib/features/settings/views/settings_view.dart b/musadaq-app/lib/features/settings/views/settings_view.dart index 986d81c..9b855ae 100644 --- a/musadaq-app/lib/features/settings/views/settings_view.dart +++ b/musadaq-app/lib/features/settings/views/settings_view.dart @@ -3,6 +3,8 @@ import 'package:get/get.dart'; import '../controllers/settings_controller.dart'; import '../../../app/routes/app_pages.dart'; import '../../../core/utils/app_snackbar.dart'; +import '../../../core/services/shorebird_update_service.dart'; +import '../../../core/services/device_security_service.dart'; class SettingsView extends GetView { const SettingsView({super.key}); @@ -148,6 +150,18 @@ class SettingsView extends GetView { isDark: isDark, ), const Divider(height: 1), + Builder(builder: (_) { + final security = Get.find(); + return Obx(() => _buildInfoTile( + icon: security.isSafe + ? Icons.verified_user_rounded + : Icons.gpp_bad_rounded, + title: 'حالة الأمان', + trailing: security.isSafe ? '✅ آمن' : '⚠️ غير آمن', + isDark: isDark, + )); + }), + const Divider(height: 1), _buildInfoTile( icon: Icons.diamond_rounded, title: 'الاشتراكات والباقات', @@ -170,6 +184,17 @@ class SettingsView extends GetView { isDark: isDark, onTap: () {}, ), + const Divider(height: 1), + _buildInfoTile( + icon: Icons.system_update_rounded, + title: 'البحث عن تحديثات', + trailing: 'فحص →', + isDark: isDark, + onTap: () { + final shorebird = Get.find(); + shorebird.manualCheckForUpdate(); + }, + ), ]), const SizedBox(height: 32), _buildLogoutButton(), diff --git a/musadaq-app/lib/firebase_options.dart b/musadaq-app/lib/firebase_options.dart index cc26806..3dd1dda 100644 --- a/musadaq-app/lib/firebase_options.dart +++ b/musadaq-app/lib/firebase_options.dart @@ -51,7 +51,7 @@ class DefaultFirebaseOptions { static const FirebaseOptions android = FirebaseOptions( apiKey: 'AIzaSyB7Gc_RNvFaFCsuN5acHK2SNkY5iMDecqk', - appId: '1:512384487867:android:eac271c0b0ea64b708749e', + appId: '1:512384487867:android:6098e34775ad6c7708749e', messagingSenderId: '512384487867', projectId: 'musadaq-c12ca', storageBucket: 'musadaq-c12ca.firebasestorage.app', @@ -59,10 +59,11 @@ class DefaultFirebaseOptions { static const FirebaseOptions ios = FirebaseOptions( apiKey: 'AIzaSyBLKc35OqzY6oQA5507E2uHCCHQbRWAC_M', - appId: '1:512384487867:ios:03bd28c6088a4aa008749e', + appId: '1:512384487867:ios:c743b83778682d8308749e', messagingSenderId: '512384487867', projectId: 'musadaq-c12ca', storageBucket: 'musadaq-c12ca.firebasestorage.app', - iosBundleId: 'com.example.musadaqApp', + iosBundleId: 'com.musadaq.app', ); -} + +} \ No newline at end of file diff --git a/musadaq-app/lib/main.dart b/musadaq-app/lib/main.dart index bd37c86..69eb257 100644 --- a/musadaq-app/lib/main.dart +++ b/musadaq-app/lib/main.dart @@ -6,6 +6,8 @@ import 'app/routes/app_pages.dart'; import 'core/services/push_notification_service.dart'; import 'core/services/upload_progress_service.dart'; import 'core/services/home_widget_service.dart'; +import 'core/services/shorebird_update_service.dart'; +import 'core/services/device_security_service.dart'; import 'app/theme/app_theme.dart'; @pragma('vm:entry-point') @@ -24,9 +26,13 @@ void main() async { // 2. Register background handler FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); - // 3. Register global services + // 3. Security check (MUST be first) + Get.put(DeviceSecurityService(), permanent: true); + + // 4. Register global services Get.put(UploadProgressService(), permanent: true); Get.put(HomeWidgetService(), permanent: true); + Get.put(ShorebirdUpdateService(), permanent: true); runApp(const MusadaqApp()); } diff --git a/musadaq-app/pubspec.lock b/musadaq-app/pubspec.lock index 83bfa23..eae5a09 100644 --- a/musadaq-app/pubspec.lock +++ b/musadaq-app/pubspec.lock @@ -592,6 +592,14 @@ packages: description: flutter source: sdk version: "0.0.0" + freerasp: + dependency: "direct main" + description: + name: freerasp + sha256: "76a3fb6f8e3fdd7d83e224866998523e7fb79d5779321983e484a6cfbf4b01b5" + url: "https://pub.dev" + source: hosted + version: "6.12.0" frontend_server_client: dependency: transitive description: @@ -1264,6 +1272,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + shorebird_code_push: + dependency: "direct main" + description: + name: shorebird_code_push + sha256: "55d5eea098a4293199d22dfd263ce1d6c10820ceb42c2acb950f971a8bcee0ce" + url: "https://pub.dev" + source: hosted + version: "2.0.6" sky_engine: dependency: transitive description: flutter diff --git a/musadaq-app/pubspec.yaml b/musadaq-app/pubspec.yaml index fbf2b3d..627d095 100644 --- a/musadaq-app/pubspec.yaml +++ b/musadaq-app/pubspec.yaml @@ -63,6 +63,12 @@ dependencies: firebase_core: ^4.7.0 firebase_messaging: ^16.2.0 + # ─── Code Push (OTA Updates) ──────────────────────── + shorebird_code_push: ^2.0.0 + + # ─── Security (Root/Jailbreak/Tamper Detection) ───── + freerasp: ^6.6.0 + dev_dependencies: flutter_test: sdk: flutter @@ -85,6 +91,7 @@ flutter_launcher_icons: flutter: uses-material-design: true assets: + - shorebird.yaml - assets/images/logo.jpg - assets/images/onboarding_1.png - assets/images/onboarding_2.png diff --git a/musadaq-app/shorebird.yaml b/musadaq-app/shorebird.yaml new file mode 100644 index 0000000..7ffe8f5 --- /dev/null +++ b/musadaq-app/shorebird.yaml @@ -0,0 +1,14 @@ +# This file is used to configure the Shorebird updater used by your app. +# Learn more at https://docs.shorebird.dev +# This file does not contain any sensitive information and should be checked into version control. + +# Your app_id is the unique identifier assigned to your app. +# It is used to identify your app when requesting patches from Shorebird's servers. +# It is not a secret and can be shared publicly. +app_id: 34bbf488-9870-4717-b6c7-408485fcf264 + +# auto_update controls if Shorebird should automatically update in the background on launch. +# If auto_update: false, you will need to use package:shorebird_code_push to trigger updates. +# https://pub.dev/packages/shorebird_code_push +# Uncomment the following line to disable automatic updates. +# auto_update: false diff --git a/public/privacy.php b/public/privacy.php new file mode 100644 index 0000000..10162d2 --- /dev/null +++ b/public/privacy.php @@ -0,0 +1,121 @@ + + + + + + سياسة الخصوصية — مُصادَق + + + + + + + + + + + +
+

سياسة الخصوصية

+
آخر تحديث: 8 مايو 2026
+ +

نحن في مُصادَق ("نحن"، "المنصة"، أو "التطبيق") نولي اهتماماً بالغاً بخصوصية بياناتك المالية والشخصية. توضح سياسة الخصوصية هذه كيفية جمعنا، واستخدامنا، وحمايتنا لمعلوماتك عند استخدامك لخدمات منصة مُصادَق للفوترة الإلكترونية وتطبيق الهواتف الذكية التابع لها.

+ +

1. المعلومات التي نجمعها

+

عند استخدامك لخدماتنا، قد نقوم بجمع الأنواع التالية من المعلومات:

+
    +
  • معلومات الحساب: الاسم، البريد الإلكتروني، رقم الهاتف، ومعلومات الشركة (الرقم الضريبي، السجل التجاري).
  • +
  • البيانات المالية والفواتير: صور الفواتير التي تقوم برفعها أو التقاطها، البيانات المستخرجة منها عبر تقنيات الذكاء الاصطناعي، وبيانات الأطراف المتعاملة معك.
  • +
  • معلومات الجهاز والاستخدام: نوع الجهاز، نظام التشغيل، عنوان الـ IP، وسجلات (Logs) تفاعلك مع المنصة للمساعدة في تحسين الخدمة واكتشاف الأخطاء.
  • +
+ +

2. كيف نستخدم معلوماتك

+

نستخدم البيانات التي نجمعها للأغراض التالية:

+
    +
  • تقديم خدمات الفوترة الإلكترونية الأساسية ومعالجة الفواتير باستخدام الذكاء الاصطناعي.
  • +
  • ربط الفواتير وإرسالها إلى منظومة جوفوترا (JoFotara) الخاصة بدائرة ضريبة الدخل والمبيعات الأردنية بناءً على طلبك.
  • +
  • تحسين دقة نماذج الذكاء الاصطناعي في استخراج البيانات (دون ربطها بهويتك الشخصية).
  • +
  • إدارة حسابك وتوفير الدعم الفني.
  • +
  • إرسال التحديثات والتنبيهات الأمنية.
  • +
+ +

3. حماية بياناتك وأمنها

+

تُعتبر حماية بياناتك من أهم أولوياتنا. ولضمان ذلك نطبق معايير أمنية صارمة منها:

+
    +
  • تشفير قواعد البيانات والمستندات الحساسة باستخدام خوارزميات التشفير المتقدمة (مثل AES-256-GCM).
  • +
  • استخدام تقنيات العزل (Multi-Tenancy) لضمان عدم تداخل بيانات الشركات.
  • +
  • تطبيق تقنيات الحماية النشطة (RASP) عبر أداة freeRASP في تطبيق الهواتف الذكية لمنع الاختراق، التعديل على الكود، أو العمليات الخبيثة.
  • +
  • حماية الوصول للتطبيق عبر المصادقة الحيوية (البصمة/الوجه).
  • +
+ +

4. مشاركة البيانات مع أطراف ثالثة

+

نحن لا نبيع أو نؤجر بياناتك لأي جهات خارجية. قد نشارك معلوماتك فقط في الحالات التالية:

+
    +
  • دائرة ضريبة الدخل والمبيعات (جوفوترا): تُرسل الفواتير التي تقوم باعتمادها حصرياً إلى المنظومة الوطنية وفق المتطلبات القانونية.
  • +
  • مقدمي الخدمات السحابية: نستخدم خدمات آمنة (مثل Google Cloud أو منصات الـ AI المعتمدة) لمعالجة واستخراج البيانات، تخضع هذه الخدمات لاتفاقيات سرية صارمة.
  • +
  • الامتثال القانوني: إذا طُلب منا ذلك بموجب مذكرة قانونية رسمية من الجهات المختصة في المملكة الأردنية الهاشمية.
  • +
+ +

5. الاحتفاظ بالبيانات وحذفها

+

نحتفظ ببياناتك طالما كان حسابك نشطاً لتقديم الخدمات المطلوبة. يحق لك طلب حذف حسابك في أي وقت. عند الحذف، سنقوم بمسح كافة الفواتير والصور المرتبطة بحسابك من خوادمنا النشطة خلال 90 يوماً، مع مراعاة البيانات التي قد يفرض القانون الاحتفاظ بها لأغراض ضريبية.

+ +

6. تحديثات سياسة الخصوصية

+

قد نقوم بتحديث هذه السياسة من وقت لآخر لمواكبة التغيرات القانونية أو التقنية. سنقوم بإعلامك بأي تغييرات جوهرية عبر البريد الإلكتروني أو من خلال إشعار داخل المنصة.

+ +

7. التواصل معنا

+

إذا كانت لديك أي أسئلة أو استفسارات حول سياسة الخصوصية الخاصة بنا، يرجى التواصل معنا عبر البريد الإلكتروني:

+

privacy@musadaq.jo

+
+ + + + + +