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,395 @@
|
|
|
1
|
+
// Check set informed by IOSSecuritySuite and the OWASP MASTG; independent implementation.
|
|
2
|
+
|
|
3
|
+
#import "DIIntegrityChecks.h"
|
|
4
|
+
|
|
5
|
+
#import <UIKit/UIKit.h>
|
|
6
|
+
#import <mach-o/dyld.h>
|
|
7
|
+
#import <sys/sysctl.h>
|
|
8
|
+
#import <unistd.h>
|
|
9
|
+
|
|
10
|
+
@interface DIIntegrityChecks ()
|
|
11
|
+
+ (void)addSignalId:(NSString *)signalId
|
|
12
|
+
category:(NSString *)category
|
|
13
|
+
description:(NSString *)description
|
|
14
|
+
signals:(NSMutableArray<NSDictionary *> *)signals
|
|
15
|
+
seenIds:(NSMutableSet<NSString *> *)seenIds;
|
|
16
|
+
+ (BOOL)pathExists:(NSString *)path;
|
|
17
|
+
+ (BOOL)hasJailbreakFiles;
|
|
18
|
+
+ (BOOL)hasJailbreakURLSchemes;
|
|
19
|
+
+ (BOOL)hasJailbreakSymlinks;
|
|
20
|
+
+ (BOOL)isSystemWritable;
|
|
21
|
+
+ (BOOL)hasHookingLibraries;
|
|
22
|
+
+ (BOOL)hasDyldInsertLibraries;
|
|
23
|
+
+ (BOOL)isDebuggerAttached;
|
|
24
|
+
@end
|
|
25
|
+
|
|
26
|
+
@implementation DIIntegrityChecks
|
|
27
|
+
|
|
28
|
+
+ (void)addSignalId:(NSString *)signalId
|
|
29
|
+
category:(NSString *)category
|
|
30
|
+
description:(NSString *)description
|
|
31
|
+
signals:(NSMutableArray<NSDictionary *> *)signals
|
|
32
|
+
seenIds:(NSMutableSet<NSString *> *)seenIds
|
|
33
|
+
{
|
|
34
|
+
if ([seenIds containsObject:signalId]) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
[seenIds addObject:signalId];
|
|
38
|
+
[signals addObject:@{
|
|
39
|
+
@"id" : signalId,
|
|
40
|
+
@"category" : category,
|
|
41
|
+
@"description" : description,
|
|
42
|
+
}];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
+ (NSDictionary *)buildReportWithSignals:(NSArray *)signals completed:(BOOL)completed
|
|
46
|
+
{
|
|
47
|
+
NSMutableDictionary *report = [@{
|
|
48
|
+
@"completed" : @(completed),
|
|
49
|
+
@"signals" : signals,
|
|
50
|
+
} mutableCopy];
|
|
51
|
+
if (!completed) {
|
|
52
|
+
report[@"reason"] = @"incomplete";
|
|
53
|
+
}
|
|
54
|
+
return [report copy];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
+ (void)runWithCompletion:(void (^)(NSDictionary *report))completion
|
|
58
|
+
{
|
|
59
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
60
|
+
NSMutableArray<NSDictionary *> *signals = [NSMutableArray array];
|
|
61
|
+
NSMutableSet<NSString *> *seenIds = [NSMutableSet set];
|
|
62
|
+
__block BOOL completed = YES;
|
|
63
|
+
|
|
64
|
+
#if TARGET_OS_SIMULATOR
|
|
65
|
+
[self addSignalId:@"simulator"
|
|
66
|
+
category:@"emulator"
|
|
67
|
+
description:@"Running on the iOS Simulator"
|
|
68
|
+
signals:signals
|
|
69
|
+
seenIds:seenIds];
|
|
70
|
+
#endif
|
|
71
|
+
|
|
72
|
+
// --- hooking_libraries (always) ---
|
|
73
|
+
@try {
|
|
74
|
+
if ([self hasHookingLibraries]) {
|
|
75
|
+
[self addSignalId:@"hooking_libraries"
|
|
76
|
+
category:@"hooking"
|
|
77
|
+
description:@"Suspicious dynamic libraries are loaded"
|
|
78
|
+
signals:signals
|
|
79
|
+
seenIds:seenIds];
|
|
80
|
+
}
|
|
81
|
+
} @catch (__unused NSException *exception) {
|
|
82
|
+
completed = NO;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --- debugger_attached (always) ---
|
|
86
|
+
@try {
|
|
87
|
+
if ([self isDebuggerAttached]) {
|
|
88
|
+
[self addSignalId:@"debugger_attached"
|
|
89
|
+
category:@"debugger"
|
|
90
|
+
description:@"A debugger is attached to the process"
|
|
91
|
+
signals:signals
|
|
92
|
+
seenIds:seenIds];
|
|
93
|
+
}
|
|
94
|
+
} @catch (__unused NSException *exception) {
|
|
95
|
+
completed = NO;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#if TARGET_OS_SIMULATOR
|
|
99
|
+
// Simulator: host Mac filesystem and Xcode-injected DYLD_INSERT_LIBRARIES
|
|
100
|
+
// make jailbreak / dyld-insert checks meaningless — skip by design.
|
|
101
|
+
// Skipped-by-design is not incomplete.
|
|
102
|
+
completion([self buildReportWithSignals:[signals copy] completed:completed]);
|
|
103
|
+
#else
|
|
104
|
+
@try {
|
|
105
|
+
if ([self hasJailbreakFiles]) {
|
|
106
|
+
[self addSignalId:@"jailbreak_files"
|
|
107
|
+
category:@"jailbreak"
|
|
108
|
+
description:@"Unexpected system paths associated with jailbreaks were found"
|
|
109
|
+
signals:signals
|
|
110
|
+
seenIds:seenIds];
|
|
111
|
+
}
|
|
112
|
+
} @catch (__unused NSException *exception) {
|
|
113
|
+
completed = NO;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@try {
|
|
117
|
+
if ([self hasJailbreakSymlinks]) {
|
|
118
|
+
[self addSignalId:@"jailbreak_symlinks"
|
|
119
|
+
category:@"jailbreak"
|
|
120
|
+
description:@"Unexpected symbolic links were found in system locations"
|
|
121
|
+
signals:signals
|
|
122
|
+
seenIds:seenIds];
|
|
123
|
+
}
|
|
124
|
+
} @catch (__unused NSException *exception) {
|
|
125
|
+
completed = NO;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@try {
|
|
129
|
+
if ([self isSystemWritable]) {
|
|
130
|
+
[self addSignalId:@"jailbreak_writable_system"
|
|
131
|
+
category:@"jailbreak"
|
|
132
|
+
description:@"A normally read-only system location is writable"
|
|
133
|
+
signals:signals
|
|
134
|
+
seenIds:seenIds];
|
|
135
|
+
}
|
|
136
|
+
} @catch (__unused NSException *exception) {
|
|
137
|
+
completed = NO;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@try {
|
|
141
|
+
if ([self hasDyldInsertLibraries]) {
|
|
142
|
+
[self addSignalId:@"hooking_dyld_insert"
|
|
143
|
+
category:@"hooking"
|
|
144
|
+
description:@"DYLD_INSERT_LIBRARIES is set in the process environment"
|
|
145
|
+
signals:signals
|
|
146
|
+
seenIds:seenIds];
|
|
147
|
+
}
|
|
148
|
+
} @catch (__unused NSException *exception) {
|
|
149
|
+
completed = NO;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// canOpenURL: must run on the main thread.
|
|
153
|
+
// These schemes need LSApplicationQueriesSchemes (added by the Expo config
|
|
154
|
+
// plugin in Phase 3 / documented for bare apps); without it canOpenURL
|
|
155
|
+
// just returns NO.
|
|
156
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
157
|
+
@try {
|
|
158
|
+
if ([self hasJailbreakURLSchemes]) {
|
|
159
|
+
[self addSignalId:@"jailbreak_url_schemes"
|
|
160
|
+
category:@"jailbreak"
|
|
161
|
+
description:@"Jailbreak-related URL schemes are registered"
|
|
162
|
+
signals:signals
|
|
163
|
+
seenIds:seenIds];
|
|
164
|
+
}
|
|
165
|
+
} @catch (__unused NSException *exception) {
|
|
166
|
+
completed = NO;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
completion([self buildReportWithSignals:[signals copy] completed:completed]);
|
|
170
|
+
});
|
|
171
|
+
#endif
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
#pragma mark - Individual checks
|
|
176
|
+
|
|
177
|
+
+ (BOOL)pathExists:(NSString *)path
|
|
178
|
+
{
|
|
179
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
|
180
|
+
return YES;
|
|
181
|
+
}
|
|
182
|
+
const char *cPath = [path fileSystemRepresentation];
|
|
183
|
+
if (cPath == NULL) {
|
|
184
|
+
return NO;
|
|
185
|
+
}
|
|
186
|
+
return access(cPath, F_OK) == 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
+ (BOOL)hasJailbreakFiles
|
|
190
|
+
{
|
|
191
|
+
static NSArray<NSString *> *paths = nil;
|
|
192
|
+
static dispatch_once_t onceToken;
|
|
193
|
+
dispatch_once(&onceToken, ^{
|
|
194
|
+
paths = @[
|
|
195
|
+
@"/Applications/Cydia.app",
|
|
196
|
+
@"/Applications/Sileo.app",
|
|
197
|
+
@"/Applications/Zebra.app",
|
|
198
|
+
@"/Applications/Filza.app",
|
|
199
|
+
@"/Applications/Installer.app",
|
|
200
|
+
@"/Applications/Undecimus.app",
|
|
201
|
+
@"/Applications/blackra1n.app",
|
|
202
|
+
@"/Applications/FakeCarrier.app",
|
|
203
|
+
@"/Applications/Icy.app",
|
|
204
|
+
@"/Applications/IntelliScreen.app",
|
|
205
|
+
@"/Applications/MxTube.app",
|
|
206
|
+
@"/Applications/RockApp.app",
|
|
207
|
+
@"/Applications/SBSettings.app",
|
|
208
|
+
@"/Applications/WinterBoard.app",
|
|
209
|
+
@"/Library/MobileSubstrate/MobileSubstrate.dylib",
|
|
210
|
+
@"/Library/MobileSubstrate/DynamicLibraries",
|
|
211
|
+
@"/usr/lib/libsubstrate.dylib",
|
|
212
|
+
@"/usr/lib/substitute-inserter.dylib",
|
|
213
|
+
@"/usr/lib/libhooker.dylib",
|
|
214
|
+
@"/usr/lib/TweakInject",
|
|
215
|
+
@"/usr/lib/libellekit.dylib",
|
|
216
|
+
@"/usr/lib/ellekit/libinjector.dylib",
|
|
217
|
+
@"/private/var/lib/apt",
|
|
218
|
+
@"/private/var/lib/apt/",
|
|
219
|
+
@"/private/var/lib/cydia",
|
|
220
|
+
@"/private/var/mobile/Library/SBSettings/Themes",
|
|
221
|
+
@"/private/var/stash",
|
|
222
|
+
@"/private/var/tmp/cydia.log",
|
|
223
|
+
@"/private/var/db/stash",
|
|
224
|
+
@"/usr/sbin/sshd",
|
|
225
|
+
@"/usr/bin/ssh",
|
|
226
|
+
@"/usr/bin/sshd",
|
|
227
|
+
@"/usr/libexec/cydia",
|
|
228
|
+
@"/usr/libexec/sftp-server",
|
|
229
|
+
@"/usr/libexec/ssh-keysign",
|
|
230
|
+
@"/bin/bash",
|
|
231
|
+
@"/etc/apt",
|
|
232
|
+
@"/etc/ssh/sshd_config",
|
|
233
|
+
@"/var/jb",
|
|
234
|
+
@"/var/binpack",
|
|
235
|
+
@"/var/checkra1n.dmg",
|
|
236
|
+
@"/.bootstrapped",
|
|
237
|
+
@"/.installed_unc0ver",
|
|
238
|
+
@"/.cydia_no_stash",
|
|
239
|
+
@"/jb/amfid_payload.dylib",
|
|
240
|
+
@"/jb/jailbreakd.plist",
|
|
241
|
+
@"/jb/libjailbreak.dylib",
|
|
242
|
+
@"/chimera/jailbreakd.plist",
|
|
243
|
+
@"/odyssey/jailbreakd.plist",
|
|
244
|
+
];
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
for (NSString *path in paths) {
|
|
248
|
+
if ([self pathExists:path]) {
|
|
249
|
+
return YES;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return NO;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
+ (BOOL)hasJailbreakURLSchemes
|
|
256
|
+
{
|
|
257
|
+
// Requires LSApplicationQueriesSchemes entries; without them canOpenURL
|
|
258
|
+
// returns NO even when the target app is installed.
|
|
259
|
+
static NSArray<NSString *> *schemes = nil;
|
|
260
|
+
static dispatch_once_t onceToken;
|
|
261
|
+
dispatch_once(&onceToken, ^{
|
|
262
|
+
schemes = @[
|
|
263
|
+
@"cydia://",
|
|
264
|
+
@"sileo://",
|
|
265
|
+
@"zbra://",
|
|
266
|
+
@"filza://",
|
|
267
|
+
@"undecimus://",
|
|
268
|
+
@"activator://",
|
|
269
|
+
];
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
UIApplication *app = [UIApplication sharedApplication];
|
|
273
|
+
for (NSString *scheme in schemes) {
|
|
274
|
+
NSURL *url = [NSURL URLWithString:scheme];
|
|
275
|
+
if (url != nil && [app canOpenURL:url]) {
|
|
276
|
+
return YES;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return NO;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
+ (BOOL)hasJailbreakSymlinks
|
|
283
|
+
{
|
|
284
|
+
static NSArray<NSString *> *paths = nil;
|
|
285
|
+
static dispatch_once_t onceToken;
|
|
286
|
+
dispatch_once(&onceToken, ^{
|
|
287
|
+
paths = @[
|
|
288
|
+
@"/Applications",
|
|
289
|
+
@"/Library/Ringtones",
|
|
290
|
+
@"/Library/Wallpaper",
|
|
291
|
+
@"/usr/arm-apple-darwin9",
|
|
292
|
+
@"/usr/include",
|
|
293
|
+
@"/usr/libexec",
|
|
294
|
+
@"/usr/share",
|
|
295
|
+
];
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
NSFileManager *fm = [NSFileManager defaultManager];
|
|
299
|
+
for (NSString *path in paths) {
|
|
300
|
+
NSError *error = nil;
|
|
301
|
+
NSString *destination =
|
|
302
|
+
[fm destinationOfSymbolicLinkAtPath:path error:&error];
|
|
303
|
+
if (destination != nil) {
|
|
304
|
+
return YES;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return NO;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
+ (BOOL)isSystemWritable
|
|
311
|
+
{
|
|
312
|
+
NSString *probeName =
|
|
313
|
+
[NSString stringWithFormat:@"di_integrity_%@", [[NSUUID UUID] UUIDString]];
|
|
314
|
+
NSString *probePath = [@"/private/" stringByAppendingPathComponent:probeName];
|
|
315
|
+
const char *cPath = [probePath fileSystemRepresentation];
|
|
316
|
+
if (cPath == NULL) {
|
|
317
|
+
return NO;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
FILE *file = fopen(cPath, "w");
|
|
321
|
+
if (file == NULL) {
|
|
322
|
+
return NO;
|
|
323
|
+
}
|
|
324
|
+
fputs("x", file);
|
|
325
|
+
fclose(file);
|
|
326
|
+
unlink(cPath);
|
|
327
|
+
return YES;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
+ (BOOL)hasHookingLibraries
|
|
331
|
+
{
|
|
332
|
+
static NSArray<NSString *> *needles = nil;
|
|
333
|
+
static dispatch_once_t onceToken;
|
|
334
|
+
dispatch_once(&onceToken, ^{
|
|
335
|
+
needles = @[
|
|
336
|
+
@"frida",
|
|
337
|
+
@"fridagadget",
|
|
338
|
+
@"cynject",
|
|
339
|
+
@"libcycript",
|
|
340
|
+
@"mobilesubstrate",
|
|
341
|
+
@"substrateloader",
|
|
342
|
+
@"substrateinserter",
|
|
343
|
+
@"substitute",
|
|
344
|
+
@"libhooker",
|
|
345
|
+
@"ellekit",
|
|
346
|
+
@"tweakinject",
|
|
347
|
+
@"sslkillswitch",
|
|
348
|
+
@"revealserver",
|
|
349
|
+
@"systemhook",
|
|
350
|
+
];
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
uint32_t count = _dyld_image_count();
|
|
354
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
355
|
+
const char *imageName = _dyld_get_image_name(i);
|
|
356
|
+
if (imageName == NULL) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
NSString *name =
|
|
360
|
+
[[NSString stringWithUTF8String:imageName] lowercaseString];
|
|
361
|
+
if (name == nil) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
for (NSString *needle in needles) {
|
|
365
|
+
if ([name rangeOfString:needle].location != NSNotFound) {
|
|
366
|
+
return YES;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return NO;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
+ (BOOL)hasDyldInsertLibraries
|
|
374
|
+
{
|
|
375
|
+
const char *value = getenv("DYLD_INSERT_LIBRARIES");
|
|
376
|
+
if (value == NULL) {
|
|
377
|
+
return NO;
|
|
378
|
+
}
|
|
379
|
+
return value[0] != '\0';
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
+ (BOOL)isDebuggerAttached
|
|
383
|
+
{
|
|
384
|
+
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
|
|
385
|
+
struct kinfo_proc info;
|
|
386
|
+
memset(&info, 0, sizeof(info));
|
|
387
|
+
size_t size = sizeof(info);
|
|
388
|
+
|
|
389
|
+
if (sysctl(mib, 4, &info, &size, NULL, 0) != 0) {
|
|
390
|
+
return NO;
|
|
391
|
+
}
|
|
392
|
+
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#import "DeviceIntegrity.h"
|
|
2
|
+
#import "DIIntegrityChecks.h"
|
|
3
|
+
|
|
4
|
+
@implementation DeviceIntegrity
|
|
5
|
+
|
|
6
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
7
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
8
|
+
{
|
|
9
|
+
return std::make_shared<facebook::react::NativeDeviceIntegritySpecJSI>(params);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
+ (NSString *)moduleName
|
|
13
|
+
{
|
|
14
|
+
return @"DeviceIntegrity";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
- (void)checkIntegrity:(RCTPromiseResolveBlock)resolve
|
|
18
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
19
|
+
{
|
|
20
|
+
@try {
|
|
21
|
+
[DIIntegrityChecks runWithCompletion:^(NSDictionary *report) {
|
|
22
|
+
resolve(report);
|
|
23
|
+
}];
|
|
24
|
+
} @catch (NSException *exception) {
|
|
25
|
+
reject(@"E_INTEGRITY", exception.reason ?: @"Unexpected integrity check failure", nil);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@end
|
|
@@ -0,0 +1,14 @@
|
|
|
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>NSPrivacyTracking</key>
|
|
6
|
+
<false/>
|
|
7
|
+
<key>NSPrivacyTrackingDomains</key>
|
|
8
|
+
<array/>
|
|
9
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
10
|
+
<array/>
|
|
11
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
12
|
+
<array/>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativeDeviceIntegrity.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAkBpE,eAAeA,mBAAmB,CAACC,GAAG,CAAO,iBAAiB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
/**
|
|
5
|
+
* Web / unsupported-platform entry. Must not import the native TurboModule.
|
|
6
|
+
*/
|
|
7
|
+
export async function checkIntegrity(_options) {
|
|
8
|
+
return {
|
|
9
|
+
status: 'unknown',
|
|
10
|
+
signals: [],
|
|
11
|
+
platform: Platform.OS,
|
|
12
|
+
reason: 'unsupported_platform'
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=checkIntegrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Platform","checkIntegrity","_options","status","signals","platform","OS","reason"],"sourceRoot":"../../src","sources":["checkIntegrity.ts"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AAGvC;AACA;AACA;AACA,OAAO,eAAeC,cAAcA,CAClCC,QAAgC,EACN;EAC1B,OAAO;IACLC,MAAM,EAAE,SAAS;IACjBC,OAAO,EAAE,EAAE;IACXC,QAAQ,EAAEL,QAAQ,CAACM,EAAE;IACrBC,MAAM,EAAE;EACV,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
import NativeDeviceIntegrity from "./NativeDeviceIntegrity.js";
|
|
5
|
+
import { resolveStatus } from "./resolveStatus.js";
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
7
|
+
class IntegrityTimeoutError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('Device integrity check timed out');
|
|
10
|
+
this.name = 'IntegrityTimeoutError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function withTimeout(promise, timeoutMs) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const timer = setTimeout(() => {
|
|
16
|
+
reject(new IntegrityTimeoutError());
|
|
17
|
+
}, timeoutMs);
|
|
18
|
+
promise.then(value => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
resolve(value);
|
|
21
|
+
}, error => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
reject(error);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function checkIntegrity(options = {}) {
|
|
28
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
29
|
+
return {
|
|
30
|
+
status: 'unknown',
|
|
31
|
+
signals: [],
|
|
32
|
+
platform: Platform.OS,
|
|
33
|
+
reason: 'unsupported_platform'
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (NativeDeviceIntegrity == null) {
|
|
37
|
+
return {
|
|
38
|
+
status: 'unknown',
|
|
39
|
+
signals: [],
|
|
40
|
+
platform: Platform.OS,
|
|
41
|
+
reason: 'native_module_unavailable'
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
45
|
+
try {
|
|
46
|
+
const report = await withTimeout(NativeDeviceIntegrity.checkIntegrity(), timeoutMs);
|
|
47
|
+
return resolveStatus(report, options, Platform.OS);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof IntegrityTimeoutError) {
|
|
50
|
+
return {
|
|
51
|
+
status: 'unknown',
|
|
52
|
+
signals: [],
|
|
53
|
+
platform: Platform.OS,
|
|
54
|
+
reason: 'timeout'
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : 'Native integrity check failed';
|
|
58
|
+
return {
|
|
59
|
+
status: 'unknown',
|
|
60
|
+
signals: [],
|
|
61
|
+
platform: Platform.OS,
|
|
62
|
+
reason: 'native_error',
|
|
63
|
+
error: message
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=checkIntegrity.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Platform","NativeDeviceIntegrity","resolveStatus","DEFAULT_TIMEOUT_MS","IntegrityTimeoutError","Error","constructor","name","withTimeout","promise","timeoutMs","Promise","resolve","reject","timer","setTimeout","then","value","clearTimeout","error","checkIntegrity","options","OS","status","signals","platform","reason","report","message"],"sourceRoot":"../../src","sources":["checkIntegrity.native.ts"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,qBAAqB,MAAM,4BAAyB;AAC3D,SAASC,aAAa,QAAQ,oBAAiB;AAG/C,MAAMC,kBAAkB,GAAG,MAAM;AAEjC,MAAMC,qBAAqB,SAASC,KAAK,CAAC;EACxCC,WAAWA,CAAA,EAAG;IACZ,KAAK,CAAC,kCAAkC,CAAC;IACzC,IAAI,CAACC,IAAI,GAAG,uBAAuB;EACrC;AACF;AAEA,SAASC,WAAWA,CAAIC,OAAmB,EAAEC,SAAiB,EAAc;EAC1E,OAAO,IAAIC,OAAO,CAAI,CAACC,OAAO,EAAEC,MAAM,KAAK;IACzC,MAAMC,KAAK,GAAGC,UAAU,CAAC,MAAM;MAC7BF,MAAM,CAAC,IAAIT,qBAAqB,CAAC,CAAC,CAAC;IACrC,CAAC,EAAEM,SAAS,CAAC;IAEbD,OAAO,CAACO,IAAI,CACTC,KAAK,IAAK;MACTC,YAAY,CAACJ,KAAK,CAAC;MACnBF,OAAO,CAACK,KAAK,CAAC;IAChB,CAAC,EACAE,KAAc,IAAK;MAClBD,YAAY,CAACJ,KAAK,CAAC;MACnBD,MAAM,CAACM,KAAK,CAAC;IACf,CACF,CAAC;EACH,CAAC,CAAC;AACJ;AAEA,OAAO,eAAeC,cAAcA,CAClCC,OAA8B,GAAG,CAAC,CAAC,EACT;EAC1B,IAAIrB,QAAQ,CAACsB,EAAE,KAAK,KAAK,IAAItB,QAAQ,CAACsB,EAAE,KAAK,SAAS,EAAE;IACtD,OAAO;MACLC,MAAM,EAAE,SAAS;MACjBC,OAAO,EAAE,EAAE;MACXC,QAAQ,EAAEzB,QAAQ,CAACsB,EAAE;MACrBI,MAAM,EAAE;IACV,CAAC;EACH;EAEA,IAAIzB,qBAAqB,IAAI,IAAI,EAAE;IACjC,OAAO;MACLsB,MAAM,EAAE,SAAS;MACjBC,OAAO,EAAE,EAAE;MACXC,QAAQ,EAAEzB,QAAQ,CAACsB,EAAE;MACrBI,MAAM,EAAE;IACV,CAAC;EACH;EAEA,MAAMhB,SAAS,GAAGW,OAAO,CAACX,SAAS,IAAIP,kBAAkB;EAEzD,IAAI;IACF,MAAMwB,MAAM,GAAG,MAAMnB,WAAW,CAC9BP,qBAAqB,CAACmB,cAAc,CAAC,CAAC,EACtCV,SACF,CAAC;IACD,OAAOR,aAAa,CAACyB,MAAM,EAAEN,OAAO,EAAErB,QAAQ,CAACsB,EAAE,CAAC;EACpD,CAAC,CAAC,OAAOH,KAAK,EAAE;IACd,IAAIA,KAAK,YAAYf,qBAAqB,EAAE;MAC1C,OAAO;QACLmB,MAAM,EAAE,SAAS;QACjBC,OAAO,EAAE,EAAE;QACXC,QAAQ,EAAEzB,QAAQ,CAACsB,EAAE;QACrBI,MAAM,EAAE;MACV,CAAC;IACH;IAEA,MAAME,OAAO,GACXT,KAAK,YAAYd,KAAK,GAClBc,KAAK,CAACS,OAAO,GACb,OAAOT,KAAK,KAAK,QAAQ,GACvBA,KAAK,GACL,+BAA+B;IAEvC,OAAO;MACLI,MAAM,EAAE,SAAS;MACjBC,OAAO,EAAE,EAAE;MACXC,QAAQ,EAAEzB,QAAQ,CAACsB,EAAE;MACrBI,MAAM,EAAE,cAAc;MACtBP,KAAK,EAAES;IACT,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["checkIntegrity","useDeviceIntegrity"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,kBAAkB;AACjD,SAASC,kBAAkB,QAAQ,yBAAsB","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const SIGNAL_CATEGORIES = new Set(['jailbreak', 'root', 'hooking', 'debugger', 'emulator', 'tamper', 'environment']);
|
|
4
|
+
function coerceCategory(category) {
|
|
5
|
+
if (typeof category === 'string' && SIGNAL_CATEGORIES.has(category)) {
|
|
6
|
+
return category;
|
|
7
|
+
}
|
|
8
|
+
return 'environment';
|
|
9
|
+
}
|
|
10
|
+
function normalizeSignal(raw) {
|
|
11
|
+
if (raw === null || typeof raw !== 'object') {
|
|
12
|
+
return {
|
|
13
|
+
id: 'unknown_signal',
|
|
14
|
+
category: 'environment',
|
|
15
|
+
description: ''
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const signal = raw;
|
|
19
|
+
const id = typeof signal.id === 'string' && signal.id.length > 0 ? signal.id : 'unknown_signal';
|
|
20
|
+
const description = typeof signal.description === 'string' ? signal.description : '';
|
|
21
|
+
return {
|
|
22
|
+
id,
|
|
23
|
+
category: coerceCategory(signal.category),
|
|
24
|
+
description
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function malformed(platform, error) {
|
|
28
|
+
return {
|
|
29
|
+
status: 'unknown',
|
|
30
|
+
signals: [],
|
|
31
|
+
platform,
|
|
32
|
+
reason: 'native_error',
|
|
33
|
+
error
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Maps a native integrity report + options to the public IntegrityResult.
|
|
39
|
+
* Compromise evidence wins over incomplete runs; incomplete/malformed never become 'clean'.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveStatus(report, options = {}, platform) {
|
|
42
|
+
if (report === null || typeof report !== 'object' || Array.isArray(report)) {
|
|
43
|
+
return malformed(platform, 'Malformed native integrity report');
|
|
44
|
+
}
|
|
45
|
+
const nativeReport = report;
|
|
46
|
+
if (!Array.isArray(nativeReport.signals)) {
|
|
47
|
+
return malformed(platform, 'Malformed native integrity report');
|
|
48
|
+
}
|
|
49
|
+
const signals = nativeReport.signals.map(normalizeSignal);
|
|
50
|
+
const completed = nativeReport.completed === true;
|
|
51
|
+
const treatEmulatorAsCompromised = options.treatEmulatorAsCompromised === true;
|
|
52
|
+
const hasNonEmulatorSignal = signals.some(signal => signal.category !== 'emulator');
|
|
53
|
+
const hasEmulatorSignal = signals.some(signal => signal.category === 'emulator');
|
|
54
|
+
if (hasNonEmulatorSignal || hasEmulatorSignal && treatEmulatorAsCompromised) {
|
|
55
|
+
return {
|
|
56
|
+
status: 'compromised',
|
|
57
|
+
signals,
|
|
58
|
+
platform
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!completed) {
|
|
62
|
+
const reason = nativeReport.reason === 'not_implemented' ? 'not_implemented' : 'incomplete';
|
|
63
|
+
return {
|
|
64
|
+
status: 'unknown',
|
|
65
|
+
signals,
|
|
66
|
+
platform,
|
|
67
|
+
reason
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
status: 'clean',
|
|
72
|
+
signals,
|
|
73
|
+
platform
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=resolveStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SIGNAL_CATEGORIES","Set","coerceCategory","category","has","normalizeSignal","raw","id","description","signal","length","malformed","platform","error","status","signals","reason","resolveStatus","report","options","Array","isArray","nativeReport","map","completed","treatEmulatorAsCompromised","hasNonEmulatorSignal","some","hasEmulatorSignal"],"sourceRoot":"../../src","sources":["resolveStatus.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}
|