npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-rootbeer-checkroot

v0.1.2

Published

RootBeer

Readme

react-native-rootbeer-checkroot

A React Native New Architecture (TurboModule) wrapper around the battle-tested Android root detection library com.scottyab:rootbeer-lib. It exposes 12 granular detection methods over JSI (JavaScript Interface) — no bridge overhead, no threading surprises.

⚠️ Android Only. This library is strictly an Android module. Its TurboModule is registered with TurboModuleRegistry.getEnforcing, which means calling any method on a non-Android platform (iOS, web) will throw a runtime error. All calls must be guarded with Platform.OS === 'android'.


Table of Contents


How It Works

react-native-rootbeer-checkroot is built as a TurboModule using React Native's New Architecture (Codegen). Instead of going through the old asynchronous JS bridge, every method call crosses the native boundary via JSI — synchronously and with zero serialization overhead.

The library ships its own android/build.gradle which declares com.scottyab:rootbeer-lib:0.1.2 as a Gradle implementation dependency. When any consumer app installs this package, Gradle resolves and downloads the RootBeer SDK automatically as a transitive dependency. You never need to touch your app's android/ folder.


Requirements

| Requirement | Version | |---|---| | React Native | ≥ 0.73 (New Architecture required) | | Android minSdkVersion | 24 (Android 7.0 Nougat) | | Android compileSdkVersion | 36 | | Java source/target compatibility | 17 | | Kotlin | 2.0.21 (bundled in the library) | | react-native-rootbeer-checkroot | 0.1.2 |

New Architecture is mandatory. This library is a TurboModule — it does not have an Old Architecture (bridge) fallback. Ensure newArchEnabled=true is set in your project's android/gradle.properties.


Installation

Bare React Native

Step 1 — Install the package

# npm
npm install react-native-rootbeer-checkroot

# yarn
yarn add react-native-rootbeer-checkroot

Step 2 — Enable New Architecture

Open android/gradle.properties and confirm the following line is present and not commented out:

# android/gradle.properties
newArchEnabled=true

Step 3 — Rebuild the native layer

cd android && ./gradlew clean
cd ..
npx react-native run-android

Gradle will automatically resolve com.scottyab:rootbeer-lib:0.1.2 from Maven Central during this build. No manual build.gradle edits are required.


Expo (Managed & Bare Workflow)

Because this library contains native Android code, it cannot run in the Expo Go client. You must use a development build.

Step 1 — Install the package

npx expo install react-native-rootbeer-checkroot

Step 2 — Enable New Architecture in your Expo config

In app.json or app.config.js, ensure the new architecture flag is set:

{
  "expo": {
    "android": {
      "newArchEnabled": true
    }
  }
}

Step 3 — Run prebuild

npx expo prebuild --platform android

Why this is safe with Expo: The library declares its own Gradle dependency inside its own android/build.gradle. When expo prebuild (re)generates the native android/ folder, it does not wipe library-level Gradle files — only the app-level files are regenerated. Your native configuration is therefore upgrade-safe and survives every future expo prebuild run.

Step 4 — Build a development client or production build

# Development build (EAS)
eas build --profile development --platform android

# Or run locally
npx expo run:android

Native Android Configuration

None required.

This is the primary design goal of react-native-rootbeer-checkroot. The com.scottyab:rootbeer-lib:0.1.2 dependency is declared inside the library's own android/build.gradle:

// This lives inside the library — you do not write this yourself.
dependencies {
  implementation "com.facebook.react:react-android"
  implementation "com.scottyab:rootbeer-lib:0.1.2"
}

Gradle's dependency resolution propagates this to your app's build graph automatically.

No AndroidManifest.xml permissions are needed. The library's own manifest is empty — root detection is performed entirely through file-system introspection, system property reads, and process execution, none of which require declared permissions on Android.


Usage Guide

Quick Start — Single Root Check

The simplest possible integration. Use RootBeer.isRooted() to run all available checks in one call.

import React, { useEffect, useState } from 'react';
import { Platform, Text, View, StyleSheet } from 'react-native';
import { RootBeer } from 'react-native-rootbeer-checkroot';

export default function App() {
  const [isRooted, setIsRooted] = useState<boolean | null>(null);

  useEffect(() => {
    // Always guard with Platform.OS — this library is Android-only.
    if (Platform.OS === 'android') {
      const result = RootBeer.isRooted();
      setIsRooted(result);
    }
  }, []);

  if (Platform.OS !== 'android') {
    return (
      <View style={styles.container}>
        <Text>Root detection is only available on Android.</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      {isRooted === null ? (
        <Text>Checking device integrity...</Text>
      ) : (
        <Text style={isRooted ? styles.danger : styles.safe}>
          {isRooted ? '⚠️ Device is rooted' : '✅ Device is not rooted'}
        </Text>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  danger: { color: '#e53e3e', fontSize: 18, fontWeight: 'bold' },
  safe: { color: '#38a169', fontSize: 18, fontWeight: 'bold' },
});

Note: All RootBeer methods are synchronous. They return boolean directly — no await, no .then().


Granular Detection Scan

For security-sensitive applications, run each check individually to understand why a device is flagged and log the results for your backend.

import { Platform } from 'react-native';
import { RootBeer } from 'react-native-rootbeer-checkroot';

interface RootScanResult {
  isRooted: boolean;
  isRootedWithoutBusyBoxCheck: boolean;
  hasRootManagementApps: boolean;
  hasDangerousApps: boolean;
  hasTestKeys: boolean;
  hasSuBinary: boolean;
  hasMagiskBinary: boolean;
  suExecutes: boolean;
  hasDangerousProps: boolean;
  hasRWPaths: boolean;
  hasRootCloakingApps: boolean;
  hasBusyBox: boolean;
}

/**
 * Runs every available RootBeer check and returns a structured result.
 * Call this on Android only.
 */
export function performFullRootScan(): RootScanResult {
  if (Platform.OS !== 'android') {
    throw new Error('performFullRootScan() is only supported on Android.');
  }

  return {
    // High-level composite checks
    isRooted: RootBeer.isRooted(),
    isRootedWithoutBusyBoxCheck: RootBeer.isRootedWithoutBusyBoxCheck(),

    // Installed app checks
    hasRootManagementApps: RootBeer.detectRootManagementApps(),
    hasDangerousApps: RootBeer.detectPotentiallyDangerousApps(),
    hasRootCloakingApps: RootBeer.detectRootCloakingApps(),

    // Build / ROM checks
    hasTestKeys: RootBeer.detectTestKeys(),

    // Binary presence checks (static — does not execute anything)
    hasSuBinary: RootBeer.checkForSuBinary(),
    hasMagiskBinary: RootBeer.checkForMagiskBinary(),
    hasBusyBox: RootBeer.checkForBinary('busybox'),

    // Runtime probe (actually attempts to run `su`)
    suExecutes: RootBeer.checkSuExists(),

    // System property & filesystem checks
    hasDangerousProps: RootBeer.checkForDangerousProps(),
    hasRWPaths: RootBeer.checkForRWPaths(),
  };
}

// --- Usage ---
if (Platform.OS === 'android') {
  const scan = performFullRootScan();

  if (scan.isRooted) {
    console.warn('Root indicators detected:', scan);
    // Send `scan` to your analytics / security backend for forensics
  }
}

Reusable React Hook

Encapsulate all detection logic in a custom hook for clean, idiomatic React consumption across your app.

// hooks/useRootDetection.ts
import { useEffect, useState } from 'react';
import { Platform } from 'react-native';
import { RootBeer } from 'react-native-rootbeer-checkroot';

interface RootDetectionState {
  /** True if any root indicator was found. */
  isRooted: boolean;
  /** True while the detection is running (first render only). */
  isChecking: boolean;
  /** True if the check completed without errors. */
  isReady: boolean;
  /** Populated if an unexpected error occurred during detection. */
  error: Error | null;
}

/**
 * Runs `RootBeer.isRooted()` once on mount.
 * On non-Android platforms, `isRooted` is always `false` and `isReady`
 * is `false` to prevent false security guarantees.
 */
export function useRootDetection(): RootDetectionState {
  const [state, setState] = useState<RootDetectionState>({
    isRooted: false,
    isChecking: Platform.OS === 'android',
    isReady: false,
    error: null,
  });

  useEffect(() => {
    if (Platform.OS !== 'android') {
      return;
    }

    try {
      const isRooted = RootBeer.isRooted();
      setState({ isRooted, isChecking: false, isReady: true, error: null });
    } catch (err) {
      setState({
        isRooted: false,
        isChecking: false,
        isReady: false,
        error: err instanceof Error ? err : new Error(String(err)),
      });
    }
  }, []);

  return state;
}
// components/SecureScreen.tsx
import React from 'react';
import { ActivityIndicator, Text, View } from 'react-native';
import { useRootDetection } from '../hooks/useRootDetection';

export function SecureScreen() {
  const { isRooted, isChecking, error } = useRootDetection();

  if (isChecking) {
    return <ActivityIndicator size="large" />;
  }

  if (error) {
    // Treat detection failure conservatively — block access
    return <Text>Security check failed. Access denied.</Text>;
  }

  if (isRooted) {
    return (
      <View>
        <Text>⚠️ This app cannot run on a rooted device.</Text>
      </View>
    );
  }

  return <Text>✅ Device integrity verified. Welcome!</Text>;
}

Navigation Guard Pattern

Block access to sensitive screens at the navigation level. This example uses React Navigation, but the pattern applies to any router.

// navigation/RootGuard.tsx
import React, { useEffect } from 'react';
import { Platform, Alert } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { RootBeer } from 'react-native-rootbeer-checkroot';

interface RootGuardProps {
  children: React.ReactNode;
  /** Screen name to redirect to when root is detected. */
  fallbackScreen?: string;
}

/**
 * Wraps a screen and redirects away if the device is rooted.
 * Must be rendered inside a React Navigation navigator.
 */
export function RootGuard({ children, fallbackScreen = 'Home' }: RootGuardProps) {
  const navigation = useNavigation();

  useEffect(() => {
    if (Platform.OS !== 'android') {
      return;
    }

    const isRooted = RootBeer.isRooted();

    if (isRooted) {
      Alert.alert(
        'Security Warning',
        'This feature is not available on rooted devices.',
        [{ text: 'OK', onPress: () => navigation.navigate(fallbackScreen as never) }],
        { cancelable: false }
      );
    }
  }, [navigation, fallbackScreen]);

  return <>{children}</>;
}
// Inside your screen component
function PaymentScreen() {
  return (
    <RootGuard fallbackScreen="Home">
      {/* Sensitive payment UI renders here */}
    </RootGuard>
  );
}

API Reference

All methods are synchronous and return boolean. They are exposed on the RootBeer object (as const) imported from react-native-rootbeer-checkroot.

import { RootBeer } from 'react-native-rootbeer-checkroot';

Method Summary

| Method | Parameters | Returns | Description | |---|---|---|---| | isRooted() | — | boolean | Runs all detection checks, including BusyBox. The primary, recommended check. | | isRootedWithoutBusyBoxCheck() | — | boolean | Runs all checks except BusyBox. Use when BusyBox causes false positives. | | detectRootManagementApps() | — | boolean | Scans installed apps for known root managers (SuperSU, Magisk Manager, KingRoot, etc.). | | detectPotentiallyDangerousApps() | — | boolean | Scans installed apps for apps commonly associated with root exploitation. | | detectRootCloakingApps() | — | boolean | Scans installed apps for apps that conceal root from other apps (RootCloak, Hide My Root). | | detectTestKeys() | — | boolean | Checks android.os.Build.TAGS for "test-keys" — a signature of unofficial/custom ROMs. | | checkForSuBinary() | — | boolean | Scans a list of known filesystem paths for the su executable. Static — does not execute anything. | | checkForMagiskBinary() | — | boolean | Scans filesystem paths for the Magisk binary. Effective even when Magisk is in hide mode. | | checkForBinary(binaryName) | binaryName: string | boolean | Scans filesystem paths for any named binary. Pass "busybox", "su", or any other name. | | checkSuExists() | — | boolean | Attempts to execute su. Returns true if the call succeeds. Runtime probe — more invasive than checkForSuBinary(). | | checkForDangerousProps() | — | boolean | Reads system properties and returns true if dangerous values are set (e.g. ro.debuggable=1, ro.secure=0). | | checkForRWPaths() | — | boolean | Inspects /proc/mounts for filesystem paths that should be read-only but are mounted read-write. |


Method Detail

RootBeer.isRooted(): boolean

The top-level composite check. Internally calls every other detection method (including BusyBox) and returns true if any single indicator is found.

Use this for the vast majority of use cases. Only fall back to individual methods when you need to understand which specific indicator triggered.

const safe = !RootBeer.isRooted();

RootBeer.isRootedWithoutBusyBoxCheck(): boolean

Identical to isRooted(), but skips the BusyBox binary check. BusyBox is a legitimate developer tool that may be present on non-malicious devices (e.g. some manufacturer ROMs include it). Use this variant if you are seeing false positives from isRooted() in your production user base.

// Prefer this if you're seeing false positives from isRooted()
const safe = !RootBeer.isRootedWithoutBusyBoxCheck();

RootBeer.detectRootManagementApps(): boolean

Queries the Android PackageManager for the package names of well-known root management applications. Returns true if any are installed.

Examples of detected apps: SuperSU, Magisk Manager, KingRoot, Towelroot, BaiduEasyRoot.

if (RootBeer.detectRootManagementApps()) {
  console.warn('Root management app found on device.');
}

RootBeer.detectPotentiallyDangerousApps(): boolean

Queries the PackageManager for apps that are commonly used to exploit, abuse, or probe rooted environments — even if they are not root managers themselves.

if (RootBeer.detectPotentiallyDangerousApps()) {
  console.warn('Potentially dangerous app detected.');
}

RootBeer.detectRootCloakingApps(): boolean

Scans for root-hiding apps (e.g. RootCloak, MagiskHide-related helpers) whose presence implies a deliberate attempt to conceal root access from the system. A positive result here is a particularly strong signal of malicious intent.

if (RootBeer.detectRootCloakingApps()) {
  // High-confidence indicator — user is actively trying to hide root
  console.warn('Root cloaking app detected.');
}

RootBeer.detectTestKeys(): boolean

Reads android.os.Build.TAGS and returns true if it contains "test-keys". Production devices from manufacturers are always signed with "release-keys". Test-key builds indicate either a custom ROM or developer engineering builds — both of which typically have root access.

if (RootBeer.detectTestKeys()) {
  console.warn('Device is running a custom or test-key ROM.');
}

RootBeer.checkForSuBinary(): boolean

Scans a curated list of common binary paths (e.g. /system/bin/su, /system/xbin/su, /sbin/su, /data/local/xbin/su, etc.) for the presence of the su executable. This is a static file check — it does not execute su.

if (RootBeer.checkForSuBinary()) {
  console.warn('su binary found on filesystem.');
}

RootBeer.checkForMagiskBinary(): boolean

Scans common paths for the Magisk binary. Useful as a secondary check because Magisk's systemless root approach can evade package-manager-based detection (detectRootManagementApps) if the Magisk Manager APK is uninstalled.

if (RootBeer.checkForMagiskBinary()) {
  console.warn('Magisk binary present — device likely uses Magisk root.');
}

RootBeer.checkForBinary(binaryName: string): boolean

| Parameter | Type | Required | Description | |---|---|---|---| | binaryName | string | ✅ | The binary filename to scan for (e.g. "su", "busybox", "magisk"). |

Scans the same set of filesystem paths as checkForSuBinary() and checkForMagiskBinary(), but for an arbitrary binary name. Use this for custom checks beyond the preset methods.

// Check for busybox specifically
const hasBusyBox = RootBeer.checkForBinary('busybox');

// Check for any custom binary
const hasFridaServer = RootBeer.checkForBinary('frida-server');

RootBeer.checkSuExists(): boolean

Attempts to execute su using Runtime.exec() and returns true if the process starts successfully. This is a runtime probe — the most definitive su check, but also the most invasive. On a hardened device, this call might be logged by security software.

// Definitive but invasive — su is actually invoked
if (RootBeer.checkSuExists()) {
  console.warn('su is executable on this device.');
}

RootBeer.checkForDangerousProps(): boolean

Reads Android system properties via getprop and returns true if any of the following dangerous values are set:

| Property | Dangerous Value | |---|---| | ro.debuggable | 1 | | ro.secure | 0 | | ro.build.type | eng | | ro.build.tags | test-keys |

if (RootBeer.checkForDangerousProps()) {
  console.warn('Dangerous system properties detected.');
}

RootBeer.checkForRWPaths(): boolean

Parses /proc/mounts and checks whether any paths that should always be read-only on a stock Android device (e.g. /system, /data) are mounted with write permissions. Read-write system mounts are a strong indicator of root-level filesystem modifications.

if (RootBeer.checkForRWPaths()) {
  console.warn('System paths are mounted read-write.');
}

Detection Method Deep Dive

Different checks have different trade-offs. Use this table to choose the right combination for your threat model.

| Method | Technique | Invasiveness | False Positive Risk | Bypass Difficulty | |---|---|---|---|---| | isRooted() | All checks combined | Medium | Low–Medium | Hard | | isRootedWithoutBusyBoxCheck() | All except BusyBox | Medium | Lower | Hard | | detectRootManagementApps() | Package query | Low | Very Low | Easy (uninstall app) | | detectPotentiallyDangerousApps() | Package query | Low | Low | Easy (uninstall app) | | detectRootCloakingApps() | Package query | Low | Very Low | Very Easy (the app hides it) | | detectTestKeys() | Build property | Very Low | Low | Hard (requires ROM resign) | | checkForSuBinary() | File path scan | Very Low | Low | Medium (bind mount) | | checkForMagiskBinary() | File path scan | Very Low | Very Low | Medium (Magisk hide) | | checkForBinary(name) | File path scan | Very Low | Depends on binary | Medium | | checkSuExists() | Process execution | High | Low | Medium | | checkForDangerousProps() | getprop read | Low | Low | Medium (prop reset) | | checkForRWPaths() | /proc/mounts parse | Very Low | Very Low | Hard |

Recommended strategy for production apps:

  1. Start with isRooted() as your primary gate.
  2. If you need lower false positives, switch to isRootedWithoutBusyBoxCheck().
  3. For high-security applications (banking, DRM), run all 12 checks individually and send the full result object to your backend for server-side evaluation.

Platform Guard Reference

Because the module is registered with TurboModuleRegistry.getEnforcing, it throws on any platform where it is not registered (iOS, Android Old Architecture, web). Always guard calls:

import { Platform } from 'react-native';
import { RootBeer } from 'react-native-rootbeer-checkroot';

// ✅ Correct — guarded
if (Platform.OS === 'android') {
  const rooted = RootBeer.isRooted();
}

// ✅ Correct — early return pattern
function checkDevice(): boolean {
  if (Platform.OS !== 'android') {
    return false; // or handle as you see fit
  }
  return RootBeer.isRooted();
}

// ❌ Incorrect — will throw on iOS / web
const rooted = RootBeer.isRooted();

For TypeScript projects you can create a typed utility:

// utils/security.ts
import { Platform } from 'react-native';
import { RootBeer } from 'react-native-rootbeer-checkroot';

/**
 * Returns `null` on non-Android platforms so callers can
 * distinguish "not applicable" from "false".
 */
export function isDeviceRooted(): boolean | null {
  if (Platform.OS !== 'android') return null;
  return RootBeer.isRooted();
}

Architecture

Consumer App (TypeScript)
         │
         │  import { RootBeer } from 'react-native-rootbeer-checkroot'
         ▼
  ┌─────────────────────┐
  │   src/RootBeer.ts   │  Public API object (as const, fully typed)
  └──────────┬──────────┘
             │
             ▼
  ┌──────────────────────────┐
  │  src/NativeRootBeer.ts   │  TurboModule Spec (Codegen source of truth)
  └──────────┬───────────────┘
             │  React Native Codegen auto-generates ↓
             ▼
  ┌────────────────────────────────┐
  │  NativeRootBeerSpec.kt         │  Abstract Kotlin class (generated)
  │  (com.rootbeer package)        │
  └──────────┬─────────────────────┘
             │  extends
             ▼
  ┌────────────────────────────────┐
  │  RootBeerModule.kt             │  Concrete implementation
  │  • Registered as TurboModule   │  • lazy-initialises RootBeerLib
  │  • isTurboModule = true        │  • delegates each method to SDK
  └──────────┬─────────────────────┘
             │
             ▼
  ┌──────────────────────────────────────────┐
  │  com.scottyab:rootbeer-lib:0.1.2         │  Android SDK (Maven Central)
  │  Declared in library's build.gradle      │  Resolved as transitive dep
  └──────────────────────────────────────────┘

Key architectural decisions:

  • BaseReactPackage (not the older ReactPackage): The RootBeerPackage class extends BaseReactPackage — the correct base for New Architecture modules that expose TurboModules.
  • isTurboModule = true in ReactModuleInfo: This opts the module into the TurboModule JSI pathway, bypassing the legacy bridge entirely.
  • needsEagerInit = false: The native module is only instantiated on first use, not at app startup.
  • Lazy SDK initialization (by lazy): The com.scottyab.rootbeer.RootBeer instance is created once, on first method call, using the ReactApplicationContext. This avoids paying the initialization cost if the module is never called.
  • Codegen spec name (codegenConfig.name = "RootBeerSpec"): The generated spec class is NativeRootBeerSpec, and the Java package is com.rootbeer, controlled by codegenConfig.android.javaPackageName.

Troubleshooting

Build error: Cannot find NativeRootBeerSpec

Codegen has not run yet. This happens when you install the package and try to build without cleaning first.

cd android && ./gradlew clean
cd .. && npx react-native run-android

For Expo:

npx expo prebuild --clean --platform android
npx expo run:android

Build error: Could not resolve com.scottyab:rootbeer-lib:0.1.2

Your Gradle build cannot reach Maven Central. Check that mavenCentral() is in your root android/build.gradle repositories block:

// android/build.gradle
allprojects {
  repositories {
    google()
    mavenCentral() // ← must be present
  }
}

TurboModuleRegistry.getEnforcing error at runtime

You called a RootBeer method on a non-Android platform, or your app is running in Old Architecture mode.

  • Platform fix: Wrap all calls in if (Platform.OS === 'android') { ... }.
  • Architecture fix: Ensure newArchEnabled=true in android/gradle.properties.

isRooted() returns true on a production device (false positive)

Some manufacturer ROMs (particularly older Xiaomi, Samsung Knox variants) ship with BusyBox or test-keys. Switch to:

// Exclude BusyBox from the composite check
const isRooted = RootBeer.isRootedWithoutBusyBoxCheck();

Then run the granular scan to identify the exact triggering check and evaluate whether it represents a real risk for your threat model.


Methods return false on a known-rooted emulator

Some AVD (Android Virtual Device) emulators and certain Genymotion configurations don't expose root indicators in the paths RootBeer scans. For reliable testing:

  • Use a physical device with Magisk or SuperSU installed.
  • Or try RootBeer.detectTestKeys() — emulators are almost always signed with test keys.

License

MIT © mr:X