react-native-device-integrity 0.1.0
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/DeviceIntegrity.podspec +21 -0
- package/LICENSE +20 -0
- package/README.md +286 -0
- package/android/build.gradle +55 -0
- package/android/src/main/AndroidManifest.xml +20 -0
- package/android/src/main/java/com/deviceintegrity/DeviceIntegrityModule.kt +48 -0
- package/android/src/main/java/com/deviceintegrity/DeviceIntegrityPackage.kt +31 -0
- package/android/src/main/java/com/deviceintegrity/IntegrityChecks.kt +454 -0
- package/app.plugin.js +1 -0
- package/ios/DIIntegrityChecks.h +15 -0
- package/ios/DIIntegrityChecks.m +395 -0
- package/ios/DeviceIntegrity.h +5 -0
- package/ios/DeviceIntegrity.mm +29 -0
- package/ios/PrivacyInfo.xcprivacy +14 -0
- package/lib/module/NativeDeviceIntegrity.js +5 -0
- package/lib/module/NativeDeviceIntegrity.js.map +1 -0
- package/lib/module/checkIntegrity.js +15 -0
- package/lib/module/checkIntegrity.js.map +1 -0
- package/lib/module/checkIntegrity.native.js +67 -0
- package/lib/module/checkIntegrity.native.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/resolveStatus.js +76 -0
- package/lib/module/resolveStatus.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useDeviceIntegrity.js +62 -0
- package/lib/module/useDeviceIntegrity.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeDeviceIntegrity.d.ts +17 -0
- package/lib/typescript/src/NativeDeviceIntegrity.d.ts.map +1 -0
- package/lib/typescript/src/checkIntegrity.d.ts +6 -0
- package/lib/typescript/src/checkIntegrity.d.ts.map +1 -0
- package/lib/typescript/src/checkIntegrity.native.d.ts +3 -0
- package/lib/typescript/src/checkIntegrity.native.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/resolveStatus.d.ts +8 -0
- package/lib/typescript/src/resolveStatus.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +23 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/useDeviceIntegrity.d.ts +10 -0
- package/lib/typescript/src/useDeviceIntegrity.d.ts.map +1 -0
- package/package.json +210 -0
- package/plugin/build/index.d.ts +19 -0
- package/plugin/build/index.js +49 -0
- package/src/NativeDeviceIntegrity.ts +19 -0
- package/src/checkIntegrity.native.ts +88 -0
- package/src/checkIntegrity.ts +16 -0
- package/src/index.tsx +10 -0
- package/src/resolveStatus.ts +125 -0
- package/src/types.ts +41 -0
- package/src/useDeviceIntegrity.ts +92 -0
|
@@ -0,0 +1,21 @@
|
|
|
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 = "DeviceIntegrity"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/RRigueira/react-native-device-integrity.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
s.resource_bundles = { "DeviceIntegrityPrivacy" => ["ios/PrivacyInfo.xcprivacy"] }
|
|
19
|
+
|
|
20
|
+
install_modules_dependencies(s)
|
|
21
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rui Rigueira
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# react-native-device-integrity
|
|
2
|
+
|
|
3
|
+
Jailbreak, root, hooking, debugger and emulator detection for React Native and Expo.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/react-native-device-integrity)
|
|
6
|
+
[](https://www.npmjs.com/package/react-native-device-integrity)
|
|
7
|
+
[](https://github.com/RRigueira/react-native-device-integrity/actions/workflows/ci.yml)
|
|
8
|
+
[](https://docs.expo.dev/config-plugins/introduction/)
|
|
9
|
+
[](https://reactnative.dev/docs/the-new-architecture/landing-page)
|
|
10
|
+
|
|
11
|
+
## What it does / doesn't
|
|
12
|
+
|
|
13
|
+
**Does:** report client-side signals for jailbreak/root, hooking frameworks, attached debuggers, and emulator/simulator environments.
|
|
14
|
+
|
|
15
|
+
**Doesn't:** send telemetry, make network calls, or request permissions. Client-side only — **not** a replacement for commercial RASP/anti-tamper products or server-side attestation (App Attest / Play Integrity).
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- **New Architecture (TurboModule) required** — no old-architecture bridge fallback.
|
|
20
|
+
- Developed and tested with **React Native 0.86** and **Expo SDK 57**.
|
|
21
|
+
- **iOS 15.1+**, **Android minSdk 24**.
|
|
22
|
+
- Older RN (**≥ 0.76**) is expected to work but is not yet tested.
|
|
23
|
+
- **Expo Go is not supported** — use a development build / `prebuild`.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Expo
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npx expo install react-native-device-integrity
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Add the config plugin to `app.json` / `app.config.js`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"expo": {
|
|
38
|
+
"plugins": ["react-native-device-integrity"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
To skip Info.plist URL-scheme entries (e.g. you manage them yourself):
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"expo": {
|
|
48
|
+
"plugins": [
|
|
49
|
+
[
|
|
50
|
+
"react-native-device-integrity",
|
|
51
|
+
{ "ios": { "jailbreakUrlSchemes": false } }
|
|
52
|
+
]
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then run `npx expo prebuild` or use a development build. Expo Go is not supported.
|
|
59
|
+
|
|
60
|
+
### Bare React Native
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
npm install react-native-device-integrity
|
|
64
|
+
# or: yarn add react-native-device-integrity
|
|
65
|
+
|
|
66
|
+
cd ios && pod install
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
On iOS, add these schemes under `LSApplicationQueriesSchemes` in `Info.plist`:
|
|
70
|
+
|
|
71
|
+
`cydia`, `sileo`, `zbra`, `filza`, `undecimus`, `activator`
|
|
72
|
+
|
|
73
|
+
Without them, `canOpenURL` cannot query those schemes and the URL-scheme check **silently finds nothing**.
|
|
74
|
+
|
|
75
|
+
Android needs no extra setup — the library manifest `<queries>` list merges automatically via the Gradle manifest merger.
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import {
|
|
81
|
+
checkIntegrity,
|
|
82
|
+
useDeviceIntegrity,
|
|
83
|
+
} from 'react-native-device-integrity';
|
|
84
|
+
|
|
85
|
+
const result = await checkIntegrity({
|
|
86
|
+
treatEmulatorAsCompromised: false,
|
|
87
|
+
timeoutMs: 10_000,
|
|
88
|
+
});
|
|
89
|
+
// result.status: 'clean' | 'compromised' | 'unknown'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Button, Text, View } from 'react-native';
|
|
94
|
+
|
|
95
|
+
function IntegrityStatus() {
|
|
96
|
+
const { status, signals, result, loading, refresh } = useDeviceIntegrity();
|
|
97
|
+
|
|
98
|
+
if (loading && result == null) {
|
|
99
|
+
return <Text>Checking…</Text>; // first check in flight
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<View>
|
|
104
|
+
<Text>Status: {status}</Text>
|
|
105
|
+
{signals.map((signal) => (
|
|
106
|
+
<Text key={signal.id}>{signal.id}</Text>
|
|
107
|
+
))}
|
|
108
|
+
<Button title="Re-check" onPress={refresh} disabled={loading} />
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## API reference
|
|
115
|
+
|
|
116
|
+
### `checkIntegrity(options?) → Promise<IntegrityResult>`
|
|
117
|
+
|
|
118
|
+
Never rejects. Failures map to `status: 'unknown'` with a `reason`.
|
|
119
|
+
|
|
120
|
+
#### `CheckIntegrityOptions`
|
|
121
|
+
|
|
122
|
+
| Option | Default | Description |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `treatEmulatorAsCompromised` | `false` | When `true`, emulator/simulator signals make `status` `'compromised'`. When `false`, they are reported but do not compromise. |
|
|
125
|
+
| `timeoutMs` | `10000` | Resolve as `'unknown'` with reason `'timeout'` if native does not answer in time. |
|
|
126
|
+
|
|
127
|
+
#### `IntegrityResult`
|
|
128
|
+
|
|
129
|
+
| Field | Type | Description |
|
|
130
|
+
| --- | --- | --- |
|
|
131
|
+
| `status` | `'clean' \| 'compromised' \| 'unknown'` | Resolved status (see [Result semantics](#result-semantics)). |
|
|
132
|
+
| `signals` | `Signal[]` | Detected signals (may be empty). |
|
|
133
|
+
| `platform` | `Platform.OS` | Platform the check ran on. |
|
|
134
|
+
| `reason?` | `UnknownReason` | Present when `status` is `'unknown'`. |
|
|
135
|
+
| `error?` | `string` | Optional message (e.g. native error text). |
|
|
136
|
+
|
|
137
|
+
#### `Signal`
|
|
138
|
+
|
|
139
|
+
| Field | Type |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| `id` | `string` (stable public API) |
|
|
142
|
+
| `category` | `SignalCategory` |
|
|
143
|
+
| `description` | `string` (generic; never includes matched paths/packages) |
|
|
144
|
+
|
|
145
|
+
#### `SignalCategory`
|
|
146
|
+
|
|
147
|
+
`'jailbreak'` | `'root'` | `'hooking'` | `'debugger'` | `'emulator'` | `'tamper'` | `'environment'`
|
|
148
|
+
|
|
149
|
+
`tamper` is reserved for future checks; no current signal uses it. A native category the JS layer doesn't recognise is reported as `environment`, never dropped.
|
|
150
|
+
|
|
151
|
+
#### `UnknownReason`
|
|
152
|
+
|
|
153
|
+
| Reason | When |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `unsupported_platform` | `Platform.OS` is not `ios` or `android` (e.g. web). |
|
|
156
|
+
| `native_module_unavailable` | TurboModule is missing / null (wrong arch, Expo Go, failed link). |
|
|
157
|
+
| `native_error` | Native call threw/rejected, or the native report was malformed. |
|
|
158
|
+
| `timeout` | Native did not answer within `timeoutMs`. |
|
|
159
|
+
| `incomplete` | Native finished with `completed: false` (a check threw; compromise evidence still wins if any non-emulator signal is present). |
|
|
160
|
+
| `not_implemented` | Native report has `completed: false` and `reason: 'not_implemented'` (stub / unfinished native path). |
|
|
161
|
+
|
|
162
|
+
### `useDeviceIntegrity(options?)`
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
|
|
166
|
+
| Field | Type | Description |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| `status` | `IntegrityStatus` | `result?.status ?? 'unknown'` |
|
|
169
|
+
| `signals` | `Signal[]` | `result?.signals ?? []` |
|
|
170
|
+
| `result` | `IntegrityResult \| null` | Latest completed result (null until first check finishes). |
|
|
171
|
+
| `loading` | `boolean` | `true` while a check is in flight. |
|
|
172
|
+
| `refresh` | `() => Promise<IntegrityResult>` | Run another check. |
|
|
173
|
+
|
|
174
|
+
Behaviour:
|
|
175
|
+
|
|
176
|
+
- Runs on mount.
|
|
177
|
+
- Re-checks when the app returns to the foreground (`background`/`inactive` → `active`).
|
|
178
|
+
- Latest-request-wins (stale responses are ignored).
|
|
179
|
+
- Keeps the previous `result` while refreshing (`loading` is `true`, `result` is not cleared).
|
|
180
|
+
|
|
181
|
+
## Result semantics
|
|
182
|
+
|
|
183
|
+
Status is resolved from the native report + options:
|
|
184
|
+
|
|
185
|
+
1. **Compromise evidence wins** — any non-emulator signal → `'compromised'`, even if the run was incomplete.
|
|
186
|
+
2. Emulator/simulator signals compromise **only** when `treatEmulatorAsCompromised` is `true`.
|
|
187
|
+
3. If the run did not complete (`completed: false`) and there is no compromising evidence → `'unknown'`.
|
|
188
|
+
4. Otherwise → `'clean'`.
|
|
189
|
+
|
|
190
|
+
**`unknown` is never `clean`.** Incomplete, timed-out, missing-module, and unsupported-platform outcomes must not be treated as a safe device.
|
|
191
|
+
|
|
192
|
+
## Recommended fail-closed usage
|
|
193
|
+
|
|
194
|
+
Treat only `'clean'` as safe. Block or degrade on both `'compromised'` and `'unknown'`. Re-check on foreground (the hook does this). For high-value actions, pair with server-side attestation.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
function IntegrityGate({ children }: { children: React.ReactNode }) {
|
|
198
|
+
const { status, result, loading } = useDeviceIntegrity();
|
|
199
|
+
|
|
200
|
+
if (loading && result == null) {
|
|
201
|
+
return null; // or a spinner — do not flash protected UI
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (status === 'clean') {
|
|
205
|
+
return <>{children}</>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Block or degrade for both 'compromised' and 'unknown'
|
|
209
|
+
return <BlockedScreen status={status} />;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Signal reference
|
|
214
|
+
|
|
215
|
+
Descriptions are generic on purpose (no matched paths or package names).
|
|
216
|
+
|
|
217
|
+
### iOS
|
|
218
|
+
|
|
219
|
+
| id | category | Checks |
|
|
220
|
+
| --- | --- | --- |
|
|
221
|
+
| `simulator` | `emulator` | Running on the iOS Simulator. |
|
|
222
|
+
| `jailbreak_files` | `jailbreak` | Unexpected system paths associated with jailbreaks. |
|
|
223
|
+
| `jailbreak_url_schemes` | `jailbreak` | Jailbreak-related URL schemes are registered (`canOpenURL`). |
|
|
224
|
+
| `jailbreak_symlinks` | `jailbreak` | Unexpected symbolic links in system locations. |
|
|
225
|
+
| `jailbreak_writable_system` | `jailbreak` | A normally read-only system location is writable. |
|
|
226
|
+
| `hooking_libraries` | `hooking` | Suspicious dynamic libraries are loaded. |
|
|
227
|
+
| `hooking_dyld_insert` | `hooking` | `DYLD_INSERT_LIBRARIES` is set in the process environment. |
|
|
228
|
+
| `debugger_attached` | `debugger` | A debugger is attached to the process. |
|
|
229
|
+
|
|
230
|
+
### Android
|
|
231
|
+
|
|
232
|
+
| id | category | Checks |
|
|
233
|
+
| --- | --- | --- |
|
|
234
|
+
| `emulator` | `emulator` | Device appears to be an emulator. |
|
|
235
|
+
| `root_su_binary` | `root` | A privileged elevation binary was found. |
|
|
236
|
+
| `root_management_apps` | `root` | A root or privilege-management app is installed. |
|
|
237
|
+
| `root_magisk_files` | `root` | Root framework artifacts were detected. |
|
|
238
|
+
| `root_test_keys` | `root` | Build was signed with test keys. |
|
|
239
|
+
| `root_dangerous_props` | `root` | System security properties indicate an insecure build. |
|
|
240
|
+
| `root_rw_system` | `root` | A system partition is mounted read-write. |
|
|
241
|
+
| `hooking_frida` | `hooking` | Instrumentation framework artifacts were detected. |
|
|
242
|
+
| `hooking_xposed` | `hooking` | Code-injection framework artifacts were detected. |
|
|
243
|
+
| `debugger_attached` | `debugger` | A debugger is attached to the process. |
|
|
244
|
+
|
|
245
|
+
### Notes
|
|
246
|
+
|
|
247
|
+
- On the **iOS Simulator**, filesystem / URL-scheme / symlink / writable-system and `DYLD_INSERT_LIBRARIES` checks are **skipped** (they would inspect the host Mac). Skipped-by-design is not incomplete; hooking-library and debugger checks still run, plus the `simulator` signal.
|
|
248
|
+
- `debugger_attached` fires whenever a debugger is attached, including during normal Xcode / Android Studio debugging.
|
|
249
|
+
- Android emulators still run root checks (they may report root-related signals independently of `emulator`).
|
|
250
|
+
|
|
251
|
+
## Limitations & threat model
|
|
252
|
+
|
|
253
|
+
These are **client-side risk indicators**. They can be bypassed by an attacker who controls the device, for example:
|
|
254
|
+
|
|
255
|
+
- Frida / objection hooking this module
|
|
256
|
+
- Rootless jailbreaks with hiding tweaks
|
|
257
|
+
- Magisk DenyList / Zygisk / Shamiko
|
|
258
|
+
- KernelSU hiding
|
|
259
|
+
|
|
260
|
+
Never rely on them alone for security decisions. For high-assurance flows, use **Apple App Attest / DeviceCheck** and the **Google Play Integrity API** server-side.
|
|
261
|
+
|
|
262
|
+
**Possible false positives:** custom ROMs, developer devices with test-keys builds, enterprise MDM environments.
|
|
263
|
+
|
|
264
|
+
**Store review:** no private APIs; no `QUERY_ALL_PACKAGES` (scoped `<queries>` list); no required-reason file-timestamp APIs; ships a [privacy manifest](ios/PrivacyInfo.xcprivacy) declaring no data collection.
|
|
265
|
+
|
|
266
|
+
## Privacy
|
|
267
|
+
|
|
268
|
+
Nothing leaves the device. No data collection. No network access. Checks run locally and return an in-process result only.
|
|
269
|
+
|
|
270
|
+
## Credits & prior art
|
|
271
|
+
|
|
272
|
+
- [OWASP MASVS / MASTG](https://mas.owasp.org/) (resilience / MASVS-RESILIENCE)
|
|
273
|
+
- [IOSSecuritySuite](https://github.com/securing/IOSSecuritySuite) by SecuRing (Wojciech Reguła) — Check categories and indicator lists were informed by version 1.9.11 and earlier, which are licensed BSD-2-Clause. Versions 2.0.0 and later are distributed under a proprietary EULA and were not used. No source code was copied.
|
|
274
|
+
- [RootBeer](https://github.com/scottyab/rootbeer) by Scott Alexander-Bown and contributors — Apache-2.0 — check categories and indicator lists informed by; independent implementation, no source code copied.
|
|
275
|
+
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
279
|
+
|
|
280
|
+
## Security
|
|
281
|
+
|
|
282
|
+
See [SECURITY.md](SECURITY.md).
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.DeviceIntegrity = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
ext.getExtOrDefault = { prop ->
|
|
9
|
+
if (rootProject.ext.has(prop)) {
|
|
10
|
+
return rootProject.ext.get(prop)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return DeviceIntegrity[prop]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
repositories {
|
|
17
|
+
google()
|
|
18
|
+
mavenCentral()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
dependencies {
|
|
22
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
23
|
+
// noinspection DifferentKotlinGradleVersion
|
|
24
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
apply plugin: "com.android.library"
|
|
30
|
+
|
|
31
|
+
if (project.extensions.findByName("kotlin") == null) {
|
|
32
|
+
apply plugin: "kotlin-android"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
apply plugin: "com.facebook.react"
|
|
37
|
+
|
|
38
|
+
android {
|
|
39
|
+
namespace "com.deviceintegrity"
|
|
40
|
+
|
|
41
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
42
|
+
|
|
43
|
+
defaultConfig {
|
|
44
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
compileOptions {
|
|
48
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
49
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dependencies {
|
|
54
|
+
implementation "com.facebook.react:react-android"
|
|
55
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<queries>
|
|
3
|
+
<package android:name="com.topjohnwu.magisk" />
|
|
4
|
+
<package android:name="io.github.huskydg.magisk" />
|
|
5
|
+
<package android:name="eu.chainfire.supersu" />
|
|
6
|
+
<package android:name="com.koushikdutta.superuser" />
|
|
7
|
+
<package android:name="com.noshufou.android.su" />
|
|
8
|
+
<package android:name="com.thirdparty.superuser" />
|
|
9
|
+
<package android:name="com.yellowes.su" />
|
|
10
|
+
<package android:name="com.kingroot.kinguser" />
|
|
11
|
+
<package android:name="com.kingo.root" />
|
|
12
|
+
<package android:name="com.zhiqupk.root.global" />
|
|
13
|
+
<package android:name="com.smedialink.oneclickroot" />
|
|
14
|
+
<package android:name="me.weishu.kernelsu" />
|
|
15
|
+
<package android:name="me.bmax.apatch" />
|
|
16
|
+
<package android:name="com.devadvance.rootcloak" />
|
|
17
|
+
<package android:name="de.robv.android.xposed.installer" />
|
|
18
|
+
<package android:name="org.lsposed.manager" />
|
|
19
|
+
</queries>
|
|
20
|
+
</manifest>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.deviceintegrity
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.Promise
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.WritableArray
|
|
7
|
+
import com.facebook.react.bridge.WritableMap
|
|
8
|
+
import java.util.concurrent.Executors
|
|
9
|
+
|
|
10
|
+
class DeviceIntegrityModule(reactContext: ReactApplicationContext) :
|
|
11
|
+
NativeDeviceIntegritySpec(reactContext) {
|
|
12
|
+
|
|
13
|
+
override fun checkIntegrity(promise: Promise) {
|
|
14
|
+
executor.execute {
|
|
15
|
+
try {
|
|
16
|
+
val report = IntegrityChecks(reactApplicationContext).run()
|
|
17
|
+
promise.resolve(toWritableMap(report))
|
|
18
|
+
} catch (t: Throwable) {
|
|
19
|
+
promise.reject("E_INTEGRITY", t.message ?: "Unexpected integrity check failure", t)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
companion object {
|
|
25
|
+
const val NAME = NativeDeviceIntegritySpec.NAME
|
|
26
|
+
|
|
27
|
+
private val executor = Executors.newSingleThreadExecutor { runnable ->
|
|
28
|
+
Thread(runnable, "DeviceIntegrityChecks").apply { isDaemon = true }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private fun toWritableMap(report: IntegrityChecks.Report): WritableMap {
|
|
32
|
+
val map = Arguments.createMap()
|
|
33
|
+
map.putBoolean("completed", report.completed)
|
|
34
|
+
report.reason?.let { map.putString("reason", it) }
|
|
35
|
+
|
|
36
|
+
val signals: WritableArray = Arguments.createArray()
|
|
37
|
+
for (signal in report.signals) {
|
|
38
|
+
val entry = Arguments.createMap()
|
|
39
|
+
entry.putString("id", signal.id)
|
|
40
|
+
entry.putString("category", signal.category)
|
|
41
|
+
entry.putString("description", signal.description)
|
|
42
|
+
signals.pushMap(entry)
|
|
43
|
+
}
|
|
44
|
+
map.putArray("signals", signals)
|
|
45
|
+
return map
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.deviceintegrity
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
9
|
+
|
|
10
|
+
class DeviceIntegrityPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == DeviceIntegrityModule.NAME) {
|
|
13
|
+
DeviceIntegrityModule(reactContext)
|
|
14
|
+
} else {
|
|
15
|
+
null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
DeviceIntegrityModule.NAME to ReactModuleInfo(
|
|
22
|
+
name = DeviceIntegrityModule.NAME,
|
|
23
|
+
className = DeviceIntegrityModule.NAME,
|
|
24
|
+
canOverrideExistingModule = false,
|
|
25
|
+
needsEagerInit = false,
|
|
26
|
+
isCxxModule = false,
|
|
27
|
+
isTurboModule = true
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|