react-native-device-integrity 0.1.0 → 0.2.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/README.md +138 -11
- package/android/src/main/java/com/deviceintegrity/DeviceIntegrityModule.kt +68 -2
- package/android/src/main/java/com/deviceintegrity/IntegrityChecks.kt +5 -1
- package/android/src/main/java/com/deviceintegrity/TamperChecks.kt +158 -0
- package/ios/DIIntegrityChecks.h +7 -1
- package/ios/DIIntegrityChecks.m +9 -1
- package/ios/DITamperChecks.h +27 -0
- package/ios/DITamperChecks.m +304 -0
- package/ios/DeviceIntegrity.mm +56 -4
- package/jest/index.d.ts +39 -0
- package/jest/index.js +100 -0
- package/lib/module/NativeDeviceIntegrity.js.map +1 -1
- package/lib/module/checkIntegrity.js +2 -0
- package/lib/module/checkIntegrity.js.map +1 -1
- package/lib/module/checkIntegrity.native.js +83 -6
- package/lib/module/checkIntegrity.native.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/resolveStatus.js +50 -5
- package/lib/module/resolveStatus.js.map +1 -1
- package/lib/module/types.js +4 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDeviceIntegrity.js +21 -7
- package/lib/module/useDeviceIntegrity.js.map +1 -1
- package/lib/typescript/src/NativeDeviceIntegrity.d.ts +2 -2
- package/lib/typescript/src/NativeDeviceIntegrity.d.ts.map +1 -1
- package/lib/typescript/src/checkIntegrity.d.ts.map +1 -1
- package/lib/typescript/src/checkIntegrity.native.d.ts +6 -0
- package/lib/typescript/src/checkIntegrity.native.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/resolveStatus.d.ts +1 -0
- package/lib/typescript/src/resolveStatus.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +49 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useDeviceIntegrity.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/NativeDeviceIntegrity.ts +5 -2
- package/src/checkIntegrity.native.ts +112 -15
- package/src/checkIntegrity.ts +2 -0
- package/src/index.tsx +5 -0
- package/src/resolveStatus.ts +69 -12
- package/src/types.ts +76 -2
- package/src/useDeviceIntegrity.ts +22 -7
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["checkIntegrity","useDeviceIntegrity"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,kBAAkB;AACjD,SAASC,kBAAkB,QAAQ,yBAAsB","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["checkIntegrity","useDeviceIntegrity","SIGNAL_IDS"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,kBAAkB;AACjD,SAASC,kBAAkB,QAAQ,yBAAsB;AACzD,SAASC,UAAU,QAAQ,YAAS","ignoreList":[]}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const SIGNAL_CATEGORIES = new Set(['jailbreak', 'root', 'hooking', 'debugger', 'emulator', 'tamper', 'environment']);
|
|
4
|
+
|
|
5
|
+
/** Default compromising set: every category except emulator. */
|
|
6
|
+
const DEFAULT_COMPROMISING_CATEGORIES = ['jailbreak', 'root', 'hooking', 'debugger', 'tamper', 'environment'];
|
|
4
7
|
function coerceCategory(category) {
|
|
5
8
|
if (typeof category === 'string' && SIGNAL_CATEGORIES.has(category)) {
|
|
6
9
|
return category;
|
|
@@ -24,10 +27,42 @@ function normalizeSignal(raw) {
|
|
|
24
27
|
description
|
|
25
28
|
};
|
|
26
29
|
}
|
|
30
|
+
function resolveCompromisingCategories(options) {
|
|
31
|
+
const base = options.policy?.compromisingCategories != null ? options.policy.compromisingCategories : DEFAULT_COMPROMISING_CATEGORIES;
|
|
32
|
+
const set = new Set(base);
|
|
33
|
+
if (options.treatEmulatorAsCompromised === true) {
|
|
34
|
+
set.add('emulator');
|
|
35
|
+
}
|
|
36
|
+
return set;
|
|
37
|
+
}
|
|
38
|
+
function partitionIgnored(signals, ignore) {
|
|
39
|
+
if (ignore == null || ignore.length === 0) {
|
|
40
|
+
return {
|
|
41
|
+
active: signals,
|
|
42
|
+
ignored: []
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const ignoreSet = new Set(ignore);
|
|
46
|
+
const active = [];
|
|
47
|
+
const ignored = [];
|
|
48
|
+
for (const signal of signals) {
|
|
49
|
+
if (ignoreSet.has(signal.id)) {
|
|
50
|
+
ignored.push(signal);
|
|
51
|
+
} else {
|
|
52
|
+
active.push(signal);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
active,
|
|
57
|
+
ignored
|
|
58
|
+
};
|
|
59
|
+
}
|
|
27
60
|
function malformed(platform, error) {
|
|
28
61
|
return {
|
|
29
62
|
status: 'unknown',
|
|
30
63
|
signals: [],
|
|
64
|
+
ignored: [],
|
|
65
|
+
durationMs: 0,
|
|
31
66
|
platform,
|
|
32
67
|
reason: 'native_error',
|
|
33
68
|
error
|
|
@@ -37,6 +72,7 @@ function malformed(platform, error) {
|
|
|
37
72
|
/**
|
|
38
73
|
* Maps a native integrity report + options to the public IntegrityResult.
|
|
39
74
|
* Compromise evidence wins over incomplete runs; incomplete/malformed never become 'clean'.
|
|
75
|
+
* `durationMs` is set to 0 here — `checkIntegrity` overwrites with measured wall time.
|
|
40
76
|
*/
|
|
41
77
|
export function resolveStatus(report, options = {}, platform) {
|
|
42
78
|
if (report === null || typeof report !== 'object' || Array.isArray(report)) {
|
|
@@ -46,15 +82,20 @@ export function resolveStatus(report, options = {}, platform) {
|
|
|
46
82
|
if (!Array.isArray(nativeReport.signals)) {
|
|
47
83
|
return malformed(platform, 'Malformed native integrity report');
|
|
48
84
|
}
|
|
49
|
-
const
|
|
85
|
+
const allSignals = nativeReport.signals.map(normalizeSignal);
|
|
86
|
+
const {
|
|
87
|
+
active: signals,
|
|
88
|
+
ignored
|
|
89
|
+
} = partitionIgnored(allSignals, options.ignore);
|
|
50
90
|
const completed = nativeReport.completed === true;
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (hasNonEmulatorSignal || hasEmulatorSignal && treatEmulatorAsCompromised) {
|
|
91
|
+
const compromising = resolveCompromisingCategories(options);
|
|
92
|
+
const hasCompromisingSignal = signals.some(signal => compromising.has(signal.category));
|
|
93
|
+
if (hasCompromisingSignal) {
|
|
55
94
|
return {
|
|
56
95
|
status: 'compromised',
|
|
57
96
|
signals,
|
|
97
|
+
ignored,
|
|
98
|
+
durationMs: 0,
|
|
58
99
|
platform
|
|
59
100
|
};
|
|
60
101
|
}
|
|
@@ -63,6 +104,8 @@ export function resolveStatus(report, options = {}, platform) {
|
|
|
63
104
|
return {
|
|
64
105
|
status: 'unknown',
|
|
65
106
|
signals,
|
|
107
|
+
ignored,
|
|
108
|
+
durationMs: 0,
|
|
66
109
|
platform,
|
|
67
110
|
reason
|
|
68
111
|
};
|
|
@@ -70,6 +113,8 @@ export function resolveStatus(report, options = {}, platform) {
|
|
|
70
113
|
return {
|
|
71
114
|
status: 'clean',
|
|
72
115
|
signals,
|
|
116
|
+
ignored,
|
|
117
|
+
durationMs: 0,
|
|
73
118
|
platform
|
|
74
119
|
};
|
|
75
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["SIGNAL_CATEGORIES","Set","coerceCategory","category","has","normalizeSignal","raw","id","description","signal","length","malformed","platform","error","status","
|
|
1
|
+
{"version":3,"names":["SIGNAL_CATEGORIES","Set","DEFAULT_COMPROMISING_CATEGORIES","coerceCategory","category","has","normalizeSignal","raw","id","description","signal","length","resolveCompromisingCategories","options","base","policy","compromisingCategories","set","treatEmulatorAsCompromised","add","partitionIgnored","signals","ignore","active","ignored","ignoreSet","push","malformed","platform","error","status","durationMs","reason","resolveStatus","report","Array","isArray","nativeReport","allSignals","map","completed","compromising","hasCompromisingSignal","some"],"sourceRoot":"../../src","sources":["resolveStatus.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/module/types.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Stable public signal ids. Unknown future ids remain accepted on `Signal.id`.
|
|
5
|
+
*/
|
|
6
|
+
export const SIGNAL_IDS = ['simulator', 'jailbreak_files', 'jailbreak_url_schemes', 'jailbreak_symlinks', 'jailbreak_writable_system', 'hooking_libraries', 'hooking_dyld_insert', 'debugger_attached', 'emulator', 'root_su_binary', 'root_management_apps', 'root_magisk_files', 'root_test_keys', 'root_dangerous_props', 'root_rw_system', 'hooking_frida', 'hooking_xposed', 'tamper_signature_mismatch', 'tamper_untrusted_installer', 'tamper_binary_decrypted', 'tamper_team_id_mismatch'];
|
|
4
7
|
//# sourceMappingURL=types.js.map
|
package/lib/module/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["SIGNAL_IDS"],"sourceRoot":"../../src","sources":["types.ts"],"mappings":";;AAaA;AACA;AACA;AACA,OAAO,MAAMA,UAAU,GAAG,CACxB,WAAW,EACX,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,2BAA2B,EAC3B,4BAA4B,EAC5B,yBAAyB,EACzB,yBAAyB,CACjB","ignoreList":[]}
|
|
@@ -3,9 +3,24 @@
|
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { AppState } from 'react-native';
|
|
5
5
|
import { checkIntegrity } from './checkIntegrity';
|
|
6
|
+
/**
|
|
7
|
+
* Stable serialization of options that affect the check. Inline object literals
|
|
8
|
+
* with the same values must not retrigger the effect.
|
|
9
|
+
*/
|
|
10
|
+
function optionsKey(options) {
|
|
11
|
+
return JSON.stringify({
|
|
12
|
+
treatEmulatorAsCompromised: options.treatEmulatorAsCompromised === true,
|
|
13
|
+
timeoutMs: options.timeoutMs,
|
|
14
|
+
ignore: options.ignore,
|
|
15
|
+
policy: options.policy,
|
|
16
|
+
android: options.android,
|
|
17
|
+
ios: options.ios
|
|
18
|
+
});
|
|
19
|
+
}
|
|
6
20
|
export function useDeviceIntegrity(options = {}) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
21
|
+
const key = optionsKey(options);
|
|
22
|
+
const optionsRef = useRef(options);
|
|
23
|
+
optionsRef.current = options;
|
|
9
24
|
const [result, setResult] = useState(null);
|
|
10
25
|
const [loading, setLoading] = useState(true);
|
|
11
26
|
const requestIdRef = useRef(0);
|
|
@@ -14,16 +29,15 @@ export function useDeviceIntegrity(options = {}) {
|
|
|
14
29
|
const run = useCallback(async () => {
|
|
15
30
|
const requestId = ++requestIdRef.current;
|
|
16
31
|
setLoading(true);
|
|
17
|
-
const next = await checkIntegrity(
|
|
18
|
-
treatEmulatorAsCompromised,
|
|
19
|
-
timeoutMs
|
|
20
|
-
});
|
|
32
|
+
const next = await checkIntegrity(optionsRef.current);
|
|
21
33
|
if (mountedRef.current && requestId === requestIdRef.current) {
|
|
22
34
|
setResult(next);
|
|
23
35
|
setLoading(false);
|
|
24
36
|
}
|
|
25
37
|
return next;
|
|
26
|
-
|
|
38
|
+
// `key` is the by-value options fingerprint (not used in the body; optionsRef is).
|
|
39
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- equal-by-value options
|
|
40
|
+
}, [key]);
|
|
27
41
|
useEffect(() => {
|
|
28
42
|
mountedRef.current = true;
|
|
29
43
|
run().catch(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useCallback","useEffect","useRef","useState","AppState","checkIntegrity","
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useRef","useState","AppState","checkIntegrity","optionsKey","options","JSON","stringify","treatEmulatorAsCompromised","timeoutMs","ignore","policy","android","ios","useDeviceIntegrity","key","optionsRef","current","result","setResult","loading","setLoading","requestIdRef","mountedRef","appStateRef","currentState","run","requestId","next","catch","onChange","nextState","previous","subscription","addEventListener","remove","refresh","status","signals"],"sourceRoot":"../../src","sources":["useDeviceIntegrity.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CodegenTypes, TurboModule } from 'react-native';
|
|
2
2
|
export type NativeSignal = {
|
|
3
3
|
id: string;
|
|
4
4
|
category: string;
|
|
@@ -10,7 +10,7 @@ export type NativeIntegrityReport = {
|
|
|
10
10
|
signals: NativeSignal[];
|
|
11
11
|
};
|
|
12
12
|
export interface Spec extends TurboModule {
|
|
13
|
-
checkIntegrity(): Promise<NativeIntegrityReport>;
|
|
13
|
+
checkIntegrity(options: CodegenTypes.UnsafeObject): Promise<NativeIntegrityReport>;
|
|
14
14
|
}
|
|
15
15
|
declare const _default: Spec | null | undefined;
|
|
16
16
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeDeviceIntegrity.d.ts","sourceRoot":"","sources":["../../../src/NativeDeviceIntegrity.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"NativeDeviceIntegrity.d.ts","sourceRoot":"","sources":["../../../src/NativeDeviceIntegrity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,cAAc,CACZ,OAAO,EAAE,YAAY,CAAC,YAAY,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACnC;;AAED,wBAAgE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkIntegrity.d.ts","sourceRoot":"","sources":["../../../src/checkIntegrity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAS,CAAC;AAEtE;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"checkIntegrity.d.ts","sourceRoot":"","sources":["../../../src/checkIntegrity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAS,CAAC;AAEtE;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,OAAO,CAAC,eAAe,CAAC,CAS1B"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import type { CheckIntegrityOptions, IntegrityResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build the native options bag: only `android` / `ios`, with sanitized fields.
|
|
4
|
+
* Missing fields are omitted. A list that is provided but unusable (wrong type,
|
|
5
|
+
* no strings) is sent as a blank entry so the check fails closed as incomplete.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizeNativeOptions(options: CheckIntegrityOptions): Record<string, unknown>;
|
|
2
8
|
export declare function checkIntegrity(options?: CheckIntegrityOptions): Promise<IntegrityResult>;
|
|
3
9
|
//# sourceMappingURL=checkIntegrity.native.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkIntegrity.native.d.ts","sourceRoot":"","sources":["../../../src/checkIntegrity.native.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"checkIntegrity.native.d.ts","sourceRoot":"","sources":["../../../src/checkIntegrity.native.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAS,CAAC;AA2DtE;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiCzB;AAYD,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAoE1B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { checkIntegrity } from './checkIntegrity';
|
|
2
2
|
export { useDeviceIntegrity } from './useDeviceIntegrity.js';
|
|
3
|
-
export
|
|
3
|
+
export { SIGNAL_IDS } from './types.js';
|
|
4
|
+
export type { AndroidIntegrityOptions, CheckIntegrityOptions, IntegrityPolicy, IntegrityResult, IntegrityStatus, IosIntegrityOptions, Signal, SignalCategory, SignalId, UnknownReason, } from './types.js';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAsB,CAAC;AAC1D,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,MAAM,EACN,cAAc,EACd,aAAa,GACd,MAAM,YAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAsB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AACrC,YAAY,EACV,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,aAAa,GACd,MAAM,YAAS,CAAC"}
|
|
@@ -3,6 +3,7 @@ import type { CheckIntegrityOptions, IntegrityResult } from './types.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Maps a native integrity report + options to the public IntegrityResult.
|
|
5
5
|
* Compromise evidence wins over incomplete runs; incomplete/malformed never become 'clean'.
|
|
6
|
+
* `durationMs` is set to 0 here — `checkIntegrity` overwrites with measured wall time.
|
|
6
7
|
*/
|
|
7
8
|
export declare function resolveStatus(report: unknown, options: CheckIntegrityOptions | undefined, platform: typeof Platform.OS): IntegrityResult;
|
|
8
9
|
//# sourceMappingURL=resolveStatus.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveStatus.d.ts","sourceRoot":"","sources":["../../../src/resolveStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,
|
|
1
|
+
{"version":3,"file":"resolveStatus.d.ts","sourceRoot":"","sources":["../../../src/resolveStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EAKhB,MAAM,YAAS,CAAC;AA4GjB;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,qBAAqB,YAAK,EACnC,QAAQ,EAAE,OAAO,QAAQ,CAAC,EAAE,GAC3B,eAAe,CAwDjB"}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { Platform } from 'react-native';
|
|
2
2
|
export type IntegrityStatus = 'clean' | 'compromised' | 'unknown';
|
|
3
3
|
export type SignalCategory = 'jailbreak' | 'root' | 'hooking' | 'debugger' | 'emulator' | 'tamper' | 'environment';
|
|
4
|
+
/**
|
|
5
|
+
* Stable public signal ids. Unknown future ids remain accepted on `Signal.id`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SIGNAL_IDS: readonly ["simulator", "jailbreak_files", "jailbreak_url_schemes", "jailbreak_symlinks", "jailbreak_writable_system", "hooking_libraries", "hooking_dyld_insert", "debugger_attached", "emulator", "root_su_binary", "root_management_apps", "root_magisk_files", "root_test_keys", "root_dangerous_props", "root_rw_system", "hooking_frida", "hooking_xposed", "tamper_signature_mismatch", "tamper_untrusted_installer", "tamper_binary_decrypted", "tamper_team_id_mismatch"];
|
|
8
|
+
export type SignalId = (typeof SIGNAL_IDS)[number];
|
|
4
9
|
export interface Signal {
|
|
5
|
-
|
|
10
|
+
/** Known ids autocomplete; unknown future ids are still accepted. */
|
|
11
|
+
id: SignalId | (string & {});
|
|
6
12
|
category: SignalCategory;
|
|
7
13
|
description: string;
|
|
8
14
|
}
|
|
@@ -10,14 +16,55 @@ export type UnknownReason = 'unsupported_platform' | 'native_module_unavailable'
|
|
|
10
16
|
export interface IntegrityResult {
|
|
11
17
|
status: IntegrityStatus;
|
|
12
18
|
signals: Signal[];
|
|
19
|
+
/** Signals removed by `ignore` — never affect status. Always present. */
|
|
20
|
+
ignored: Signal[];
|
|
21
|
+
/** Wall time of the check in JS (monotonic when available). 0 on unsupported platforms. */
|
|
22
|
+
durationMs: number;
|
|
13
23
|
platform: typeof Platform.OS;
|
|
14
24
|
reason?: UnknownReason;
|
|
15
25
|
error?: string;
|
|
16
26
|
}
|
|
27
|
+
export interface AndroidIntegrityOptions {
|
|
28
|
+
/** SHA-256 digests of expected signing certificates (hex; colons/whitespace optional). */
|
|
29
|
+
expectedSigningCertificates?: string[];
|
|
30
|
+
/** Package names of allowed installers (e.g. `com.android.vending`). */
|
|
31
|
+
allowedInstallers?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface IosIntegrityOptions {
|
|
34
|
+
/** Expected Apple Team IDs (keychain access-group prefix). */
|
|
35
|
+
expectedTeamIds?: string[];
|
|
36
|
+
/** When true, require the main executable to be encrypted (App Store / TestFlight). */
|
|
37
|
+
requireEncryptedBinary?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface IntegrityPolicy {
|
|
40
|
+
/**
|
|
41
|
+
* Categories that make status `'compromised'`.
|
|
42
|
+
* When omitted, defaults to every category except `'emulator'`.
|
|
43
|
+
*/
|
|
44
|
+
compromisingCategories?: SignalCategory[];
|
|
45
|
+
}
|
|
17
46
|
export interface CheckIntegrityOptions {
|
|
18
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Treat emulator/simulator signals as compromising. Default false (reported,
|
|
49
|
+
* but status stays 'clean' unless the category is otherwise in the policy set).
|
|
50
|
+
* Shorthand that unions `'emulator'` into the compromising category set.
|
|
51
|
+
*/
|
|
19
52
|
treatEmulatorAsCompromised?: boolean;
|
|
20
53
|
/** Resolve as 'unknown' with reason 'timeout' if native does not answer in time. Default 10000 ms. */
|
|
21
54
|
timeoutMs?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Signal ids to exclude from status resolution. Matching signals move to
|
|
57
|
+
* `result.ignored` and never affect status.
|
|
58
|
+
*/
|
|
59
|
+
ignore?: SignalId[];
|
|
60
|
+
/**
|
|
61
|
+
* Status policy. `compromisingCategories` replaces the default set when
|
|
62
|
+
* provided; `treatEmulatorAsCompromised` is unioned in when both are set.
|
|
63
|
+
*/
|
|
64
|
+
policy?: IntegrityPolicy;
|
|
65
|
+
/** Android-only tamper options forwarded to native when provided. */
|
|
66
|
+
android?: AndroidIntegrityOptions;
|
|
67
|
+
/** iOS-only tamper options forwarded to native when provided. */
|
|
68
|
+
ios?: IosIntegrityOptions;
|
|
22
69
|
}
|
|
23
70
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;AAElE,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,QAAQ,GACR,aAAa,CAAC;AAElB,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;AAElE,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,QAAQ,GACR,aAAa,CAAC;AAElB;;GAEG;AACH,eAAO,MAAM,UAAU,mdAsBb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,WAAW,MAAM;IACrB,qEAAqE;IACrE,EAAE,EAAE,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GACrB,sBAAsB,GACtB,2BAA2B,GAC3B,cAAc,GACd,SAAS,GACT,YAAY,GACZ,iBAAiB,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,QAAQ,CAAC,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,0FAA0F;IAC1F,2BAA2B,CAAC,EAAE,MAAM,EAAE,CAAC;IACvC,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,uFAAuF;IACvF,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,cAAc,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,sGAAsG;IACtG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,qEAAqE;IACrE,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAClC,iEAAiE;IACjE,GAAG,CAAC,EAAE,mBAAmB,CAAC;CAC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDeviceIntegrity.d.ts","sourceRoot":"","sources":["../../../src/useDeviceIntegrity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,MAAM,EACP,MAAM,YAAS,CAAC;AAEjB,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACzC,CAAC;
|
|
1
|
+
{"version":3,"file":"useDeviceIntegrity.d.ts","sourceRoot":"","sources":["../../../src/useDeviceIntegrity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,MAAM,EACP,MAAM,YAAS,CAAC;AAEjB,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACzC,CAAC;AAiBF,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,qBAA0B,GAClC,wBAAwB,CAuE1B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-device-integrity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Jailbreak, root, hooking, debugger and emulator detection for React Native and Expo",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"types": "./lib/typescript/src/index.d.ts",
|
|
11
11
|
"default": "./lib/module/index.js"
|
|
12
12
|
},
|
|
13
|
+
"./jest": {
|
|
14
|
+
"types": "./jest/index.d.ts",
|
|
15
|
+
"default": "./jest/index.js"
|
|
16
|
+
},
|
|
13
17
|
"./package.json": "./package.json"
|
|
14
18
|
},
|
|
15
19
|
"files": [
|
|
@@ -18,6 +22,7 @@
|
|
|
18
22
|
"android",
|
|
19
23
|
"ios",
|
|
20
24
|
"cpp",
|
|
25
|
+
"jest",
|
|
21
26
|
"app.plugin.js",
|
|
22
27
|
"plugin/build",
|
|
23
28
|
"*.podspec",
|
|
@@ -153,7 +158,8 @@
|
|
|
153
158
|
"<rootDir>/plugin/build"
|
|
154
159
|
],
|
|
155
160
|
"testPathIgnorePatterns": [
|
|
156
|
-
"<rootDir>/plugin/build"
|
|
161
|
+
"<rootDir>/plugin/build",
|
|
162
|
+
"<rootDir>/jest"
|
|
157
163
|
],
|
|
158
164
|
"collectCoverageFrom": [
|
|
159
165
|
"src/**/*.{ts,tsx}",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CodegenTypes, TurboModule } from 'react-native';
|
|
2
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
2
3
|
|
|
3
4
|
export type NativeSignal = {
|
|
4
5
|
id: string;
|
|
@@ -13,7 +14,9 @@ export type NativeIntegrityReport = {
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export interface Spec extends TurboModule {
|
|
16
|
-
checkIntegrity(
|
|
17
|
+
checkIntegrity(
|
|
18
|
+
options: CodegenTypes.UnsafeObject
|
|
19
|
+
): Promise<NativeIntegrityReport>;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export default TurboModuleRegistry.get<Spec>('DeviceIntegrity');
|
|
@@ -12,6 +12,15 @@ class IntegrityTimeoutError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function nowMs(): number {
|
|
16
|
+
const perf = (globalThis as { performance?: { now?: () => number } })
|
|
17
|
+
.performance;
|
|
18
|
+
if (perf != null && typeof perf.now === 'function') {
|
|
19
|
+
return perf.now();
|
|
20
|
+
}
|
|
21
|
+
return Date.now();
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
|
16
25
|
return new Promise<T>((resolve, reject) => {
|
|
17
26
|
const timer = setTimeout(() => {
|
|
@@ -31,6 +40,78 @@ function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
|
|
31
40
|
});
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
// A blank entry makes native treat the check as configured but unusable,
|
|
44
|
+
// so it reports the run as incomplete instead of skipping the check.
|
|
45
|
+
const UNUSABLE_LIST = [''];
|
|
46
|
+
|
|
47
|
+
function stringArray(value: unknown): string[] | undefined {
|
|
48
|
+
if (value == null) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
if (!Array.isArray(value)) {
|
|
52
|
+
return UNUSABLE_LIST;
|
|
53
|
+
}
|
|
54
|
+
const strings = value.filter(
|
|
55
|
+
(item): item is string => typeof item === 'string'
|
|
56
|
+
);
|
|
57
|
+
if (value.length > 0 && strings.length === 0) {
|
|
58
|
+
return UNUSABLE_LIST;
|
|
59
|
+
}
|
|
60
|
+
return strings;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build the native options bag: only `android` / `ios`, with sanitized fields.
|
|
65
|
+
* Missing fields are omitted. A list that is provided but unusable (wrong type,
|
|
66
|
+
* no strings) is sent as a blank entry so the check fails closed as incomplete.
|
|
67
|
+
*/
|
|
68
|
+
export function sanitizeNativeOptions(
|
|
69
|
+
options: CheckIntegrityOptions
|
|
70
|
+
): Record<string, unknown> {
|
|
71
|
+
const native: Record<string, unknown> = {};
|
|
72
|
+
|
|
73
|
+
if (options.android != null && typeof options.android === 'object') {
|
|
74
|
+
const android: Record<string, unknown> = {};
|
|
75
|
+
const certs = stringArray(options.android.expectedSigningCertificates);
|
|
76
|
+
if (certs != null) {
|
|
77
|
+
android.expectedSigningCertificates = certs;
|
|
78
|
+
}
|
|
79
|
+
const installers = stringArray(options.android.allowedInstallers);
|
|
80
|
+
if (installers != null) {
|
|
81
|
+
android.allowedInstallers = installers;
|
|
82
|
+
}
|
|
83
|
+
if (Object.keys(android).length > 0) {
|
|
84
|
+
native.android = android;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (options.ios != null && typeof options.ios === 'object') {
|
|
89
|
+
const ios: Record<string, unknown> = {};
|
|
90
|
+
const teamIds = stringArray(options.ios.expectedTeamIds);
|
|
91
|
+
if (teamIds != null) {
|
|
92
|
+
ios.expectedTeamIds = teamIds;
|
|
93
|
+
}
|
|
94
|
+
if (typeof options.ios.requireEncryptedBinary === 'boolean') {
|
|
95
|
+
ios.requireEncryptedBinary = options.ios.requireEncryptedBinary;
|
|
96
|
+
}
|
|
97
|
+
if (Object.keys(ios).length > 0) {
|
|
98
|
+
native.ios = ios;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return native;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function withDuration(
|
|
106
|
+
result: IntegrityResult,
|
|
107
|
+
startedAt: number
|
|
108
|
+
): IntegrityResult {
|
|
109
|
+
return {
|
|
110
|
+
...result,
|
|
111
|
+
durationMs: Math.max(0, nowMs() - startedAt),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
34
115
|
export async function checkIntegrity(
|
|
35
116
|
options: CheckIntegrityOptions = {}
|
|
36
117
|
): Promise<IntegrityResult> {
|
|
@@ -38,6 +119,8 @@ export async function checkIntegrity(
|
|
|
38
119
|
return {
|
|
39
120
|
status: 'unknown',
|
|
40
121
|
signals: [],
|
|
122
|
+
ignored: [],
|
|
123
|
+
durationMs: 0,
|
|
41
124
|
platform: Platform.OS,
|
|
42
125
|
reason: 'unsupported_platform',
|
|
43
126
|
};
|
|
@@ -47,27 +130,36 @@ export async function checkIntegrity(
|
|
|
47
130
|
return {
|
|
48
131
|
status: 'unknown',
|
|
49
132
|
signals: [],
|
|
133
|
+
ignored: [],
|
|
134
|
+
durationMs: 0,
|
|
50
135
|
platform: Platform.OS,
|
|
51
136
|
reason: 'native_module_unavailable',
|
|
52
137
|
};
|
|
53
138
|
}
|
|
54
139
|
|
|
55
140
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
141
|
+
const startedAt = nowMs();
|
|
142
|
+
const nativeOptions = sanitizeNativeOptions(options);
|
|
56
143
|
|
|
57
144
|
try {
|
|
58
145
|
const report = await withTimeout(
|
|
59
|
-
NativeDeviceIntegrity.checkIntegrity(),
|
|
146
|
+
NativeDeviceIntegrity.checkIntegrity(nativeOptions),
|
|
60
147
|
timeoutMs
|
|
61
148
|
);
|
|
62
|
-
return resolveStatus(report, options, Platform.OS);
|
|
149
|
+
return withDuration(resolveStatus(report, options, Platform.OS), startedAt);
|
|
63
150
|
} catch (error) {
|
|
64
151
|
if (error instanceof IntegrityTimeoutError) {
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
152
|
+
return withDuration(
|
|
153
|
+
{
|
|
154
|
+
status: 'unknown',
|
|
155
|
+
signals: [],
|
|
156
|
+
ignored: [],
|
|
157
|
+
durationMs: 0,
|
|
158
|
+
platform: Platform.OS,
|
|
159
|
+
reason: 'timeout',
|
|
160
|
+
},
|
|
161
|
+
startedAt
|
|
162
|
+
);
|
|
71
163
|
}
|
|
72
164
|
|
|
73
165
|
const message =
|
|
@@ -77,12 +169,17 @@ export async function checkIntegrity(
|
|
|
77
169
|
? error
|
|
78
170
|
: 'Native integrity check failed';
|
|
79
171
|
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
172
|
+
return withDuration(
|
|
173
|
+
{
|
|
174
|
+
status: 'unknown',
|
|
175
|
+
signals: [],
|
|
176
|
+
ignored: [],
|
|
177
|
+
durationMs: 0,
|
|
178
|
+
platform: Platform.OS,
|
|
179
|
+
reason: 'native_error',
|
|
180
|
+
error: message,
|
|
181
|
+
},
|
|
182
|
+
startedAt
|
|
183
|
+
);
|
|
87
184
|
}
|
|
88
185
|
}
|
package/src/checkIntegrity.ts
CHANGED
package/src/index.tsx
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
export { checkIntegrity } from './checkIntegrity';
|
|
2
2
|
export { useDeviceIntegrity } from './useDeviceIntegrity';
|
|
3
|
+
export { SIGNAL_IDS } from './types';
|
|
3
4
|
export type {
|
|
5
|
+
AndroidIntegrityOptions,
|
|
4
6
|
CheckIntegrityOptions,
|
|
7
|
+
IntegrityPolicy,
|
|
5
8
|
IntegrityResult,
|
|
6
9
|
IntegrityStatus,
|
|
10
|
+
IosIntegrityOptions,
|
|
7
11
|
Signal,
|
|
8
12
|
SignalCategory,
|
|
13
|
+
SignalId,
|
|
9
14
|
UnknownReason,
|
|
10
15
|
} from './types';
|