Update: 2026-05-08 06:19:56

This commit is contained in:
Hamza-Ayed
2026-05-08 06:19:56 +03:00
parent df92a44878
commit 753497649a
18 changed files with 1310 additions and 21 deletions

View File

@@ -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 = <String>[].obs;
final securityChecked = false.obs;
@override
void onInit() {
super.onInit();
_initSecurity();
}
/// Initialize freeRASP
Future<void> _initSecurity() async {
try {
await _initFreeRASP();
} catch (e) {
debugPrint('[Security] freeRASP init failed: $e');
}
securityChecked.value = true;
}
/// Initialize freeRASP with full threat callbacks
Future<void> _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<String, dynamic> getSecurityStatus() {
return {
'is_safe': isSafe,
'checked': securityChecked.value,
'threats': threatDetails.toList(),
'threat_count': threatDetails.length,
};
}
}

View File

@@ -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<void> loadCurrentPatch() async {
if (!isShorebirdAvailable) return;
try {
final patch = await _updater.readCurrentPatch();
currentPatchNumber.value = patch?.number ?? 0;
} catch (_) {}
}
/// Check for updates on launch
Future<void> _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<void> 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<void> 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);
}
}
}