v0.3.0 · MIT · New Architecture
Device integrity for React Native,that fails closed.
Jailbreak, root, hooking, debugger, emulator and opt-in tamper signals for React Native and Expo. No network. No telemetry. Unknown is never clean.
// npx expo install react-native-device-integrity
import { useDeviceIntegrity } from "react-native-device-integrity";
function IntegrityGate({ children }: { children: React.ReactNode }) {
const { status, result, loading } = useDeviceIntegrity();
if (loading && result == null) {
return null; // do not flash protected UI
}
if (status === "clean") return <>{children}</>;
return <BlockedScreen status={status} />;
}What it detects
Six areas.Typed signal ids.
Descriptions are generic on purpose — no matched paths or package names. Tamper checks run only when you pass the corresponding options.
Jailbreak
iOS: jailbreak_files, jailbreak_url_schemes, jailbreak_symlinks, jailbreak_writable_system.
Root & bootloader
Android: root_su_binary, root_management_apps, root_magisk_files, root_test_keys, root_dangerous_props, root_rw_system — plus bootloader_unlocked for an unlocked bootloader.
Hooking
iOS: hooking_libraries, hooking_dyld_insert. Android: hooking_frida, hooking_xposed.
Debugger
Both platforms: debugger_attached — fires whenever a debugger is attached.
Emulator
iOS: simulator. Android: emulator. Reported separately; compromising only with treatEmulatorAsCompromised or policy.
Tamper
Opt-in. iOS: tamper_binary_decrypted, tamper_team_id_mismatch. Android: tamper_signature_mismatch, tamper_untrusted_installer.
Reference
Every check, explained.Mechanism, exploit, bypass, references.
One page per signal: what the native code inspects, the exploit it surfaces, how it can be bypassed, and where to read more — OWASP MASTG, Apple and Android docs.
Result semantics
Three statuses.Unknown is never clean.
Status is resolved from the native report plus options. Treat only clean as safe. Block or degrade on both compromised and unknown.
01
clean
The run completed and no non-ignored compromising signal was present.
02
compromised
Any non-ignored signal whose category is in the compromising set — even if the run was incomplete.
03
unknown
Incomplete, timed-out, missing-module, or unsupported-platform. Unknown is never clean.
Resolution rules
Compromise evidence wins.Everything else follows.
Apply ignore first. Build the compromising category set. Then resolve in this order.
- 01
Compromise evidence wins
Any non-ignored signal in the compromising category set → compromised, even if the run was incomplete.
- 02
Emulator and report-only stay separate
Emulator/simulator signals stay in signals but do not change status unless treatEmulatorAsCompromised or policy includes emulator. The same goes for a signal the app set to report at build time (bootloader_unlocked: "report").
- 03
Incomplete → unknown
If the run did not complete and there is no compromising evidence → unknown.
- 04
Else clean
Otherwise → clean. Treat only clean as safe; block or degrade on compromised and unknown.
Configure
Two build-time options.Everything else per call.
The Expo config plugin writes these at prebuild. In a bare app, add the same entries by hand — the native code reads them from the manifest and Info.plist.
{
"expo": {
"plugins": [
[
"react-native-device-integrity",
{
"android": { "bootloaderUnlocked": "report" },
"ios": { "jailbreakUrlSchemes": true }
}
]
]
}
}<!-- android/app/src/main/AndroidManifest.xml, inside <application> -->
<meta-data
android:name="com.deviceintegrity.bootloader_unlocked"
android:value="report" />
<!-- ios/<App>/Info.plist: LSApplicationQueriesSchemes -->
<!-- cydia, sileo, zbra, filza, undecimus, activator -->android.bootloaderUnlocked
Build time. "compromised" (default): an unlocked bootloader makes status compromised, like Play Integrity's device verdict. "report": bootloader_unlocked is still returned but never compromising on its own. "off": the check doesn't run. Unknown values count as compromised. Relaxing it never hides root — the root signals are separate checks — but it weakens the backstop when root is hidden.
ios.jailbreakUrlSchemes
Build time, Expo only. Default true: the plugin adds the jailbreak URL schemes to LSApplicationQueriesSchemes, without which the URL-scheme check finds nothing. Set false if you manage Info.plist yourself; bare apps add the schemes by hand.
Runtime options
Per call to checkIntegrity / useDeviceIntegrity: ignore (signal ids moved to ignored), treatEmulatorAsCompromised, policy.compromisingCategories, timeoutMs, and the opt-in tamper checks under android / ios. E.g. ignore: __DEV__ ? ["bootloader_unlocked"] : [] for development builds only.
Testing guides
Prepare a device you own.Then test the checks.
Android compares Magisk, KernelSU, and APatch after a bootloader unlock. iOS starts from chip and iOS coverage, then palera1n on checkm8 or Dopamine on A12 and later. No jailbreak? Embed Frida Gadget in your app and test hooking detection on any stock iPhone.
How it compares
A snapshot of popular options,as of September 2026.
Check each project for current details. None replace server-side attestation for high-value actions.
Coverage
Model & ops
Snapshot as of September 2026. Check each project for current details.
Pick this library if you want a small, fully open-source, zero-network check with typed, fail-closed results that you can audit end to end.
Pick freeRASP if you need broad RASP coverage (tampering, store, screen capture, malware) and managed reporting, and can accept telemetry and its fair-usage terms.
Pick jail-monkey if you also need its extra device flags (mock location, ADB, developer settings) or old-architecture support.
None of these replace server-side attestation (App Attest / Play Integrity) for high-value actions.
Limitations & threat model
Client-side risk indicators.Not a RASP replacement.
Client-side indicators
They can be bypassed by an attacker who controls the device — Frida / objection hooking this module, rootless jailbreaks with hiding tweaks, Magisk DenyList / Zygisk / Shamiko, KernelSU hiding.
Not attestation
Never rely on them alone for security decisions. For high-assurance flows, use Apple App Attest / DeviceCheck and the Google Play Integrity API server-side.
False positives
Custom ROMs, developer devices with test-keys builds, developer phones with an unlocked bootloader (bootloader_unlocked, configurable per app), and enterprise MDM environments can trip signals.
Check categories informed by OWASP MASVS/MASTG, IOSSecuritySuite (BSD-2-Clause ≤1.9.11; no source copied), and RootBeer (Apache-2.0; independent implementation).
Install the library.Audit the signals.
npx expo install react-native-device-integrity · New Architecture required · Expo Go not supported