NO NETWORK · NO TELEMETRY · FAILS CLOSED · v0.3.0

A07

Bootloader setting: compromised, report, offValidated

The build-time bootloaderUnlocked setting changes only how bootloader_unlocked counts. Root keeps the device compromised whatever it's set to.

Validatedandroid

How to read this page

A scenario is one test case: prepare the environment, run the steps, then compare the expected result (written before the run) with the observed result (copied from the run, with screenshots).

A07 · A
Run on a real phone that's been attacked or modified — rooted, jailbroken, hooked or hidden.
Validated
Run, and the result matched what was expected.

Why it matters

Relaxing bootloader_unlocked for developer phones must not hide root. This proves the setting and the root checks are independent.

Environment

App
Example app, library 0.3.0, built once per setting through the Expo config plugin
Device
OnePlus Nord 3 (CPH2493), Android 15, bootloader unlocked (orange)
Root
Magisk 31.0, no hiding

Setup

Prepare the environment.Then run the steps.

  1. 01Set android.bootloaderUnlocked in the plugin options (none for the default, then report, then off), run expo prebuild and install each build.
  2. 02For the root-ignored cases, pass ignore: ['root_su_binary', 'root_management_apps', 'root_magisk_files'] so bootloader_unlocked is the only evidence left.

Steps

What to run.What to read.

  1. 01Default, nothing ignored (see A01).
  2. 02Default, root signals ignored.
  3. 03Default, ignore: ['bootloader_unlocked'].
  4. 04report, nothing ignored.
  5. 05report, root signals ignored.
  6. 06off, nothing ignored.

Expected result · written before the run

No single pass/fail status.4 signals.

compromised whenever root is active. With root ignored: compromised on the default (bootloader_unlocked counts), clean on report (it's listed but not counted). off removes the signal entirely.

Observed result · copied from the run

No single pass/fail status.Observed 2026-10-06.

Every result matched. The only setup that reads clean is report with the root signals ignored, which is the point of report: an unlocked phone without root evidence. The built manifest carried report / off / no entry as set.

evidence
Default: compromised, 4 signals (A01, 235 ms) · default, root ignored: compromised, Signals (1) bootloader_unlocked, Ignored 3 (298 ms) · default + ignore bootloader_unlocked: compromised, Signals (3) root, Ignored 1 (245 ms) · report: compromised, Signals (4) (232 ms) · report, root ignored: clean, Signals (1) bootloader_unlocked, Ignored 3 (251 ms) · off: compromised, Signals (3) root, no bootloader_unlocked (270 ms)
Default build, the three root signals ignored at runtime: status COMPROMISED, one signal, bootloader_unlocked; Ignored (3) below.
Default · root ignored → compromised from bootloader_unlocked alone
Default build with ignore: ['bootloader_unlocked']: status COMPROMISED, three root signals; bootloader_unlocked moved to Ignored.
Default + ignore bootloader_unlocked → compromised from root
Build with bootloaderUnlocked: report, nothing ignored: status COMPROMISED, four signals including bootloader_unlocked.
report · nothing ignored → compromised from root
Build with bootloaderUnlocked: report, root signals ignored: status CLEAN, bootloader_unlocked still listed; Ignored (3) below.
report · root ignored → clean, bootloader_unlocked listed but not counted
Build with bootloaderUnlocked: off: status COMPROMISED, three root signals, no bootloader_unlocked.
off → not checked; compromised from root

Limitations

  • Root was active and visible in every run. With root hidden (A02) and the setting on report or off, a rooted phone whose Magisk app is also renamed would read clean — keep the default in production.

  • Emulators: on the Google Play image (S02) the boot properties are empty, so bootloader_unlocked can't fire; on a development image reporting orange (S11) the emulator skip keeps it out — both verified on 0.3.0.

Checks this scenario covers

Every check this test case touches — including ones that aren't expected to fire here. The expected and observed results above say which ones should and did. Each links to how the check works.