Add security service implementation and device safety checks
parent
a767f082c5
commit
af8df09bf7
Binary file not shown.
@ -0,0 +1,150 @@
|
||||
import 'package:doctor_app_flutter/core/service/talsec_config.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
|
||||
import 'package:doctor_app_flutter/config/config.dart' as config;
|
||||
|
||||
|
||||
/// Enum to categorize threat severity levels
|
||||
enum ThreatSeverity {
|
||||
critical, // Block app usage
|
||||
warning, // Log only, don't block
|
||||
}
|
||||
|
||||
/// Model to track threat details
|
||||
class ThreatEvent {
|
||||
final String threatType;
|
||||
final ThreatSeverity severity;
|
||||
final DateTime timestamp;
|
||||
final String? additionalInfo;
|
||||
|
||||
ThreatEvent({
|
||||
required this.threatType,
|
||||
required this.severity,
|
||||
this.additionalInfo,
|
||||
}) : timestamp = DateTime.now();
|
||||
}
|
||||
|
||||
/// Callback type for critical threat detection
|
||||
typedef OnCriticalThreatDetected = void Function();
|
||||
|
||||
/// Abstract class defining the security service interface
|
||||
abstract class SecurityService {
|
||||
/// Initialize and start the security monitoring
|
||||
Future<void> initialize();
|
||||
|
||||
/// Check if device is currently safe
|
||||
bool get isSafeDevice;
|
||||
|
||||
/// Get list of detected threats
|
||||
List<ThreatEvent> get detectedThreats;
|
||||
|
||||
/// Set callback for when critical threat is detected
|
||||
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback);
|
||||
}
|
||||
|
||||
/// Implementation of SecurityService using Talsec (freeRASP)
|
||||
class SecurityServiceImpl implements SecurityService {
|
||||
Logger logger = Logger(
|
||||
printer: PrettyPrinter(
|
||||
methodCount: 2,
|
||||
errorMethodCount: 5,
|
||||
lineLength: 1000,
|
||||
colors: true,
|
||||
printEmojis: true,
|
||||
),
|
||||
);
|
||||
|
||||
final List<ThreatEvent> _detectedThreats = [];
|
||||
bool _isInitialized = false;
|
||||
OnCriticalThreatDetected? _onCriticalThreatCallback;
|
||||
|
||||
SecurityServiceImpl();
|
||||
|
||||
@override
|
||||
bool get isSafeDevice => config.isSafeDevice;
|
||||
|
||||
@override
|
||||
List<ThreatEvent> get detectedThreats => [..._detectedThreats];
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
if (_isInitialized) {
|
||||
logger.d('SecurityService already initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.d('Initializing SecurityService with Talsec');
|
||||
|
||||
// Start the RASP engine
|
||||
await Talsec.instance.start(talsecConfig);
|
||||
|
||||
// Setup threat callbacks
|
||||
final callback = ThreatCallback(
|
||||
onAppIntegrity: () => _handleThreat('App Integrity', ThreatSeverity.critical),
|
||||
onObfuscationIssues: () => _handleThreat('Obfuscation Issues', ThreatSeverity.warning),
|
||||
onDebug: () => _handleThreat('Debug Mode', ThreatSeverity.critical),
|
||||
onDeviceBinding: () => _handleThreat('Device Binding', ThreatSeverity.critical),
|
||||
onDeviceID: () => _handleThreat('Device ID Mismatch', ThreatSeverity.critical),
|
||||
onHooks: () => _handleThreat('Hooks Detected', ThreatSeverity.critical),
|
||||
onPasscode: () => _handleThreat('Passcode Not Set', ThreatSeverity.warning),
|
||||
onPrivilegedAccess: () => _handleThreat('Privileged Access (Root/Jailbreak)', ThreatSeverity.critical),
|
||||
onSecureHardwareNotAvailable: () => _handleThreat('Secure Hardware Not Available', ThreatSeverity.warning),
|
||||
onSimulator: () => _handleThreat('Simulator/Emulator Detected', ThreatSeverity.critical),
|
||||
onSystemVPN: () => _handleThreat('System VPN Active', ThreatSeverity.warning),
|
||||
onDevMode: () => _handleThreat('Developer Mode', ThreatSeverity.warning),
|
||||
onADBEnabled: () => _handleThreat('USB Debugging Enabled', ThreatSeverity.warning),
|
||||
onUnofficialStore: () => _handleThreat('Unofficial Store Installation', ThreatSeverity.critical),
|
||||
onScreenshot: () => _handleThreat('Screenshot Detected', ThreatSeverity.warning),
|
||||
onScreenRecording: () => _handleThreat('Screen Recording Active', ThreatSeverity.warning),
|
||||
onMultiInstance: () => _handleThreat('Multiple Instances', ThreatSeverity.warning),
|
||||
onLocationSpoofing: () => _handleThreat('Location Spoofing', ThreatSeverity.warning),
|
||||
onTimeSpoofing: () => _handleThreat('Time Spoofing', ThreatSeverity.warning),
|
||||
onAutomation: () => _handleThreat('Automation Detected', ThreatSeverity.warning),
|
||||
onBootloader: () => _handleThreat('Unlocked Bootloader', ThreatSeverity.critical),
|
||||
onMalware: (suspiciousApps) => _handleThreat('Malware/Suspicious Apps', ThreatSeverity.critical, additionalInfo: suspiciousApps.toString()),
|
||||
);
|
||||
|
||||
Talsec.instance.attachListener(callback);
|
||||
|
||||
_isInitialized = true;
|
||||
logger.d('SecurityService initialized successfully');
|
||||
} catch (e) {
|
||||
logger.e('Failed to initialize SecurityService: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback) {
|
||||
_onCriticalThreatCallback = callback;
|
||||
logger.d('Critical threat callback registered');
|
||||
}
|
||||
|
||||
/// Handle detected threats with appropriate severity
|
||||
void _handleThreat(String threatType, ThreatSeverity severity, {String? additionalInfo}) {
|
||||
final threat = ThreatEvent(
|
||||
threatType: threatType,
|
||||
severity: severity,
|
||||
additionalInfo: additionalInfo,
|
||||
);
|
||||
|
||||
_detectedThreats.add(threat);
|
||||
|
||||
// Log to console
|
||||
if (severity == ThreatSeverity.critical) {
|
||||
logger.e('🔴 CRITICAL THREAT: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||
} else {
|
||||
logger.d('⚠️ WARNING: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||
}
|
||||
|
||||
// Block app if critical threat
|
||||
if (severity == ThreatSeverity.critical) {
|
||||
config.isSafeDevice = false;
|
||||
logger.e('Device marked as UNSAFE due to: $threatType');
|
||||
_onCriticalThreatCallback?.call();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
|
||||
final talsecConfig = TalsecConfig(
|
||||
androidConfig: AndroidConfig(
|
||||
packageName: 'com.hmg.hmgDr',
|
||||
signingCertHashes: ['uNLWc6Ces/bVOgSs5vxQtlHdNtDW8hoBxe4FypG697Y=', 'MP0tIs2hPii94DAsiU9Jy9oZJfsEQy0a0PrG0/qVQfo='], // Must be the release cert hash
|
||||
supportedStores: ['com.android.vending'], // Google Play Store
|
||||
),
|
||||
iosConfig: IOSConfig(
|
||||
bundleIds: ['com.hmg.hmgDr'], // iOS Bundle ID
|
||||
teamId: '3A359E86ZF', // Found in Apple Developer portal
|
||||
),
|
||||
watcherMail: '', // Required to receive security alerts
|
||||
isProd: true, // Enforces strict checks for release builds
|
||||
);
|
||||
@ -0,0 +1,117 @@
|
||||
import 'package:doctor_app_flutter/core/service/security_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:http/http.dart' as getIt;
|
||||
|
||||
import 'locator.dart';
|
||||
|
||||
class UnsafeDevice extends StatefulWidget {
|
||||
const UnsafeDevice({super.key});
|
||||
|
||||
@override
|
||||
State<UnsafeDevice> createState() => _UnsafeDeviceState();
|
||||
}
|
||||
|
||||
class _UnsafeDeviceState extends State<UnsafeDevice> {
|
||||
@override
|
||||
void initState() {
|
||||
Talsec.instance.detachListener();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Logo
|
||||
Center(
|
||||
child: Image.asset('assets/images/dr_app_logo.png'),
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
// Warning Icon
|
||||
Icon(
|
||||
Icons.security,
|
||||
size: 80,
|
||||
color: Color(0xFFED1C2B),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
// Title
|
||||
Text(
|
||||
'Unsafe Device Detected',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFFED1C2B),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
|
||||
// Description
|
||||
Text(
|
||||
'For your security, this app cannot run on devices with security vulnerabilities. is ssss',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
if (locator.get<SecurityService>().detectedThreats.isNotEmpty) ...[
|
||||
Text(
|
||||
'Detected Issues:',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Container(
|
||||
padding: EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.red.withOpacity(0.3)),
|
||||
),
|
||||
child: Column(
|
||||
children: locator
|
||||
.get<SecurityService>()
|
||||
.detectedThreats
|
||||
.where((t) => t.severity == ThreatSeverity.critical)
|
||||
.take(5) // Show max 5 threats
|
||||
.map((threat) => Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 16, color: Colors.red),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
threat.threatType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue