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

@noobdigital/react-native-shieldscan

v1.1.1

Published

Runtime security checks for React Native: jailbreak/root detection, Frida, debugger, emulator detection, and screen security. Works in both Old and New Architecture using classic NativeModules.

Downloads

680

Readme

@noobdigital/react-native-shieldscan

npm version npm downloads license platform architecture

Runtime security detection and screen security for React Native apps. Detects jailbreak, root, Frida instrumentation, debugger attachment, emulator environments, runtime hooking frameworks, and developer mode — plus background blur, screenshot/recording prevention, and screen recording detection — in a single native module.

Supports Old Architecture (Bridge) and New Architecture (Turbo Modules / JSI). React Native 0.70+.


Features

Runtime Security Detection

| Check | iOS | Android | |---|---|---| | Jailbreak / Root | ✅ File paths + sandbox write test + symlink check | ✅ RootBeer 0.1.2 | | File-based root | ✅ Cydia, MobileSubstrate, bash, sshd | ✅ Magisk, SuperSU, Xposed, Zygisk paths | | Frida detection | ✅ dylib injection + port 27042 + env var | ✅ File paths + port 27042 + /proc/self/maps | | Debugger attached | ✅ kinfo_proc / P_TRACED via sysctl | ✅ Debug.isDebuggerConnected() + waitingForDebugger() + TracerPid | | Emulator / Simulator | ✅ targetEnvironment(simulator) | ✅ Build fingerprint + sensor count heuristic (covers Bluestacks, Nox, LDPlayer, Genymotion, MEmu) | | Hooking frameworks | ✅ dyld image scan (Substrate, Substitute, LibHooker) | ✅ Package scan + stack trace probe + /proc/self/maps (Xposed, LSPosed, Frida gadget, SandHook) | | Developer mode | ✅ Always false (no public iOS API) | ✅ Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED |

Screen Security

| Feature | iOS | Android | |---|---|---| | Background blur | ✅ Overlay shown on willResignActive, removed on didBecomeActive | ✅ Overlay shown on window focus loss (app switcher / Home), removed on regain | | Screenshot / recording prevention | ✅ Secure UITextField layer trick — blank in captures, visible on-device | ✅ WindowManager.LayoutParams.FLAG_SECURE — blank in captures and in the recents card | | Screen recording detection | ✅ UIScreen.main.isCaptured, any iOS version | ⚠️ Android 15 (API 35)+ only, via WindowManager.addScreenRecordingCallback. Resolves false on older OS versions — there is no public API pre-15 |

Platform trade-off to know before you ship: on Android, enabling Screenshot Prevention makes the recents/app-switcher card render fully blank — your Background Blur message cannot appear at the same time, because FLAG_SECURE blocks the OS from rendering any content (including your own overlay) into that snapshot. This is an Android platform restriction, not a bug. iOS has no equivalent conflict — both features work independently there. See Security Notes below.


Screenshots

Example app running on Android emulator and iOS Simulator. Running on Emulator: TRUE confirms emulator detection is working correctly on both platforms.


Installation

npm install @noobdigital/react-native-shieldscan
# or
yarn add @noobdigital/react-native-shieldscan

iOS

cd ios && pod install

Auto-linked via React Native 0.60+ auto-linking. No manual steps required.

Android

Auto-linked via React Native 0.60+ auto-linking. No manual steps required.

For older setups, register manually in MainApplication.kt:

import com.shieldscan.ShieldScanPackage

// inside getPackages():
packages.add(ShieldScanPackage())

Permissions: the library's own manifest declares android.permission.DETECT_SCREEN_RECORDING (a normal, install-time permission — not a runtime prompt, not on Google Play's sensitive-permissions list). Gradle's manifest merger folds this into your app automatically; no action needed in your app's AndroidManifest.xml. This permission only enables isScreenBeingRecorded() on Android 15+ and has no effect on distribution, review, or user-facing prompts.


Usage

Full result object

import { runSecurityChecks } from '@noobdigital/react-native-shieldscan';

const result = await runSecurityChecks();

console.log(result);
// {
//   rooted:        false,
//   fileBasedRoot: false,
//   fridaDetected: false,
//   debugger:      false,
//   emulator:      false,
//   hooksDetected: false,
//   developerMode: false,
// }

Single boolean gate

import { isDeviceCompromised } from '@noobdigital/react-native-shieldscan';

const compromised = await isDeviceCompromised();

if (compromised) {
  Alert.alert('Security Error', 'This app cannot run on a compromised device.');
}

Enterprise — risk scoring engine

import { getDeviceRiskAssessment } from '@noobdigital/react-native-shieldscan';

const assessment = await getDeviceRiskAssessment();

console.log(assessment);
// {
//   compromised:    false,
//   threatLevel:    'CLEAN',   // CLEAN | LOW | MEDIUM | HIGH | CRITICAL
//   score:          0,         // 0–100 weighted risk score
//   signals:        [],        // which signals fired
//   recommendation: 'Device is clean. No action required.'
// }

Tiered response based on threat level:

switch (assessment.threatLevel) {
  case 'CLEAN':
    // proceed normally
    break;
  case 'LOW':
    // log only — e.g. developer mode on, expected in dev/QA
    break;
  case 'MEDIUM':
    // restrict sensitive features (payments, PII)
    break;
  case 'HIGH':
    // block sensitive flows, require re-authentication
    break;
  case 'CRITICAL':
    // Frida/hooks detected — terminate session immediately
    await revokeSessionToken();
    BackHandler.exitApp();
    break;
}

Recommended — startup enforcement with telemetry

import { getDeviceRiskAssessment } from '@noobdigital/react-native-shieldscan';

async function enforceDeviceSecurity() {
  const assessment = await getDeviceRiskAssessment();

  // Always log everything to your security backend
  analytics.track('device_security_assessment', {
    score:       assessment.score,
    threatLevel: assessment.threatLevel,
    signals:     assessment.signals,
    platform:    Platform.OS,
  });

  if (assessment.threatLevel === 'CRITICAL') {
    throw new Error('COMPROMISED_DEVICE');
  }
}

Screen Security — background blur

Shows a cover screen with a protective message whenever the app is backgrounded or the app switcher is opened, hiding sensitive content from the recents thumbnail.

import { setBlurEnabled } from '@noobdigital/react-native-shieldscan';

// Enable on a sensitive screen
await setBlurEnabled(true);

// Disable when leaving that screen
await setBlurEnabled(false);

Enabling this just arms the feature — the overlay itself only appears once the app actually loses focus (Home pressed, app switcher opened, etc.), not immediately on the call.

Screen Security — screenshot & recording prevention

Blocks the content of the current screen from appearing in screenshots and screen recordings.

import { setScreenshotPreventionEnabled } from '@noobdigital/react-native-shieldscan';

// Enable on a payment / card details screen
await setScreenshotPreventionEnabled(true);

// Disable when leaving that screen
await setScreenshotPreventionEnabled(false);

On Android this also blanks the recents/app-switcher card — see the platform trade-off note above if you're also using Background Blur.

Screen Security — detecting an active screen recording

import { isScreenBeingRecorded } from '@noobdigital/react-native-shieldscan';

const { isRecording } = await isScreenBeingRecorded();

if (isRecording) {
  // e.g. warn the user, or hide a sensitive value in JS as an extra layer
}

Poll-on-demand only — call it when you need the current state (e.g. before revealing a sensitive value), rather than in a tight loop. On Android this requires API 35+ and resolves false on older versions, since there is no public detection API pre-Android 15. On iOS it works via UIScreen.main.isCaptured on any supported iOS version.


API Reference

runSecurityChecks(): Promise<SecurityScanResult>

Runs all security checks natively in a single call. Resolves with a SecurityScanResult object. All checks run in parallel on the native side.

isDeviceCompromised(): Promise<boolean>

Convenience wrapper. Returns true if the risk score is ≥ 30 — meaning any of rooted, fileBasedRoot, fridaDetected, debugger, or hooksDetected is true.

Note: emulator and developerMode alone never mark a device as compromised. emulator is excluded entirely from scoring. developerMode contributes only 5 points — below the 30-point threshold. Check them separately if your threat model requires blocking them.

getDeviceRiskAssessment(): Promise<CompromisedResult>

Enterprise-grade weighted risk assessment. Returns a CompromisedResult with threat level, score, active signals, and a recommended action.

Signal weights:

| Signal | Weight | Rationale | |---|---|---| | fridaDetected | 40 pts | Active runtime instrumentation | | hooksDetected | 40 pts | Active runtime instrumentation | | rooted | 30 pts | OS integrity broken | | fileBasedRoot | 20 pts | Artifacts present, not confirmed active | | debugger | 10 pts | Suspicious in production | | developerMode | 5 pts | Elevated attack surface | | emulator | 0 pts | Informational only |

setBlurEnabled(enabled: boolean): Promise<boolean>

Arms or disarms the background blur cover screen. Resolves with the value passed in. See usage example above.

setScreenshotPreventionEnabled(enabled: boolean): Promise<boolean>

Enables or disables screenshot/recording blocking for the current screen. Resolves with the value passed in. See usage example above.

Android threading note: both of these are safe to call from any JS context/thread on the RN side — the native module internally dispatches all view/window mutations to the UI thread. You do not need to guard calls yourself.

isScreenBeingRecorded(): Promise<{ isRecording: boolean }>

Returns whether the screen is currently being captured by a recording/casting session at the moment of the call. See usage example above.

SecurityScanResult

interface SecurityScanResult {
  /**
   * True if RootBeer (Android) or jailbreak file paths (iOS) detect
   * a compromised OS environment. Primary root/jailbreak signal.
   */
  rooted: boolean;

  /**
   * True if known root, jailbreak, or Frida file paths exist on disk.
   * Android: /sbin/su, /magisk, XposedBridge.jar, Zygisk modules
   * iOS: /Applications/Cydia.app, /bin/bash, /usr/sbin/sshd
   */
  fileBasedRoot: boolean;

  /**
   * True if Frida instrumentation framework is detected.
   * Checks: known file paths + TCP port 27042 + environment variables
   * + dylib injection (iOS) + /proc/self/maps scan (Android).
   */
  fridaDetected: boolean;

  /**
   * True if a debugger is currently attached to the process.
   * iOS: sysctl kinfo_proc P_TRACED flag.
   * Android: Debug.isDebuggerConnected() + waitingForDebugger() + TracerPid.
   */
  debugger: boolean;

  /**
   * True if running on an Android emulator or iOS Simulator.
   * iOS: compile-time targetEnvironment(simulator).
   * Android: Build fingerprint heuristics + sensor count (< 5 sensors).
   * Covers: AOSP, Genymotion, Bluestacks, Nox, LDPlayer, MEmu, Andy, Droid4X.
   */
  emulator: boolean;

  /**
   * True if a runtime hooking framework is detected.
   * iOS: dyld image scan for Substrate, Substitute, LibHooker, TweakInject.
   * Android: package scan (Xposed/LSPosed managers) + stack trace probe
   *          + /proc/self/maps scan for XposedBridge, frida-gadget, SandHook.
   */
  hooksDetected: boolean;

  /**
   * True if developer options/mode is enabled on the device.
   * Android: Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED.
   * iOS: Always false — no public API exposes developer mode on iOS.
   */
  developerMode: boolean;
}

CompromisedResult

interface CompromisedResult {
  /** True if risk score >= 30 */
  compromised: boolean;
  /** CLEAN | LOW | MEDIUM | HIGH | CRITICAL */
  threatLevel: ThreatLevel;
  /** Weighted risk score 0–100 */
  score: number;
  /** Which signals fired */
  signals: string[];
  /** Recommended action for the consuming app */
  recommendation: string;
}

Example App

A working example app is included in the repository under example/SampleApp/.

It demonstrates all seven security checks with a live result screen including risk score card, threat level indicator, and per-check severity badges — plus a Screen Security panel to toggle background blur and screenshot prevention live, and a screen recording status indicator.

example/
  SampleApp/
    src/
      SampleAppScreen.tsx   ← main demo screen
      assets/
        noobdigital-logo.png

Running the example

# Clone the repo
git clone https://github.com/NoobDigital/react-native-shieldscan.git
cd react-native-shieldscan

# Install dependencies
yarn install

# iOS
cd example/SampleApp && yarn install
cd ios && pod install && cd ..
yarn ios

# Android
cd example/SampleApp && yarn install
yarn android

Architecture

Old Architecture (newArchEnabled=false)

Module resolved via NativeModules.ShieldScan through the standard React Native bridge.

New Architecture (newArchEnabled=true, React Native 0.71+)

Module resolved via TurboModuleRegistry.get('ShieldScan') through JSI. The TypeScript spec in src/NativeShieldScanSpec.ts drives codegen for type-safe native bindings with zero bridge serialisation overhead.

The package detects which architecture is active at runtime and selects the appropriate resolution path automatically.

Android TurboModule threading: under the New Architecture, @ReactMethod calls run on the native-modules thread, not the UI thread. setBlurEnabled and setScreenshotPreventionEnabled account for this internally (via runOnUiThread) so they're safe to call regardless of architecture — this is handled for you, not something consumers need to work around.


Security Notes

False positive guarantee hooksDetected returns false on BrowserStack, LambdaTest, Firebase Test Lab, AWS Device Farm real devices, and any device with ADB enabled, developer options on, or corporate/MDM certificates installed. Only genuine hooking framework artifacts trigger this signal.

developerMode in production developerMode: true alone does not mark a device as compromised — it contributes only 5 points to the risk score, well below the 30-point threshold. It is an informational signal. Guard hard blocks with !__DEV__ to avoid blocking your own development workflow.

Simulator / emulator emulator: true never contributes to the risk score. It is excluded from isDeviceCompromised(). Many teams run QA on emulators — this signal is informational only unless you explicitly require blocking it.

Debugger flag in development debugger: true is expected during Xcode and Android Studio debug sessions. Guard hard blocks with !__DEV__.

Frida port check latency The TCP socket probe to 127.0.0.1:27042 adds approximately 50–300ms on a clean device (connection refused with explicit timeout). This is acceptable for a one-time startup check. Avoid calling runSecurityChecks() in render loops or hot paths.

RootBeer version Android root detection uses RootBeer 0.1.2, which includes 16 KB ELF page size alignment required for Android 15+ / Google Play compliance from November 2025.

Simulator guards on iOS Jailbreak and hook detection checks are disabled at compile time on the iOS Simulator via #if targetEnvironment(simulator). This prevents false positives from macOS filesystem paths (e.g. /bin/bash) that exist on the simulator host but are not jailbreak indicators.

Android: Screenshot Prevention blanks Background Blur in recents FLAG_SECURE operates at the OS compositor level — it instructs Android to never render the window's content into any snapshot (recents card, screenshots, screen recordings, casting), regardless of what views are in the hierarchy. If both setBlurEnabled(true) and setScreenshotPreventionEnabled(true) are active, the recents card will show a blank/white card, not your blur message — the OS discards the entire frame before any snapshot consumer sees it. This is expected, by-design Android behavior, not a bug, and there is no workaround at the app level. If both your message and hard capture-blocking matter, consider gating the UI so users understand only one visual outcome is possible on Android at a time; iOS has no equivalent conflict since its two features use independent mechanisms.

Android: screen recording detection requires API 35+ isScreenBeingRecorded() uses WindowManager.addScreenRecordingCallback, introduced in Android 15 (API 35), and requires the DETECT_SCREEN_RECORDING manifest permission (declared automatically by this library). On devices below API 35, it resolves false — there is no public, reliable screen-recording-detection API on earlier Android versions. It also only detects MediaProjection-based recorders (Android's built-in recorder and most third-party apps); tools like scrcpy or the low-level screenrecord binary are not identified as recording sessions by this Android API.

Android: manifest permission is a library-level declaration DETECT_SCREEN_RECORDING is a normal, install-time permission — it does not trigger a Google Play sensitive-permissions declaration form, extended review, or a runtime user prompt. It's merged into consuming apps automatically via Gradle's manifest merger; no action is required in your app's manifest or Play Console listing.


VAPT Compliance

Developed and validated against OWASP Mobile Top 10:

| VAPT Finding | OWASP Reference | ShieldScan Signal | |---|---|---| | App does not detect jailbroken/rooted devices | M8, M9 | rooted, fileBasedRoot | | Frida can attach and instrument the app at runtime | M10 | fridaDetected, hooksDetected | | No debugger detection mechanism | M8 | debugger | | Hooking frameworks (Xposed, Substrate) not detected | M10 | hooksDetected | | App runs on emulator without restriction | M8 | emulator | | Developer options not detected | M8 | developerMode | | Sensitive content exposed in app switcher / recents | M9 | setBlurEnabled | | Sensitive content captured via screenshot or screen recording | M9 | setScreenshotPreventionEnabled, isScreenBeingRecorded |


Contributing

Pull requests are welcome.

Before submitting:

yarn lint        # must pass
yarn typecheck   # must pass
yarn test        # must pass

New detection checks must include:

  • Native implementation for both iOS (Swift) and Android (Kotlin)
  • Corresponding field in SecurityScanResult TypeScript interface
  • Unit tests in __tests__/
  • Documentation update in this README

License

MIT © noobdigital