@capawesome/capacitor-root-detection 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CapawesomeCapacitorRootDetection.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +205 -0
  5. package/android/build.gradle +60 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/RootDetection.java +70 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/RootDetectionPlugin.java +105 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/classes/results/IsDeveloperModeEnabledResult.java +22 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/classes/results/IsEmulatorResult.java +22 -0
  11. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/classes/results/IsRootedResult.java +22 -0
  12. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/interfaces/Callback.java +5 -0
  13. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/interfaces/NonEmptyResultCallback.java +7 -0
  14. package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/interfaces/Result.java +7 -0
  15. package/android/src/main/res/.gitkeep +0 -0
  16. package/dist/docs.json +162 -0
  17. package/dist/esm/definitions.d.ts +66 -0
  18. package/dist/esm/definitions.js +2 -0
  19. package/dist/esm/definitions.js.map +1 -0
  20. package/dist/esm/index.d.ts +4 -0
  21. package/dist/esm/index.js +7 -0
  22. package/dist/esm/index.js.map +1 -0
  23. package/dist/esm/web.d.ts +7 -0
  24. package/dist/esm/web.js +13 -0
  25. package/dist/esm/web.js.map +1 -0
  26. package/dist/plugin.cjs.js +27 -0
  27. package/dist/plugin.cjs.js.map +1 -0
  28. package/dist/plugin.js +30 -0
  29. package/dist/plugin.js.map +1 -0
  30. package/ios/Plugin/Classes/Results/IsEmulatorResult.swift +16 -0
  31. package/ios/Plugin/Classes/Results/IsRootedResult.swift +16 -0
  32. package/ios/Plugin/Info.plist +24 -0
  33. package/ios/Plugin/Protocols/Result.swift +5 -0
  34. package/ios/Plugin/RootDetection.swift +64 -0
  35. package/ios/Plugin/RootDetectionPlugin.swift +62 -0
  36. package/package.json +91 -0
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapawesomeCapacitorRootDetection'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '15.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Robin Genz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapawesomeCapacitorRootDetection",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapawesomeCapacitorRootDetection",
10
+ targets: ["RootDetectionPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "RootDetectionPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Plugin"),
23
+ .testTarget(
24
+ name: "RootDetectionPluginTests",
25
+ dependencies: ["RootDetectionPlugin"],
26
+ path: "ios/PluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # @capawesome/capacitor-root-detection
2
+
3
+ Capacitor plugin for detecting rooted and jailbroken devices.
4
+
5
+ <div class="capawesome-z29o10a">
6
+ <a href="https://cloud.capawesome.io/" target="_blank">
7
+ <img alt="Deliver Live Updates to your Capacitor app with Capawesome Cloud" src="https://cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
8
+ </a>
9
+ </div>
10
+
11
+ ## Features
12
+
13
+ - 🔒 **Root detection**: Detect rooted (Android) and jailbroken (iOS) devices.
14
+ - 🤖 **Emulator detection**: Detect whether the app is running on an emulator or simulator.
15
+ - 🛠️ **Developer mode**: Detect whether developer mode is enabled (Android).
16
+ - 🔗 **Compatibility**: Works alongside the [App Integrity](https://capawesome.io/plugins/app-integrity/) plugin for server-verifiable device attestation.
17
+ - 📦 **SPM**: Supports Swift Package Manager and CocoaPods for iOS.
18
+ - 🔁 **Up-to-date**: Always supports the latest Capacitor version.
19
+
20
+ Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
21
+
22
+ ## Newsletter
23
+
24
+ Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).
25
+
26
+ ## Compatibility
27
+
28
+ | Plugin Version | Capacitor Version | Status |
29
+ | -------------- | ----------------- | -------------- |
30
+ | 0.x.x | >=8.x.x | Active support |
31
+
32
+ ## Installation
33
+
34
+ You can use our **AI-Assisted Setup** to install the plugin.
35
+ Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:
36
+
37
+ ```bash
38
+ npx skills add capawesome-team/skills --skill capacitor-plugins
39
+ ```
40
+
41
+ Then use the following prompt:
42
+
43
+ ```
44
+ Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-root-detection` plugin in my project.
45
+ ```
46
+
47
+ If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:
48
+
49
+ ```bash
50
+ npm install @capawesome/capacitor-root-detection
51
+ npx cap sync
52
+ ```
53
+
54
+ ### Android
55
+
56
+ #### Variables
57
+
58
+ This plugin will use the following project variables (defined in your app's `variables.gradle` file):
59
+
60
+ - `$rootbeerVersion` version of `com.scottyab:rootbeer-lib` (default: `0.1.0`)
61
+
62
+ ### iOS
63
+
64
+ #### Application Queries Schemes
65
+
66
+ The `isRooted()` method checks whether the `cydia://` URL scheme can be opened as part of its jailbreak heuristics. For this check to work, add the `cydia` scheme to the `LSApplicationQueriesSchemes` array in the `Info.plist` file of your app:
67
+
68
+ ```xml
69
+ <key>LSApplicationQueriesSchemes</key>
70
+ <array>
71
+ <string>cydia</string>
72
+ </array>
73
+ ```
74
+
75
+ This step is optional. If the scheme is not added, the `isRooted()` method still works but skips the URL scheme check.
76
+
77
+ ## Configuration
78
+
79
+ No configuration required for this plugin.
80
+
81
+ ## Usage
82
+
83
+ ```typescript
84
+ import { RootDetection } from '@capawesome/capacitor-root-detection';
85
+
86
+ const isRooted = async () => {
87
+ const { rooted } = await RootDetection.isRooted();
88
+ return rooted;
89
+ };
90
+
91
+ const isEmulator = async () => {
92
+ const { emulator } = await RootDetection.isEmulator();
93
+ return emulator;
94
+ };
95
+
96
+ const isDeveloperModeEnabled = async () => {
97
+ const { enabled } = await RootDetection.isDeveloperModeEnabled();
98
+ return enabled;
99
+ };
100
+ ```
101
+
102
+ ## API
103
+
104
+ <docgen-index>
105
+
106
+ * [`isDeveloperModeEnabled()`](#isdevelopermodeenabled)
107
+ * [`isEmulator()`](#isemulator)
108
+ * [`isRooted()`](#isrooted)
109
+ * [Interfaces](#interfaces)
110
+
111
+ </docgen-index>
112
+
113
+ <docgen-api>
114
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
115
+
116
+ ### isDeveloperModeEnabled()
117
+
118
+ ```typescript
119
+ isDeveloperModeEnabled() => Promise<IsDeveloperModeEnabledResult>
120
+ ```
121
+
122
+ Check whether developer mode is enabled on the device.
123
+
124
+ Only available on Android.
125
+
126
+ **Returns:** <code>Promise&lt;<a href="#isdevelopermodeenabledresult">IsDeveloperModeEnabledResult</a>&gt;</code>
127
+
128
+ **Since:** 0.1.0
129
+
130
+ --------------------
131
+
132
+
133
+ ### isEmulator()
134
+
135
+ ```typescript
136
+ isEmulator() => Promise<IsEmulatorResult>
137
+ ```
138
+
139
+ Check whether the app is running on an emulator (Android) or simulator (iOS).
140
+
141
+ **Returns:** <code>Promise&lt;<a href="#isemulatorresult">IsEmulatorResult</a>&gt;</code>
142
+
143
+ **Since:** 0.1.0
144
+
145
+ --------------------
146
+
147
+
148
+ ### isRooted()
149
+
150
+ ```typescript
151
+ isRooted() => Promise<IsRootedResult>
152
+ ```
153
+
154
+ Check whether the device is rooted (Android) or jailbroken (iOS).
155
+
156
+ **Attention**: This is a best-effort, client-side check that can be
157
+ bypassed by a determined attacker. Do not rely on it as the sole security
158
+ measure. Use the App Integrity plugin when server-verifiable trust is
159
+ required.
160
+
161
+ **Returns:** <code>Promise&lt;<a href="#isrootedresult">IsRootedResult</a>&gt;</code>
162
+
163
+ **Since:** 0.1.0
164
+
165
+ --------------------
166
+
167
+
168
+ ### Interfaces
169
+
170
+
171
+ #### IsDeveloperModeEnabledResult
172
+
173
+ | Prop | Type | Description | Since |
174
+ | ------------- | -------------------- | ------------------------------------------------ | ----- |
175
+ | **`enabled`** | <code>boolean</code> | Whether developer mode is enabled on the device. | 0.1.0 |
176
+
177
+
178
+ #### IsEmulatorResult
179
+
180
+ | Prop | Type | Description | Since |
181
+ | -------------- | -------------------- | ----------------------------------------------------------------------- | ----- |
182
+ | **`emulator`** | <code>boolean</code> | Whether the app is running on an emulator (Android) or simulator (iOS). | 0.1.0 |
183
+
184
+
185
+ #### IsRootedResult
186
+
187
+ | Prop | Type | Description | Since |
188
+ | ------------ | -------------------- | ----------------------------------------------------------- | ----- |
189
+ | **`rooted`** | <code>boolean</code> | Whether the device is rooted (Android) or jailbroken (iOS). | 0.1.0 |
190
+
191
+ </docgen-api>
192
+
193
+ ## Security Considerations
194
+
195
+ The checks provided by this plugin are performed entirely on the device and are **best-effort**. A determined attacker with full control over a rooted or jailbroken device can bypass or spoof any client-side detection. Therefore, you should **never** rely on this plugin as the sole security measure for protecting sensitive functionality.
196
+
197
+ For **server-verifiable** device and app integrity, use the [App Integrity](https://capawesome.io/plugins/app-integrity/) plugin, which leverages the Play Integrity API (Android) and App Attest (iOS) to produce attestations that can be validated on your backend.
198
+
199
+ ## Changelog
200
+
201
+ See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/root-detection/CHANGELOG.md).
202
+
203
+ ## License
204
+
205
+ See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/root-detection/LICENSE).
@@ -0,0 +1,60 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ rootbeerVersion = project.hasProperty('rootbeerVersion') ? rootProject.ext.rootbeerVersion : '0.1.0'
7
+ }
8
+
9
+ buildscript {
10
+ repositories {
11
+ google()
12
+ mavenCentral()
13
+ }
14
+ dependencies {
15
+ classpath 'com.android.tools.build:gradle:8.13.0'
16
+ }
17
+ }
18
+
19
+ apply plugin: 'com.android.library'
20
+
21
+ android {
22
+ namespace = "io.capawesome.capacitorjs.plugins.rootdetection"
23
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
24
+ defaultConfig {
25
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
26
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
27
+ versionCode 1
28
+ versionName "1.0"
29
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30
+ }
31
+ buildTypes {
32
+ release {
33
+ minifyEnabled false
34
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
35
+ }
36
+ }
37
+ lintOptions {
38
+ abortOnError = false
39
+ }
40
+ compileOptions {
41
+ sourceCompatibility JavaVersion.VERSION_21
42
+ targetCompatibility JavaVersion.VERSION_21
43
+ }
44
+ }
45
+
46
+ repositories {
47
+ google()
48
+ mavenCentral()
49
+ }
50
+
51
+
52
+ dependencies {
53
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
54
+ implementation project(':capacitor-android')
55
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
56
+ implementation "com.scottyab:rootbeer-lib:$rootbeerVersion"
57
+ testImplementation "junit:junit:$junitVersion"
58
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
59
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
60
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,70 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection;
2
+
3
+ import android.os.Build;
4
+ import android.provider.Settings;
5
+ import androidx.annotation.NonNull;
6
+ import com.scottyab.rootbeer.RootBeer;
7
+ import io.capawesome.capacitorjs.plugins.rootdetection.classes.results.IsDeveloperModeEnabledResult;
8
+ import io.capawesome.capacitorjs.plugins.rootdetection.classes.results.IsEmulatorResult;
9
+ import io.capawesome.capacitorjs.plugins.rootdetection.classes.results.IsRootedResult;
10
+ import io.capawesome.capacitorjs.plugins.rootdetection.interfaces.NonEmptyResultCallback;
11
+
12
+ public class RootDetection {
13
+
14
+ @NonNull
15
+ private final RootDetectionPlugin plugin;
16
+
17
+ @NonNull
18
+ private final RootBeer rootBeer;
19
+
20
+ public RootDetection(@NonNull RootDetectionPlugin plugin) {
21
+ this.plugin = plugin;
22
+ this.rootBeer = new RootBeer(plugin.getContext());
23
+ }
24
+
25
+ public void isDeveloperModeEnabled(@NonNull NonEmptyResultCallback<IsDeveloperModeEnabledResult> callback) {
26
+ boolean enabled =
27
+ Settings.Global.getInt(plugin.getContext().getContentResolver(), Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
28
+ callback.success(new IsDeveloperModeEnabledResult(enabled));
29
+ }
30
+
31
+ public void isEmulator(@NonNull NonEmptyResultCallback<IsEmulatorResult> callback) {
32
+ callback.success(new IsEmulatorResult(isRunningOnEmulator()));
33
+ }
34
+
35
+ public void isRooted(@NonNull NonEmptyResultCallback<IsRootedResult> callback) {
36
+ callback.success(new IsRootedResult(rootBeer.isRooted()));
37
+ }
38
+
39
+ private boolean isRunningOnEmulator() {
40
+ String fingerprint = Build.FINGERPRINT == null ? "" : Build.FINGERPRINT;
41
+ String model = Build.MODEL == null ? "" : Build.MODEL;
42
+ String manufacturer = Build.MANUFACTURER == null ? "" : Build.MANUFACTURER;
43
+ String hardware = Build.HARDWARE == null ? "" : Build.HARDWARE;
44
+ String product = Build.PRODUCT == null ? "" : Build.PRODUCT;
45
+ String brand = Build.BRAND == null ? "" : Build.BRAND;
46
+ String device = Build.DEVICE == null ? "" : Build.DEVICE;
47
+
48
+ if (fingerprint.startsWith("generic") || fingerprint.startsWith("unknown")) {
49
+ return true;
50
+ }
51
+ if (model.contains("google_sdk") || model.contains("Emulator") || model.contains("Android SDK built for x86")) {
52
+ return true;
53
+ }
54
+ if (manufacturer.contains("Genymotion")) {
55
+ return true;
56
+ }
57
+ if (hardware.contains("goldfish") || hardware.contains("ranchu")) {
58
+ return true;
59
+ }
60
+ if (
61
+ product.contains("sdk_gphone") ||
62
+ product.contains("google_sdk") ||
63
+ product.contains("emulator") ||
64
+ product.contains("simulator")
65
+ ) {
66
+ return true;
67
+ }
68
+ return brand.startsWith("generic") && device.startsWith("generic");
69
+ }
70
+ }
@@ -0,0 +1,105 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+ import com.getcapacitor.Logger;
6
+ import com.getcapacitor.Plugin;
7
+ import com.getcapacitor.PluginCall;
8
+ import com.getcapacitor.PluginMethod;
9
+ import com.getcapacitor.annotation.CapacitorPlugin;
10
+ import io.capawesome.capacitorjs.plugins.rootdetection.classes.results.IsDeveloperModeEnabledResult;
11
+ import io.capawesome.capacitorjs.plugins.rootdetection.classes.results.IsEmulatorResult;
12
+ import io.capawesome.capacitorjs.plugins.rootdetection.classes.results.IsRootedResult;
13
+ import io.capawesome.capacitorjs.plugins.rootdetection.interfaces.NonEmptyResultCallback;
14
+ import io.capawesome.capacitorjs.plugins.rootdetection.interfaces.Result;
15
+
16
+ @CapacitorPlugin(name = "RootDetection")
17
+ public class RootDetectionPlugin extends Plugin {
18
+
19
+ public static final String ERROR_UNKNOWN_ERROR = "An unknown error has occurred.";
20
+ public static final String TAG = "RootDetection";
21
+
22
+ private RootDetection implementation;
23
+
24
+ @Override
25
+ public void load() {
26
+ this.implementation = new RootDetection(this);
27
+ }
28
+
29
+ @PluginMethod
30
+ public void isDeveloperModeEnabled(PluginCall call) {
31
+ try {
32
+ NonEmptyResultCallback<IsDeveloperModeEnabledResult> callback = new NonEmptyResultCallback<>() {
33
+ @Override
34
+ public void success(@NonNull IsDeveloperModeEnabledResult result) {
35
+ resolveCall(call, result);
36
+ }
37
+
38
+ @Override
39
+ public void error(Exception exception) {
40
+ rejectCall(call, exception);
41
+ }
42
+ };
43
+ implementation.isDeveloperModeEnabled(callback);
44
+ } catch (Exception exception) {
45
+ rejectCall(call, exception);
46
+ }
47
+ }
48
+
49
+ @PluginMethod
50
+ public void isEmulator(PluginCall call) {
51
+ try {
52
+ NonEmptyResultCallback<IsEmulatorResult> callback = new NonEmptyResultCallback<>() {
53
+ @Override
54
+ public void success(@NonNull IsEmulatorResult result) {
55
+ resolveCall(call, result);
56
+ }
57
+
58
+ @Override
59
+ public void error(Exception exception) {
60
+ rejectCall(call, exception);
61
+ }
62
+ };
63
+ implementation.isEmulator(callback);
64
+ } catch (Exception exception) {
65
+ rejectCall(call, exception);
66
+ }
67
+ }
68
+
69
+ @PluginMethod
70
+ public void isRooted(PluginCall call) {
71
+ try {
72
+ NonEmptyResultCallback<IsRootedResult> callback = new NonEmptyResultCallback<>() {
73
+ @Override
74
+ public void success(@NonNull IsRootedResult result) {
75
+ resolveCall(call, result);
76
+ }
77
+
78
+ @Override
79
+ public void error(Exception exception) {
80
+ rejectCall(call, exception);
81
+ }
82
+ };
83
+ implementation.isRooted(callback);
84
+ } catch (Exception exception) {
85
+ rejectCall(call, exception);
86
+ }
87
+ }
88
+
89
+ private void rejectCall(@NonNull PluginCall call, @NonNull Exception exception) {
90
+ String message = exception.getMessage();
91
+ if (message == null) {
92
+ message = ERROR_UNKNOWN_ERROR;
93
+ }
94
+ Logger.error(TAG, message, exception);
95
+ call.reject(message);
96
+ }
97
+
98
+ private void resolveCall(@NonNull PluginCall call, @Nullable Result result) {
99
+ if (result == null) {
100
+ call.resolve();
101
+ } else {
102
+ call.resolve(result.toJSObject());
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,22 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection.classes.results;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import com.getcapacitor.JSObject;
5
+ import io.capawesome.capacitorjs.plugins.rootdetection.interfaces.Result;
6
+
7
+ public class IsDeveloperModeEnabledResult implements Result {
8
+
9
+ private final boolean enabled;
10
+
11
+ public IsDeveloperModeEnabledResult(boolean enabled) {
12
+ this.enabled = enabled;
13
+ }
14
+
15
+ @Override
16
+ @NonNull
17
+ public JSObject toJSObject() {
18
+ JSObject result = new JSObject();
19
+ result.put("enabled", enabled);
20
+ return result;
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection.classes.results;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import com.getcapacitor.JSObject;
5
+ import io.capawesome.capacitorjs.plugins.rootdetection.interfaces.Result;
6
+
7
+ public class IsEmulatorResult implements Result {
8
+
9
+ private final boolean emulator;
10
+
11
+ public IsEmulatorResult(boolean emulator) {
12
+ this.emulator = emulator;
13
+ }
14
+
15
+ @Override
16
+ @NonNull
17
+ public JSObject toJSObject() {
18
+ JSObject result = new JSObject();
19
+ result.put("emulator", emulator);
20
+ return result;
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection.classes.results;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import com.getcapacitor.JSObject;
5
+ import io.capawesome.capacitorjs.plugins.rootdetection.interfaces.Result;
6
+
7
+ public class IsRootedResult implements Result {
8
+
9
+ private final boolean rooted;
10
+
11
+ public IsRootedResult(boolean rooted) {
12
+ this.rooted = rooted;
13
+ }
14
+
15
+ @Override
16
+ @NonNull
17
+ public JSObject toJSObject() {
18
+ JSObject result = new JSObject();
19
+ result.put("rooted", rooted);
20
+ return result;
21
+ }
22
+ }
@@ -0,0 +1,5 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection.interfaces;
2
+
3
+ public interface Callback {
4
+ void error(Exception exception);
5
+ }
@@ -0,0 +1,7 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection.interfaces;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ public interface NonEmptyResultCallback<T extends Result> extends Callback {
6
+ void success(@NonNull T result);
7
+ }
@@ -0,0 +1,7 @@
1
+ package io.capawesome.capacitorjs.plugins.rootdetection.interfaces;
2
+
3
+ import com.getcapacitor.JSObject;
4
+
5
+ public interface Result {
6
+ JSObject toJSObject();
7
+ }
File without changes
package/dist/docs.json ADDED
@@ -0,0 +1,162 @@
1
+ {
2
+ "api": {
3
+ "name": "RootDetectionPlugin",
4
+ "slug": "rootdetectionplugin",
5
+ "docs": "",
6
+ "tags": [
7
+ {
8
+ "text": "0.1.0",
9
+ "name": "since"
10
+ }
11
+ ],
12
+ "methods": [
13
+ {
14
+ "name": "isDeveloperModeEnabled",
15
+ "signature": "() => Promise<IsDeveloperModeEnabledResult>",
16
+ "parameters": [],
17
+ "returns": "Promise<IsDeveloperModeEnabledResult>",
18
+ "tags": [
19
+ {
20
+ "name": "since",
21
+ "text": "0.1.0"
22
+ }
23
+ ],
24
+ "docs": "Check whether developer mode is enabled on the device.\n\nOnly available on Android.",
25
+ "complexTypes": [
26
+ "IsDeveloperModeEnabledResult"
27
+ ],
28
+ "slug": "isdevelopermodeenabled"
29
+ },
30
+ {
31
+ "name": "isEmulator",
32
+ "signature": "() => Promise<IsEmulatorResult>",
33
+ "parameters": [],
34
+ "returns": "Promise<IsEmulatorResult>",
35
+ "tags": [
36
+ {
37
+ "name": "since",
38
+ "text": "0.1.0"
39
+ }
40
+ ],
41
+ "docs": "Check whether the app is running on an emulator (Android) or simulator (iOS).",
42
+ "complexTypes": [
43
+ "IsEmulatorResult"
44
+ ],
45
+ "slug": "isemulator"
46
+ },
47
+ {
48
+ "name": "isRooted",
49
+ "signature": "() => Promise<IsRootedResult>",
50
+ "parameters": [],
51
+ "returns": "Promise<IsRootedResult>",
52
+ "tags": [
53
+ {
54
+ "name": "since",
55
+ "text": "0.1.0"
56
+ }
57
+ ],
58
+ "docs": "Check whether the device is rooted (Android) or jailbroken (iOS).\n\n**Attention**: This is a best-effort, client-side check that can be\nbypassed by a determined attacker. Do not rely on it as the sole security\nmeasure. Use the App Integrity plugin when server-verifiable trust is\nrequired.",
59
+ "complexTypes": [
60
+ "IsRootedResult"
61
+ ],
62
+ "slug": "isrooted"
63
+ }
64
+ ],
65
+ "properties": []
66
+ },
67
+ "interfaces": [
68
+ {
69
+ "name": "IsDeveloperModeEnabledResult",
70
+ "slug": "isdevelopermodeenabledresult",
71
+ "docs": "",
72
+ "tags": [
73
+ {
74
+ "text": "0.1.0",
75
+ "name": "since"
76
+ }
77
+ ],
78
+ "methods": [],
79
+ "properties": [
80
+ {
81
+ "name": "enabled",
82
+ "tags": [
83
+ {
84
+ "text": "false",
85
+ "name": "example"
86
+ },
87
+ {
88
+ "text": "0.1.0",
89
+ "name": "since"
90
+ }
91
+ ],
92
+ "docs": "Whether developer mode is enabled on the device.",
93
+ "complexTypes": [],
94
+ "type": "boolean"
95
+ }
96
+ ]
97
+ },
98
+ {
99
+ "name": "IsEmulatorResult",
100
+ "slug": "isemulatorresult",
101
+ "docs": "",
102
+ "tags": [
103
+ {
104
+ "text": "0.1.0",
105
+ "name": "since"
106
+ }
107
+ ],
108
+ "methods": [],
109
+ "properties": [
110
+ {
111
+ "name": "emulator",
112
+ "tags": [
113
+ {
114
+ "text": "false",
115
+ "name": "example"
116
+ },
117
+ {
118
+ "text": "0.1.0",
119
+ "name": "since"
120
+ }
121
+ ],
122
+ "docs": "Whether the app is running on an emulator (Android) or simulator (iOS).",
123
+ "complexTypes": [],
124
+ "type": "boolean"
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ "name": "IsRootedResult",
130
+ "slug": "isrootedresult",
131
+ "docs": "",
132
+ "tags": [
133
+ {
134
+ "text": "0.1.0",
135
+ "name": "since"
136
+ }
137
+ ],
138
+ "methods": [],
139
+ "properties": [
140
+ {
141
+ "name": "rooted",
142
+ "tags": [
143
+ {
144
+ "text": "false",
145
+ "name": "example"
146
+ },
147
+ {
148
+ "text": "0.1.0",
149
+ "name": "since"
150
+ }
151
+ ],
152
+ "docs": "Whether the device is rooted (Android) or jailbroken (iOS).",
153
+ "complexTypes": [],
154
+ "type": "boolean"
155
+ }
156
+ ]
157
+ }
158
+ ],
159
+ "enums": [],
160
+ "typeAliases": [],
161
+ "pluginConfigs": []
162
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @since 0.1.0
3
+ */
4
+ export interface RootDetectionPlugin {
5
+ /**
6
+ * Check whether developer mode is enabled on the device.
7
+ *
8
+ * Only available on Android.
9
+ *
10
+ * @since 0.1.0
11
+ */
12
+ isDeveloperModeEnabled(): Promise<IsDeveloperModeEnabledResult>;
13
+ /**
14
+ * Check whether the app is running on an emulator (Android) or simulator (iOS).
15
+ *
16
+ * @since 0.1.0
17
+ */
18
+ isEmulator(): Promise<IsEmulatorResult>;
19
+ /**
20
+ * Check whether the device is rooted (Android) or jailbroken (iOS).
21
+ *
22
+ * **Attention**: This is a best-effort, client-side check that can be
23
+ * bypassed by a determined attacker. Do not rely on it as the sole security
24
+ * measure. Use the App Integrity plugin when server-verifiable trust is
25
+ * required.
26
+ *
27
+ * @since 0.1.0
28
+ */
29
+ isRooted(): Promise<IsRootedResult>;
30
+ }
31
+ /**
32
+ * @since 0.1.0
33
+ */
34
+ export interface IsDeveloperModeEnabledResult {
35
+ /**
36
+ * Whether developer mode is enabled on the device.
37
+ *
38
+ * @example false
39
+ * @since 0.1.0
40
+ */
41
+ enabled: boolean;
42
+ }
43
+ /**
44
+ * @since 0.1.0
45
+ */
46
+ export interface IsEmulatorResult {
47
+ /**
48
+ * Whether the app is running on an emulator (Android) or simulator (iOS).
49
+ *
50
+ * @example false
51
+ * @since 0.1.0
52
+ */
53
+ emulator: boolean;
54
+ }
55
+ /**
56
+ * @since 0.1.0
57
+ */
58
+ export interface IsRootedResult {
59
+ /**
60
+ * Whether the device is rooted (Android) or jailbroken (iOS).
61
+ *
62
+ * @example false
63
+ * @since 0.1.0
64
+ */
65
+ rooted: boolean;
66
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * @since 0.1.0\n */\nexport interface RootDetectionPlugin {\n /**\n * Check whether developer mode is enabled on the device.\n *\n * Only available on Android.\n *\n * @since 0.1.0\n */\n isDeveloperModeEnabled(): Promise<IsDeveloperModeEnabledResult>;\n /**\n * Check whether the app is running on an emulator (Android) or simulator (iOS).\n *\n * @since 0.1.0\n */\n isEmulator(): Promise<IsEmulatorResult>;\n /**\n * Check whether the device is rooted (Android) or jailbroken (iOS).\n *\n * **Attention**: This is a best-effort, client-side check that can be\n * bypassed by a determined attacker. Do not rely on it as the sole security\n * measure. Use the App Integrity plugin when server-verifiable trust is\n * required.\n *\n * @since 0.1.0\n */\n isRooted(): Promise<IsRootedResult>;\n}\n\n/**\n * @since 0.1.0\n */\nexport interface IsDeveloperModeEnabledResult {\n /**\n * Whether developer mode is enabled on the device.\n *\n * @example false\n * @since 0.1.0\n */\n enabled: boolean;\n}\n\n/**\n * @since 0.1.0\n */\nexport interface IsEmulatorResult {\n /**\n * Whether the app is running on an emulator (Android) or simulator (iOS).\n *\n * @example false\n * @since 0.1.0\n */\n emulator: boolean;\n}\n\n/**\n * @since 0.1.0\n */\nexport interface IsRootedResult {\n /**\n * Whether the device is rooted (Android) or jailbroken (iOS).\n *\n * @example false\n * @since 0.1.0\n */\n rooted: boolean;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { RootDetectionPlugin } from './definitions';
2
+ declare const RootDetection: RootDetectionPlugin;
3
+ export * from './definitions';
4
+ export { RootDetection };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const RootDetection = registerPlugin('RootDetection', {
3
+ web: () => import('./web').then(m => new m.RootDetectionWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { RootDetection };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,aAAa,GAAG,cAAc,CAAsB,eAAe,EAAE;IACzE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;CAC/D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { RootDetectionPlugin } from './definitions';\n\nconst RootDetection = registerPlugin<RootDetectionPlugin>('RootDetection', {\n web: () => import('./web').then(m => new m.RootDetectionWeb()),\n});\n\nexport * from './definitions';\nexport { RootDetection };\n"]}
@@ -0,0 +1,7 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { IsDeveloperModeEnabledResult, IsEmulatorResult, IsRootedResult, RootDetectionPlugin } from './definitions';
3
+ export declare class RootDetectionWeb extends WebPlugin implements RootDetectionPlugin {
4
+ isDeveloperModeEnabled(): Promise<IsDeveloperModeEnabledResult>;
5
+ isEmulator(): Promise<IsEmulatorResult>;
6
+ isRooted(): Promise<IsRootedResult>;
7
+ }
@@ -0,0 +1,13 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class RootDetectionWeb extends WebPlugin {
3
+ async isDeveloperModeEnabled() {
4
+ throw this.unimplemented('Not implemented on web.');
5
+ }
6
+ async isEmulator() {
7
+ throw this.unimplemented('Not implemented on web.');
8
+ }
9
+ async isRooted() {
10
+ throw this.unimplemented('Not implemented on web.');
11
+ }
12
+ }
13
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAS5C,MAAM,OAAO,gBAAiB,SAAQ,SAAS;IAC7C,KAAK,CAAC,sBAAsB;QAC1B,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC;IACtD,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n IsDeveloperModeEnabledResult,\n IsEmulatorResult,\n IsRootedResult,\n RootDetectionPlugin,\n} from './definitions';\n\nexport class RootDetectionWeb extends WebPlugin implements RootDetectionPlugin {\n async isDeveloperModeEnabled(): Promise<IsDeveloperModeEnabledResult> {\n throw this.unimplemented('Not implemented on web.');\n }\n\n async isEmulator(): Promise<IsEmulatorResult> {\n throw this.unimplemented('Not implemented on web.');\n }\n\n async isRooted(): Promise<IsRootedResult> {\n throw this.unimplemented('Not implemented on web.');\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const RootDetection = core.registerPlugin('RootDetection', {
6
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.RootDetectionWeb()),
7
+ });
8
+
9
+ class RootDetectionWeb extends core.WebPlugin {
10
+ async isDeveloperModeEnabled() {
11
+ throw this.unimplemented('Not implemented on web.');
12
+ }
13
+ async isEmulator() {
14
+ throw this.unimplemented('Not implemented on web.');
15
+ }
16
+ async isRooted() {
17
+ throw this.unimplemented('Not implemented on web.');
18
+ }
19
+ }
20
+
21
+ var web = /*#__PURE__*/Object.freeze({
22
+ __proto__: null,
23
+ RootDetectionWeb: RootDetectionWeb
24
+ });
25
+
26
+ exports.RootDetection = RootDetection;
27
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst RootDetection = registerPlugin('RootDetection', {\n web: () => import('./web').then(m => new m.RootDetectionWeb()),\n});\nexport * from './definitions';\nexport { RootDetection };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class RootDetectionWeb extends WebPlugin {\n async isDeveloperModeEnabled() {\n throw this.unimplemented('Not implemented on web.');\n }\n async isEmulator() {\n throw this.unimplemented('Not implemented on web.');\n }\n async isRooted() {\n throw this.unimplemented('Not implemented on web.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;AACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;AAClE,CAAC;;ACFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;AAChD,IAAI,MAAM,sBAAsB,GAAG;AACnC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;AAC3D,IAAI;AACJ,IAAI,MAAM,UAAU,GAAG;AACvB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;AAC3D,IAAI;AACJ,IAAI,MAAM,QAAQ,GAAG;AACrB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;AAC3D,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,30 @@
1
+ var capacitorRootDetection = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const RootDetection = core.registerPlugin('RootDetection', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.RootDetectionWeb()),
6
+ });
7
+
8
+ class RootDetectionWeb extends core.WebPlugin {
9
+ async isDeveloperModeEnabled() {
10
+ throw this.unimplemented('Not implemented on web.');
11
+ }
12
+ async isEmulator() {
13
+ throw this.unimplemented('Not implemented on web.');
14
+ }
15
+ async isRooted() {
16
+ throw this.unimplemented('Not implemented on web.');
17
+ }
18
+ }
19
+
20
+ var web = /*#__PURE__*/Object.freeze({
21
+ __proto__: null,
22
+ RootDetectionWeb: RootDetectionWeb
23
+ });
24
+
25
+ exports.RootDetection = RootDetection;
26
+
27
+ return exports;
28
+
29
+ })({}, capacitorExports);
30
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst RootDetection = registerPlugin('RootDetection', {\n web: () => import('./web').then(m => new m.RootDetectionWeb()),\n});\nexport * from './definitions';\nexport { RootDetection };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class RootDetectionWeb extends WebPlugin {\n async isDeveloperModeEnabled() {\n throw this.unimplemented('Not implemented on web.');\n }\n async isEmulator() {\n throw this.unimplemented('Not implemented on web.');\n }\n async isRooted() {\n throw this.unimplemented('Not implemented on web.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;IACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;;ICFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;IAChD,IAAI,MAAM,sBAAsB,GAAG;IACnC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,UAAU,GAAG;IACvB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,QAAQ,GAAG;IACrB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,16 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class IsEmulatorResult: NSObject, Result {
5
+ let emulator: Bool
6
+
7
+ init(emulator: Bool) {
8
+ self.emulator = emulator
9
+ }
10
+
11
+ @objc public func toJSObject() -> AnyObject {
12
+ var result = JSObject()
13
+ result["emulator"] = emulator
14
+ return result as AnyObject
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class IsRootedResult: NSObject, Result {
5
+ let rooted: Bool
6
+
7
+ init(rooted: Bool) {
8
+ self.rooted = rooted
9
+ }
10
+
11
+ @objc public func toJSObject() -> AnyObject {
12
+ var result = JSObject()
13
+ result["rooted"] = rooted
14
+ return result as AnyObject
15
+ }
16
+ }
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
@@ -0,0 +1,5 @@
1
+ import Foundation
2
+
3
+ @objc public protocol Result {
4
+ @objc func toJSObject() -> AnyObject
5
+ }
@@ -0,0 +1,64 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ @objc public class RootDetection: NSObject {
5
+ private let plugin: RootDetectionPlugin
6
+
7
+ init(plugin: RootDetectionPlugin) {
8
+ self.plugin = plugin
9
+ }
10
+
11
+ @objc public func isEmulator(completion: @escaping (IsEmulatorResult?, Error?) -> Void) {
12
+ #if targetEnvironment(simulator)
13
+ completion(IsEmulatorResult(emulator: true), nil)
14
+ #else
15
+ completion(IsEmulatorResult(emulator: false), nil)
16
+ #endif
17
+ }
18
+
19
+ @objc public func isRooted(completion: @escaping (IsRootedResult?, Error?) -> Void) {
20
+ completion(IsRootedResult(rooted: isJailbroken()), nil)
21
+ }
22
+
23
+ private func canOpenJailbreakScheme() -> Bool {
24
+ guard let url = URL(string: "cydia://package/com.example.package") else {
25
+ return false
26
+ }
27
+ return UIApplication.shared.canOpenURL(url)
28
+ }
29
+
30
+ private func canWriteOutsideSandbox() -> Bool {
31
+ let path = "/private/jailbreak.txt"
32
+ do {
33
+ try "jailbreak".write(toFile: path, atomically: true, encoding: .utf8)
34
+ try FileManager.default.removeItem(atPath: path)
35
+ return true
36
+ } catch {
37
+ return false
38
+ }
39
+ }
40
+
41
+ private func hasSuspiciousFiles() -> Bool {
42
+ let paths = [
43
+ "/Applications/Cydia.app",
44
+ "/Library/MobileSubstrate/MobileSubstrate.dylib",
45
+ "/bin/bash",
46
+ "/usr/sbin/sshd",
47
+ "/etc/apt",
48
+ "/private/var/lib/apt",
49
+ "/usr/bin/ssh"
50
+ ]
51
+ for path in paths where FileManager.default.fileExists(atPath: path) {
52
+ return true
53
+ }
54
+ return false
55
+ }
56
+
57
+ private func isJailbroken() -> Bool {
58
+ #if targetEnvironment(simulator)
59
+ return false
60
+ #else
61
+ return hasSuspiciousFiles() || canWriteOutsideSandbox() || canOpenJailbreakScheme()
62
+ #endif
63
+ }
64
+ }
@@ -0,0 +1,62 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc(RootDetectionPlugin)
5
+ public class RootDetectionPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "RootDetectionPlugin"
7
+ public let jsName = "RootDetection"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "isDeveloperModeEnabled", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "isEmulator", returnType: CAPPluginReturnPromise),
11
+ CAPPluginMethod(name: "isRooted", returnType: CAPPluginReturnPromise)
12
+ ]
13
+
14
+ public static let tag = "RootDetectionPlugin"
15
+
16
+ private var implementation: RootDetection?
17
+
18
+ override public func load() {
19
+ self.implementation = RootDetection(plugin: self)
20
+ }
21
+
22
+ @objc func isDeveloperModeEnabled(_ call: CAPPluginCall) {
23
+ rejectCallAsUnimplemented(call)
24
+ }
25
+
26
+ @objc func isEmulator(_ call: CAPPluginCall) {
27
+ implementation?.isEmulator { result, error in
28
+ if let error = error {
29
+ self.rejectCall(call, error)
30
+ return
31
+ }
32
+ self.resolveCall(call, result)
33
+ }
34
+ }
35
+
36
+ @objc func isRooted(_ call: CAPPluginCall) {
37
+ implementation?.isRooted { result, error in
38
+ if let error = error {
39
+ self.rejectCall(call, error)
40
+ return
41
+ }
42
+ self.resolveCall(call, result)
43
+ }
44
+ }
45
+
46
+ private func rejectCall(_ call: CAPPluginCall, _ error: Error) {
47
+ CAPLog.print("[", RootDetectionPlugin.tag, "] ", error)
48
+ call.reject(error.localizedDescription)
49
+ }
50
+
51
+ private func rejectCallAsUnimplemented(_ call: CAPPluginCall) {
52
+ call.unimplemented("This method is not available on this platform.")
53
+ }
54
+
55
+ private func resolveCall(_ call: CAPPluginCall, _ result: Result?) {
56
+ if let result = result?.toJSObject() as? JSObject {
57
+ call.resolve(result)
58
+ } else {
59
+ call.resolve()
60
+ }
61
+ }
62
+ }
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@capawesome/capacitor-root-detection",
3
+ "version": "0.0.1",
4
+ "description": "Capacitor plugin for detecting rooted and jailbroken devices.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "CapawesomeCapacitorRootDetection.podspec",
15
+ "Package.swift"
16
+ ],
17
+ "author": "Robin Genz <mail@robingenz.dev>",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/capawesome-team/capacitor-plugins.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/capawesome-team/capacitor-plugins/issues"
25
+ },
26
+ "funding": [
27
+ {
28
+ "type": "github",
29
+ "url": "https://github.com/sponsors/capawesome-team/"
30
+ },
31
+ {
32
+ "type": "opencollective",
33
+ "url": "https://opencollective.com/capawesome"
34
+ }
35
+ ],
36
+ "homepage": "https://capawesome.io/docs/plugins/root-detection/",
37
+ "keywords": [
38
+ "capacitor",
39
+ "plugin",
40
+ "native"
41
+ ],
42
+ "scripts": {
43
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
44
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
45
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
46
+ "verify:web": "npm run build",
47
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
48
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
49
+ "eslint": "eslint . --ext ts",
50
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
51
+ "swiftlint": "node-swiftlint",
52
+ "docgen": "docgen --api RootDetectionPlugin --output-readme README.md --output-json dist/docs.json",
53
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
54
+ "clean": "rimraf ./dist",
55
+ "watch": "tsc --watch",
56
+ "ios:pod:install": "cd ios && pod install --repo-update && cd ..",
57
+ "ios:spm:install": "cd ios && swift package resolve && cd ..",
58
+ "prepublishOnly": "npm run build"
59
+ },
60
+ "devDependencies": {
61
+ "@capacitor/android": "8.0.0",
62
+ "@capacitor/cli": "8.0.0",
63
+ "@capacitor/core": "8.0.0",
64
+ "@capacitor/docgen": "0.3.1",
65
+ "@capacitor/ios": "8.0.0",
66
+ "@ionic/eslint-config": "0.4.0",
67
+ "eslint": "8.57.0",
68
+ "prettier-plugin-java": "2.6.7",
69
+ "rimraf": "6.1.2",
70
+ "rollup": "4.53.3",
71
+ "swiftlint": "2.0.0",
72
+ "typescript": "5.9.3"
73
+ },
74
+ "peerDependencies": {
75
+ "@capacitor/core": ">=8.0.0"
76
+ },
77
+ "eslintConfig": {
78
+ "extends": "@ionic/eslint-config/recommended"
79
+ },
80
+ "capacitor": {
81
+ "ios": {
82
+ "src": "ios"
83
+ },
84
+ "android": {
85
+ "src": "android"
86
+ }
87
+ },
88
+ "publishConfig": {
89
+ "access": "public"
90
+ }
91
+ }