@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.
- package/CapawesomeCapacitorRootDetection.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +205 -0
- package/android/build.gradle +60 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/RootDetection.java +70 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/RootDetectionPlugin.java +105 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/classes/results/IsDeveloperModeEnabledResult.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/classes/results/IsEmulatorResult.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/classes/results/IsRootedResult.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/interfaces/Callback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/interfaces/NonEmptyResultCallback.java +7 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/interfaces/Result.java +7 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +162 -0
- package/dist/esm/definitions.d.ts +66 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +7 -0
- package/dist/esm/web.js +13 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +27 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +30 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/Classes/Results/IsEmulatorResult.swift +16 -0
- package/ios/Plugin/Classes/Results/IsRootedResult.swift +16 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/Protocols/Result.swift +5 -0
- package/ios/Plugin/RootDetection.swift +64 -0
- package/ios/Plugin/RootDetectionPlugin.swift +62 -0
- 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<<a href="#isdevelopermodeenabledresult">IsDeveloperModeEnabledResult</a>></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<<a href="#isemulatorresult">IsEmulatorResult</a>></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<<a href="#isrootedresult">IsRootedResult</a>></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
|
+
}
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/rootdetection/RootDetection.java
ADDED
|
@@ -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
|
+
}
|
|
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 @@
|
|
|
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,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
|
+
}
|
package/dist/esm/web.js
ADDED
|
@@ -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,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
|
+
}
|