device_integrity_guard 1.0.0
device_integrity_guard: ^1.0.0 copied to clipboard
Reusable guard that blocks Flutter apps when the runtime environment looks unsafe.
Device Integrity Guard #
device_integrity_guard is a reusable toolkit that lets you stop a Flutter application before it boots when the runtime environment looks unsafe. It bundles common integrity detectors (emulator, root, jailbreak, debugger) and gives you a cohesive policy + UI surface to decide whether suspicious signals should block the UX, simply be logged, or be ignored in specific builds.
Highlights #
- 🔐 Configurable policy – Toggle each detector, decide if emulators/root/jailbreak/debuggers are acceptable, and bypass checks entirely in debug builds.
- 🧪 Swappable detectors – Implement the
IntegritySignalDetectorinterface to plug in custom probes (e.g., corporate MDM, binary signatures). Default detectors live undersrc/detection/. - 🧭 Actionable results –
DeviceIntegrityGuard.checkAndMaybeBlock()returns aDeviceIntegrityResultthat lists allIntegritySignals plus how long scanning took. - 🖥️ Built‑in blocking UI –
CompromisedEnvironmentScreenships with sensible English copy and can be replaced with any widget at runtime. - 🚀 Drop-in bootstrap helper –
runGuardedAppruns the guard beforerunApp, automatically routing compromised devices to your block screen. - 🧱 Safe defaults – Detector implementations no-op on unsupported platforms (desktop/web) and carefully guard expensive
Process.runcalls with timeouts.
Installation #
Add the dependency:
dependencies:
device_integrity_guard: ^1.0.0
device_info_plus and url_launcher are pulled in automatically.
Quick start #
import 'package:device_integrity_guard/device_integrity_guard.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
final guard = DeviceIntegrityGuard(
policy: DeviceIntegrityPolicy(
allowDebugMode: !kReleaseMode,
allowEmulator: !kReleaseMode,
allowRoot: !kReleaseMode,
allowJailbreak: !kReleaseMode,
blockOnCompromise: kReleaseMode,
),
onViolation: (result) {
debugPrint('Signals: ${result.blockedSignals}');
},
);
await runGuardedApp(
app: const MyApp(),
guard: guard,
);
}
When the guard reports isCompromised == true and the policy says to block, a CompromisedEnvironmentScreen is shown instead of your normal app widget. For debug/profile builds you can set allowDebugMode: true to skip the work entirely.
Customizing the block screen #
Pass any widget (or builder fed with the IntegritySummary) via runGuardedApp. If nothing is provided the stock CompromisedEnvironmentScreen with English copy is displayed.
runGuardedApp(
app: const MyApp(),
guard: guard,
blockedWidget: const MyLockedDownScreen(),
);
Or, if you want the summary data for analytics/UX tweaks, use blockedBuilder:
runGuardedApp(
app: const MyApp(),
guard: guard,
blockedBuilder: (summary) {
return CompromisedEnvironmentScreen(
summary: summary,
title: 'Gerät blockiert',
description:
'Wir haben diese Ausführung gestoppt. Bitte starte auf einem sicheren Gerät.',
ctaLabel: 'App schließen',
icon: const Icon(Icons.report_gmailerrorred),
);
},
);
IntegritySummary holds every triggered IntegritySignal, the subset that forced a block, and when the evaluation happened so you can surface rich UI or analytics.
Adding your own detectors #
Implement IntegritySignalDetector:
class CustomSignatureDetector extends IntegritySignalDetector {
const CustomSignatureDetector() : super(logger: debugPrint);
@override
String get name => 'SignatureDetector';
@override
IntegritySignalType get signalType => IntegritySignalType.custom;
@override
Future<IntegritySignal?> detect() async {
final compromised = await checkBinarySignature();
if (!compromised) return null;
return IntegritySignal(
type: IntegritySignalType.custom,
severity: IntegritySeverity.high,
detector: name,
details: 'APK signature mismatch',
);
}
}
Inject it either by replacing the detector list or appending to the defaults:
final guard = DeviceIntegrityGuard(
detectors: DeviceIntegrityGuard.defaultDetectors(logger: debugPrint),
additionalDetectors: const [CustomSignatureDetector()],
policy: const DeviceIntegrityPolicy(
enableCustomDetectors: true,
allowCustomSignals: false,
),
);
Example app #
The /example directory shows how to:
- Gate production builds while allowing engineers to iterate locally.
- Provide a fully custom blocked screen through
blockedBuilder. - Log metrics via the
onViolationcallback.
cd example
flutter run --release # blocks compromised devices
flutter run # logs signals but stays debuggable
Testing #
Run the included pipeline tests with:
flutter test
They exercise the policy combination logic via fake detectors so your CI can stay deterministic. You are encouraged to write integration tests around your own custom detectors and UI overrides.