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,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { AppState } from 'react-native';
|
|
5
|
+
import { checkIntegrity } from './checkIntegrity';
|
|
6
|
+
export function useDeviceIntegrity(options = {}) {
|
|
7
|
+
const treatEmulatorAsCompromised = options.treatEmulatorAsCompromised;
|
|
8
|
+
const timeoutMs = options.timeoutMs;
|
|
9
|
+
const [result, setResult] = useState(null);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
const requestIdRef = useRef(0);
|
|
12
|
+
const mountedRef = useRef(true);
|
|
13
|
+
const appStateRef = useRef(AppState.currentState);
|
|
14
|
+
const run = useCallback(async () => {
|
|
15
|
+
const requestId = ++requestIdRef.current;
|
|
16
|
+
setLoading(true);
|
|
17
|
+
const next = await checkIntegrity({
|
|
18
|
+
treatEmulatorAsCompromised,
|
|
19
|
+
timeoutMs
|
|
20
|
+
});
|
|
21
|
+
if (mountedRef.current && requestId === requestIdRef.current) {
|
|
22
|
+
setResult(next);
|
|
23
|
+
setLoading(false);
|
|
24
|
+
}
|
|
25
|
+
return next;
|
|
26
|
+
}, [treatEmulatorAsCompromised, timeoutMs]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
mountedRef.current = true;
|
|
29
|
+
run().catch(() => {
|
|
30
|
+
// checkIntegrity never rejects; guard against unexpected throws
|
|
31
|
+
});
|
|
32
|
+
return () => {
|
|
33
|
+
mountedRef.current = false;
|
|
34
|
+
};
|
|
35
|
+
}, [run]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const onChange = nextState => {
|
|
38
|
+
const previous = appStateRef.current;
|
|
39
|
+
appStateRef.current = nextState;
|
|
40
|
+
if ((previous === 'background' || previous === 'inactive') && nextState === 'active') {
|
|
41
|
+
run().catch(() => {
|
|
42
|
+
// checkIntegrity never rejects; guard against unexpected throws
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const subscription = AppState.addEventListener('change', onChange);
|
|
47
|
+
return () => {
|
|
48
|
+
subscription.remove();
|
|
49
|
+
};
|
|
50
|
+
}, [run]);
|
|
51
|
+
const refresh = useCallback(() => {
|
|
52
|
+
return run();
|
|
53
|
+
}, [run]);
|
|
54
|
+
return {
|
|
55
|
+
status: result?.status ?? 'unknown',
|
|
56
|
+
signals: result?.signals ?? [],
|
|
57
|
+
result,
|
|
58
|
+
loading,
|
|
59
|
+
refresh
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=useDeviceIntegrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useRef","useState","AppState","checkIntegrity","useDeviceIntegrity","options","treatEmulatorAsCompromised","timeoutMs","result","setResult","loading","setLoading","requestIdRef","mountedRef","appStateRef","currentState","run","requestId","current","next","catch","onChange","nextState","previous","subscription","addEventListener","remove","refresh","status","signals"],"sourceRoot":"../../src","sources":["useDeviceIntegrity.ts"],"mappings":";;AAAA,SAASA,WAAW,EAAEC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAChE,SAASC,QAAQ,QAA6B,cAAc;AAC5D,SAASC,cAAc,QAAQ,kBAAkB;AAgBjD,OAAO,SAASC,kBAAkBA,CAChCC,OAA8B,GAAG,CAAC,CAAC,EACT;EAC1B,MAAMC,0BAA0B,GAAGD,OAAO,CAACC,0BAA0B;EACrE,MAAMC,SAAS,GAAGF,OAAO,CAACE,SAAS;EAEnC,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,GAAGR,QAAQ,CAAyB,IAAI,CAAC;EAClE,MAAM,CAACS,OAAO,EAAEC,UAAU,CAAC,GAAGV,QAAQ,CAAC,IAAI,CAAC;EAE5C,MAAMW,YAAY,GAAGZ,MAAM,CAAC,CAAC,CAAC;EAC9B,MAAMa,UAAU,GAAGb,MAAM,CAAC,IAAI,CAAC;EAC/B,MAAMc,WAAW,GAAGd,MAAM,CAACE,QAAQ,CAACa,YAAY,CAAC;EAEjD,MAAMC,GAAG,GAAGlB,WAAW,CAAC,YAAsC;IAC5D,MAAMmB,SAAS,GAAG,EAAEL,YAAY,CAACM,OAAO;IACxCP,UAAU,CAAC,IAAI,CAAC;IAEhB,MAAMQ,IAAI,GAAG,MAAMhB,cAAc,CAAC;MAChCG,0BAA0B;MAC1BC;IACF,CAAC,CAAC;IAEF,IAAIM,UAAU,CAACK,OAAO,IAAID,SAAS,KAAKL,YAAY,CAACM,OAAO,EAAE;MAC5DT,SAAS,CAACU,IAAI,CAAC;MACfR,UAAU,CAAC,KAAK,CAAC;IACnB;IAEA,OAAOQ,IAAI;EACb,CAAC,EAAE,CAACb,0BAA0B,EAAEC,SAAS,CAAC,CAAC;EAE3CR,SAAS,CAAC,MAAM;IACdc,UAAU,CAACK,OAAO,GAAG,IAAI;IACzBF,GAAG,CAAC,CAAC,CAACI,KAAK,CAAC,MAAM;MAChB;IAAA,CACD,CAAC;IAEF,OAAO,MAAM;MACXP,UAAU,CAACK,OAAO,GAAG,KAAK;IAC5B,CAAC;EACH,CAAC,EAAE,CAACF,GAAG,CAAC,CAAC;EAETjB,SAAS,CAAC,MAAM;IACd,MAAMsB,QAAQ,GAAIC,SAAyB,IAAK;MAC9C,MAAMC,QAAQ,GAAGT,WAAW,CAACI,OAAO;MACpCJ,WAAW,CAACI,OAAO,GAAGI,SAAS;MAE/B,IACE,CAACC,QAAQ,KAAK,YAAY,IAAIA,QAAQ,KAAK,UAAU,KACrDD,SAAS,KAAK,QAAQ,EACtB;QACAN,GAAG,CAAC,CAAC,CAACI,KAAK,CAAC,MAAM;UAChB;QAAA,CACD,CAAC;MACJ;IACF,CAAC;IAED,MAAMI,YAAY,GAAGtB,QAAQ,CAACuB,gBAAgB,CAAC,QAAQ,EAAEJ,QAAQ,CAAC;IAClE,OAAO,MAAM;MACXG,YAAY,CAACE,MAAM,CAAC,CAAC;IACvB,CAAC;EACH,CAAC,EAAE,CAACV,GAAG,CAAC,CAAC;EAET,MAAMW,OAAO,GAAG7B,WAAW,CAAC,MAAgC;IAC1D,OAAOkB,GAAG,CAAC,CAAC;EACd,CAAC,EAAE,CAACA,GAAG,CAAC,CAAC;EAET,OAAO;IACLY,MAAM,EAAEpB,MAAM,EAAEoB,MAAM,IAAI,SAAS;IACnCC,OAAO,EAAErB,MAAM,EAAEqB,OAAO,IAAI,EAAE;IAC9BrB,MAAM;IACNE,OAAO;IACPiB;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type TurboModule } from 'react-native';
|
|
2
|
+
export type NativeSignal = {
|
|
3
|
+
id: string;
|
|
4
|
+
category: string;
|
|
5
|
+
description: string;
|
|
6
|
+
};
|
|
7
|
+
export type NativeIntegrityReport = {
|
|
8
|
+
completed: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
signals: NativeSignal[];
|
|
11
|
+
};
|
|
12
|
+
export interface Spec extends TurboModule {
|
|
13
|
+
checkIntegrity(): Promise<NativeIntegrityReport>;
|
|
14
|
+
}
|
|
15
|
+
declare const _default: Spec | null | undefined;
|
|
16
|
+
export default _default;
|
|
17
|
+
//# sourceMappingURL=NativeDeviceIntegrity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeDeviceIntegrity.d.ts","sourceRoot":"","sources":["../../../src/NativeDeviceIntegrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,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,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAClD;;AAED,wBAAgE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CheckIntegrityOptions, IntegrityResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Web / unsupported-platform entry. Must not import the native TurboModule.
|
|
4
|
+
*/
|
|
5
|
+
export declare function checkIntegrity(_options?: CheckIntegrityOptions): Promise<IntegrityResult>;
|
|
6
|
+
//# sourceMappingURL=checkIntegrity.d.ts.map
|
|
@@ -0,0 +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,CAO1B"}
|
|
@@ -0,0 +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;AA8BtE,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAoD1B"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { checkIntegrity } from './checkIntegrity';
|
|
2
|
+
export { useDeviceIntegrity } from './useDeviceIntegrity.js';
|
|
3
|
+
export type { CheckIntegrityOptions, IntegrityResult, IntegrityStatus, Signal, SignalCategory, UnknownReason, } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Platform } from 'react-native';
|
|
2
|
+
import type { CheckIntegrityOptions, IntegrityResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Maps a native integrity report + options to the public IntegrityResult.
|
|
5
|
+
* Compromise evidence wins over incomplete runs; incomplete/malformed never become 'clean'.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveStatus(report: unknown, options: CheckIntegrityOptions | undefined, platform: typeof Platform.OS): IntegrityResult;
|
|
8
|
+
//# sourceMappingURL=resolveStatus.d.ts.map
|
|
@@ -0,0 +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,EAIhB,MAAM,YAAS,CAAC;AAwDjB;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,qBAAqB,YAAK,EACnC,QAAQ,EAAE,OAAO,QAAQ,CAAC,EAAE,GAC3B,eAAe,CAqDjB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Platform } from 'react-native';
|
|
2
|
+
export type IntegrityStatus = 'clean' | 'compromised' | 'unknown';
|
|
3
|
+
export type SignalCategory = 'jailbreak' | 'root' | 'hooking' | 'debugger' | 'emulator' | 'tamper' | 'environment';
|
|
4
|
+
export interface Signal {
|
|
5
|
+
id: string;
|
|
6
|
+
category: SignalCategory;
|
|
7
|
+
description: string;
|
|
8
|
+
}
|
|
9
|
+
export type UnknownReason = 'unsupported_platform' | 'native_module_unavailable' | 'native_error' | 'timeout' | 'incomplete' | 'not_implemented';
|
|
10
|
+
export interface IntegrityResult {
|
|
11
|
+
status: IntegrityStatus;
|
|
12
|
+
signals: Signal[];
|
|
13
|
+
platform: typeof Platform.OS;
|
|
14
|
+
reason?: UnknownReason;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CheckIntegrityOptions {
|
|
18
|
+
/** Treat emulator/simulator signals as compromising. Default false (reported, but status stays 'clean'). */
|
|
19
|
+
treatEmulatorAsCompromised?: boolean;
|
|
20
|
+
/** Resolve as 'unknown' with reason 'timeout' if native does not answer in time. Default 10000 ms. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +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;IACX,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,QAAQ,EAAE,OAAO,QAAQ,CAAC,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,4GAA4G;IAC5G,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,sGAAsG;IACtG,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CheckIntegrityOptions, IntegrityResult, IntegrityStatus, Signal } from './types.js';
|
|
2
|
+
export type UseDeviceIntegrityResult = {
|
|
3
|
+
status: IntegrityStatus;
|
|
4
|
+
signals: Signal[];
|
|
5
|
+
result: IntegrityResult | null;
|
|
6
|
+
loading: boolean;
|
|
7
|
+
refresh: () => Promise<IntegrityResult>;
|
|
8
|
+
};
|
|
9
|
+
export declare function useDeviceIntegrity(options?: CheckIntegrityOptions): UseDeviceIntegrityResult;
|
|
10
|
+
//# sourceMappingURL=useDeviceIntegrity.d.ts.map
|
|
@@ -0,0 +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;AAEF,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,qBAA0B,GAClC,wBAAwB,CAuE1B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-device-integrity",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Jailbreak, root, hooking, debugger and emulator detection for React Native and Expo",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"react-native-device-integrity-source": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"lib",
|
|
18
|
+
"android",
|
|
19
|
+
"ios",
|
|
20
|
+
"cpp",
|
|
21
|
+
"app.plugin.js",
|
|
22
|
+
"plugin/build",
|
|
23
|
+
"*.podspec",
|
|
24
|
+
"react-native.config.js",
|
|
25
|
+
"!ios/build",
|
|
26
|
+
"!android/build",
|
|
27
|
+
"!android/app/build",
|
|
28
|
+
"!android/gradle",
|
|
29
|
+
"!android/gradlew",
|
|
30
|
+
"!android/gradlew.bat",
|
|
31
|
+
"!android/local.properties",
|
|
32
|
+
"!**/__tests__",
|
|
33
|
+
"!**/__fixtures__",
|
|
34
|
+
"!**/__mocks__",
|
|
35
|
+
"!**/.*"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"example": "yarn workspace react-native-device-integrity-example",
|
|
39
|
+
"clean": "del-cli lib",
|
|
40
|
+
"prepare": "bob build && yarn build:plugin",
|
|
41
|
+
"build:plugin": "tsc -p plugin",
|
|
42
|
+
"typecheck": "tsc && tsc -p plugin --noEmit",
|
|
43
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
44
|
+
"test": "jest",
|
|
45
|
+
"release": "release-it --only-version"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"react-native",
|
|
49
|
+
"ios",
|
|
50
|
+
"android",
|
|
51
|
+
"expo",
|
|
52
|
+
"expo-config-plugin"
|
|
53
|
+
],
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "git+https://github.com/RRigueira/react-native-device-integrity.git"
|
|
57
|
+
},
|
|
58
|
+
"author": "Rui Rigueira <14238908+RRigueira@users.noreply.github.com> (https://github.com/RRigueira)",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/RRigueira/react-native-device-integrity/issues"
|
|
62
|
+
},
|
|
63
|
+
"homepage": "https://github.com/RRigueira/react-native-device-integrity#readme",
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"registry": "https://registry.npmjs.org/",
|
|
66
|
+
"access": "public",
|
|
67
|
+
"provenance": true
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@commitlint/cli": "^21.2.3",
|
|
71
|
+
"@commitlint/config-conventional": "^21.2.3",
|
|
72
|
+
"@eslint/compat": "^2.1.0",
|
|
73
|
+
"@eslint/eslintrc": "^3.3.6",
|
|
74
|
+
"@eslint/js": "^9.39.5",
|
|
75
|
+
"@react-native/babel-preset": "0.86.2",
|
|
76
|
+
"@react-native/eslint-config": "0.86.2",
|
|
77
|
+
"@react-native/jest-preset": "0.86.3",
|
|
78
|
+
"@release-it/conventional-changelog": "^12.0.2",
|
|
79
|
+
"@testing-library/react-native": "^13.3.3",
|
|
80
|
+
"@types/jest": "^30.0.0",
|
|
81
|
+
"@types/node": "^26.6.3",
|
|
82
|
+
"@types/react": "^19.2.0",
|
|
83
|
+
"@types/react-test-renderer": "^19",
|
|
84
|
+
"del-cli": "^7.0.0",
|
|
85
|
+
"eslint": "^9.39.5",
|
|
86
|
+
"eslint-config-prettier": "^10.1.8",
|
|
87
|
+
"eslint-plugin-ft-flow": "^3.0.11",
|
|
88
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
89
|
+
"expo": "~57.0.26",
|
|
90
|
+
"jest": "^30.5.2",
|
|
91
|
+
"lefthook": "^2.1.14",
|
|
92
|
+
"prettier": "^3.9.6",
|
|
93
|
+
"react": "19.2.3",
|
|
94
|
+
"react-native": "0.86.3",
|
|
95
|
+
"react-native-builder-bob": "^0.43.1",
|
|
96
|
+
"react-test-renderer": "19.2.3",
|
|
97
|
+
"release-it": "^21.1.0",
|
|
98
|
+
"turbo": "^2.10.8",
|
|
99
|
+
"typescript": "^6.0.3"
|
|
100
|
+
},
|
|
101
|
+
"peerDependencies": {
|
|
102
|
+
"expo": ">=52",
|
|
103
|
+
"react": "*",
|
|
104
|
+
"react-native": "*"
|
|
105
|
+
},
|
|
106
|
+
"peerDependenciesMeta": {
|
|
107
|
+
"expo": {
|
|
108
|
+
"optional": true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"workspaces": [
|
|
112
|
+
"example"
|
|
113
|
+
],
|
|
114
|
+
"packageManager": "yarn@4.11.0",
|
|
115
|
+
"react-native-builder-bob": {
|
|
116
|
+
"source": "src",
|
|
117
|
+
"output": "lib",
|
|
118
|
+
"targets": [
|
|
119
|
+
[
|
|
120
|
+
"module",
|
|
121
|
+
{
|
|
122
|
+
"esm": true
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
"typescript",
|
|
127
|
+
{
|
|
128
|
+
"project": "tsconfig.build.json"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"codegenConfig": {
|
|
134
|
+
"name": "DeviceIntegritySpec",
|
|
135
|
+
"type": "modules",
|
|
136
|
+
"jsSrcsDir": "src",
|
|
137
|
+
"android": {
|
|
138
|
+
"javaPackageName": "com.deviceintegrity"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"prettier": {
|
|
142
|
+
"quoteProps": "consistent",
|
|
143
|
+
"singleQuote": true,
|
|
144
|
+
"tabWidth": 2,
|
|
145
|
+
"trailingComma": "es5",
|
|
146
|
+
"useTabs": false
|
|
147
|
+
},
|
|
148
|
+
"jest": {
|
|
149
|
+
"preset": "react-native",
|
|
150
|
+
"modulePathIgnorePatterns": [
|
|
151
|
+
"<rootDir>/example/node_modules",
|
|
152
|
+
"<rootDir>/lib/",
|
|
153
|
+
"<rootDir>/plugin/build"
|
|
154
|
+
],
|
|
155
|
+
"testPathIgnorePatterns": [
|
|
156
|
+
"<rootDir>/plugin/build"
|
|
157
|
+
],
|
|
158
|
+
"collectCoverageFrom": [
|
|
159
|
+
"src/**/*.{ts,tsx}",
|
|
160
|
+
"!src/**/__tests__/**",
|
|
161
|
+
"!src/NativeDeviceIntegrity.ts",
|
|
162
|
+
"!src/types.ts",
|
|
163
|
+
"plugin/src/**/*.ts",
|
|
164
|
+
"!plugin/src/**/__tests__/**"
|
|
165
|
+
],
|
|
166
|
+
"coverageThreshold": {
|
|
167
|
+
"global": {
|
|
168
|
+
"statements": 95,
|
|
169
|
+
"branches": 90,
|
|
170
|
+
"functions": 85,
|
|
171
|
+
"lines": 95
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"commitlint": {
|
|
176
|
+
"extends": [
|
|
177
|
+
"@commitlint/config-conventional"
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
"release-it": {
|
|
181
|
+
"git": {
|
|
182
|
+
"commitMessage": "chore: release ${version}",
|
|
183
|
+
"tagName": "v${version}",
|
|
184
|
+
"requireBranch": "main",
|
|
185
|
+
"requireCleanWorkingDir": true
|
|
186
|
+
},
|
|
187
|
+
"npm": {
|
|
188
|
+
"publish": false
|
|
189
|
+
},
|
|
190
|
+
"github": {
|
|
191
|
+
"release": true
|
|
192
|
+
},
|
|
193
|
+
"plugins": {
|
|
194
|
+
"@release-it/conventional-changelog": {
|
|
195
|
+
"preset": {
|
|
196
|
+
"name": "angular"
|
|
197
|
+
},
|
|
198
|
+
"infile": "CHANGELOG.md"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"create-react-native-library": {
|
|
203
|
+
"type": "turbo-module",
|
|
204
|
+
"languages": "kotlin-objc",
|
|
205
|
+
"tools": [
|
|
206
|
+
"eslint"
|
|
207
|
+
],
|
|
208
|
+
"version": "0.63.1"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ConfigPlugin, type InfoPlist } from 'expo/config-plugins';
|
|
2
|
+
export declare const JAILBREAK_URL_SCHEMES: readonly ["cydia", "sileo", "zbra", "filza", "undecimus", "activator"];
|
|
3
|
+
export type DeviceIntegrityPluginProps = {
|
|
4
|
+
ios?: {
|
|
5
|
+
/** When false, skip Info.plist changes. Default true. */
|
|
6
|
+
jailbreakUrlSchemes?: boolean;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Merge jailbreak URL schemes into LSApplicationQueriesSchemes.
|
|
11
|
+
* Keeps existing entries/order, appends missing ones, never removes.
|
|
12
|
+
*
|
|
13
|
+
* Android needs nothing — the library AndroidManifest `<queries>` merges
|
|
14
|
+
* automatically via the Gradle manifest merger.
|
|
15
|
+
*/
|
|
16
|
+
export declare function addJailbreakQueriesSchemes(infoPlist: InfoPlist): InfoPlist;
|
|
17
|
+
export declare const withDeviceIntegrity: ConfigPlugin<DeviceIntegrityPluginProps | void>;
|
|
18
|
+
declare const _default: ConfigPlugin<void | DeviceIntegrityPluginProps>;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withDeviceIntegrity = exports.JAILBREAK_URL_SCHEMES = void 0;
|
|
4
|
+
exports.addJailbreakQueriesSchemes = addJailbreakQueriesSchemes;
|
|
5
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
6
|
+
// keep in sync with ios/DIIntegrityChecks.m hasJailbreakURLSchemes
|
|
7
|
+
exports.JAILBREAK_URL_SCHEMES = [
|
|
8
|
+
'cydia',
|
|
9
|
+
'sileo',
|
|
10
|
+
'zbra',
|
|
11
|
+
'filza',
|
|
12
|
+
'undecimus',
|
|
13
|
+
'activator',
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Merge jailbreak URL schemes into LSApplicationQueriesSchemes.
|
|
17
|
+
* Keeps existing entries/order, appends missing ones, never removes.
|
|
18
|
+
*
|
|
19
|
+
* Android needs nothing — the library AndroidManifest `<queries>` merges
|
|
20
|
+
* automatically via the Gradle manifest merger.
|
|
21
|
+
*/
|
|
22
|
+
function addJailbreakQueriesSchemes(infoPlist) {
|
|
23
|
+
const existing = infoPlist.LSApplicationQueriesSchemes;
|
|
24
|
+
const current = Array.isArray(existing) ? existing.map(String) : [];
|
|
25
|
+
const seen = new Set(current);
|
|
26
|
+
for (const scheme of exports.JAILBREAK_URL_SCHEMES) {
|
|
27
|
+
if (!seen.has(scheme)) {
|
|
28
|
+
current.push(scheme);
|
|
29
|
+
seen.add(scheme);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...infoPlist,
|
|
34
|
+
LSApplicationQueriesSchemes: current,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const withDeviceIntegrity = (config, props) => {
|
|
38
|
+
var _a;
|
|
39
|
+
if (((_a = props === null || props === void 0 ? void 0 : props.ios) === null || _a === void 0 ? void 0 : _a.jailbreakUrlSchemes) === false) {
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
42
|
+
return (0, config_plugins_1.withInfoPlist)(config, (modConfig) => {
|
|
43
|
+
modConfig.modResults = addJailbreakQueriesSchemes(modConfig.modResults);
|
|
44
|
+
return modConfig;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
exports.withDeviceIntegrity = withDeviceIntegrity;
|
|
48
|
+
const pkg = require('../../package.json');
|
|
49
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(exports.withDeviceIntegrity, pkg.name, pkg.version);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type NativeSignal = {
|
|
4
|
+
id: string;
|
|
5
|
+
category: string;
|
|
6
|
+
description: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type NativeIntegrityReport = {
|
|
10
|
+
completed: boolean;
|
|
11
|
+
reason?: string;
|
|
12
|
+
signals: NativeSignal[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export interface Spec extends TurboModule {
|
|
16
|
+
checkIntegrity(): Promise<NativeIntegrityReport>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default TurboModuleRegistry.get<Spec>('DeviceIntegrity');
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import NativeDeviceIntegrity from './NativeDeviceIntegrity';
|
|
3
|
+
import { resolveStatus } from './resolveStatus';
|
|
4
|
+
import type { CheckIntegrityOptions, IntegrityResult } from './types';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
7
|
+
|
|
8
|
+
class IntegrityTimeoutError extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('Device integrity check timed out');
|
|
11
|
+
this.name = 'IntegrityTimeoutError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
|
16
|
+
return new Promise<T>((resolve, reject) => {
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
reject(new IntegrityTimeoutError());
|
|
19
|
+
}, timeoutMs);
|
|
20
|
+
|
|
21
|
+
promise.then(
|
|
22
|
+
(value) => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
resolve(value);
|
|
25
|
+
},
|
|
26
|
+
(error: unknown) => {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
reject(error);
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function checkIntegrity(
|
|
35
|
+
options: CheckIntegrityOptions = {}
|
|
36
|
+
): Promise<IntegrityResult> {
|
|
37
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
38
|
+
return {
|
|
39
|
+
status: 'unknown',
|
|
40
|
+
signals: [],
|
|
41
|
+
platform: Platform.OS,
|
|
42
|
+
reason: 'unsupported_platform',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (NativeDeviceIntegrity == null) {
|
|
47
|
+
return {
|
|
48
|
+
status: 'unknown',
|
|
49
|
+
signals: [],
|
|
50
|
+
platform: Platform.OS,
|
|
51
|
+
reason: 'native_module_unavailable',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const report = await withTimeout(
|
|
59
|
+
NativeDeviceIntegrity.checkIntegrity(),
|
|
60
|
+
timeoutMs
|
|
61
|
+
);
|
|
62
|
+
return resolveStatus(report, options, Platform.OS);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof IntegrityTimeoutError) {
|
|
65
|
+
return {
|
|
66
|
+
status: 'unknown',
|
|
67
|
+
signals: [],
|
|
68
|
+
platform: Platform.OS,
|
|
69
|
+
reason: 'timeout',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const message =
|
|
74
|
+
error instanceof Error
|
|
75
|
+
? error.message
|
|
76
|
+
: typeof error === 'string'
|
|
77
|
+
? error
|
|
78
|
+
: 'Native integrity check failed';
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
status: 'unknown',
|
|
82
|
+
signals: [],
|
|
83
|
+
platform: Platform.OS,
|
|
84
|
+
reason: 'native_error',
|
|
85
|
+
error: message,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import type { CheckIntegrityOptions, IntegrityResult } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Web / unsupported-platform entry. Must not import the native TurboModule.
|
|
6
|
+
*/
|
|
7
|
+
export async function checkIntegrity(
|
|
8
|
+
_options?: CheckIntegrityOptions
|
|
9
|
+
): Promise<IntegrityResult> {
|
|
10
|
+
return {
|
|
11
|
+
status: 'unknown',
|
|
12
|
+
signals: [],
|
|
13
|
+
platform: Platform.OS,
|
|
14
|
+
reason: 'unsupported_platform',
|
|
15
|
+
};
|
|
16
|
+
}
|
package/src/index.tsx
ADDED