react-native-device-integrity 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +138 -11
  2. package/android/src/main/java/com/deviceintegrity/DeviceIntegrityModule.kt +68 -2
  3. package/android/src/main/java/com/deviceintegrity/IntegrityChecks.kt +5 -1
  4. package/android/src/main/java/com/deviceintegrity/TamperChecks.kt +158 -0
  5. package/ios/DIIntegrityChecks.h +7 -1
  6. package/ios/DIIntegrityChecks.m +9 -1
  7. package/ios/DITamperChecks.h +27 -0
  8. package/ios/DITamperChecks.m +304 -0
  9. package/ios/DeviceIntegrity.mm +56 -4
  10. package/jest/index.d.ts +39 -0
  11. package/jest/index.js +100 -0
  12. package/lib/module/NativeDeviceIntegrity.js.map +1 -1
  13. package/lib/module/checkIntegrity.js +2 -0
  14. package/lib/module/checkIntegrity.js.map +1 -1
  15. package/lib/module/checkIntegrity.native.js +83 -6
  16. package/lib/module/checkIntegrity.native.js.map +1 -1
  17. package/lib/module/index.js +1 -0
  18. package/lib/module/index.js.map +1 -1
  19. package/lib/module/resolveStatus.js +50 -5
  20. package/lib/module/resolveStatus.js.map +1 -1
  21. package/lib/module/types.js +4 -1
  22. package/lib/module/types.js.map +1 -1
  23. package/lib/module/useDeviceIntegrity.js +21 -7
  24. package/lib/module/useDeviceIntegrity.js.map +1 -1
  25. package/lib/typescript/src/NativeDeviceIntegrity.d.ts +2 -2
  26. package/lib/typescript/src/NativeDeviceIntegrity.d.ts.map +1 -1
  27. package/lib/typescript/src/checkIntegrity.d.ts.map +1 -1
  28. package/lib/typescript/src/checkIntegrity.native.d.ts +6 -0
  29. package/lib/typescript/src/checkIntegrity.native.d.ts.map +1 -1
  30. package/lib/typescript/src/index.d.ts +2 -1
  31. package/lib/typescript/src/index.d.ts.map +1 -1
  32. package/lib/typescript/src/resolveStatus.d.ts +1 -0
  33. package/lib/typescript/src/resolveStatus.d.ts.map +1 -1
  34. package/lib/typescript/src/types.d.ts +49 -2
  35. package/lib/typescript/src/types.d.ts.map +1 -1
  36. package/lib/typescript/src/useDeviceIntegrity.d.ts.map +1 -1
  37. package/package.json +14 -3
  38. package/src/NativeDeviceIntegrity.ts +5 -2
  39. package/src/checkIntegrity.native.ts +112 -15
  40. package/src/checkIntegrity.ts +2 -0
  41. package/src/index.tsx +5 -0
  42. package/src/resolveStatus.ts +69 -12
  43. package/src/types.ts +76 -2
  44. package/src/useDeviceIntegrity.ts +22 -7
@@ -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 signals = nativeReport.signals.map(normalizeSignal);
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 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) {
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","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":[]}
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":[]}
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
2
 
3
- export {};
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
@@ -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 treatEmulatorAsCompromised = options.treatEmulatorAsCompromised;
8
- const timeoutMs = options.timeoutMs;
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
- }, [treatEmulatorAsCompromised, timeoutMs]);
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","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":[]}
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 { type TurboModule } from 'react-native';
1
+ import type { 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: Object): 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,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"}
1
+ {"version":3,"file":"NativeDeviceIntegrity.d.ts","sourceRoot":"","sources":["../../../src/NativeDeviceIntegrity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,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;IAGvC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACjE;;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,CAO1B"}
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;AA8BtE,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAoD1B"}
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 type { CheckIntegrityOptions, IntegrityResult, IntegrityStatus, Signal, SignalCategory, UnknownReason, } from './types.js';
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,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"}
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
- id: string;
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
- /** Treat emulator/simulator signals as compromising. Default false (reported, but status stays 'clean'). */
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;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"}
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;AAEF,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,qBAA0B,GAClC,wBAAwB,CAuE1B"}
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.1.0",
3
+ "version": "0.2.1",
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,11 @@
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
+ },
17
+ "./app.plugin.js": "./app.plugin.js",
13
18
  "./package.json": "./package.json"
14
19
  },
15
20
  "files": [
@@ -18,6 +23,7 @@
18
23
  "android",
19
24
  "ios",
20
25
  "cpp",
26
+ "jest",
21
27
  "app.plugin.js",
22
28
  "plugin/build",
23
29
  "*.podspec",
@@ -42,7 +48,8 @@
42
48
  "typecheck": "tsc && tsc -p plugin --noEmit",
43
49
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
44
50
  "test": "jest",
45
- "release": "release-it --only-version"
51
+ "release": "release-it --only-version",
52
+ "check:exports": "node -e \"for (const s of ['react-native-device-integrity/app.plugin.js','react-native-device-integrity/jest','react-native-device-integrity/package.json']) require.resolve(s); console.log('exports ok')\""
46
53
  },
47
54
  "keywords": [
48
55
  "react-native",
@@ -153,7 +160,8 @@
153
160
  "<rootDir>/plugin/build"
154
161
  ],
155
162
  "testPathIgnorePatterns": [
156
- "<rootDir>/plugin/build"
163
+ "<rootDir>/plugin/build",
164
+ "<rootDir>/jest"
157
165
  ],
158
166
  "collectCoverageFrom": [
159
167
  "src/**/*.{ts,tsx}",
@@ -206,5 +214,8 @@
206
214
  "eslint"
207
215
  ],
208
216
  "version": "0.63.1"
217
+ },
218
+ "resolutions": {
219
+ "release-it/undici": "7.29.1"
209
220
  }
210
221
  }
@@ -1,4 +1,5 @@
1
- import { TurboModuleRegistry, type TurboModule } from 'react-native';
1
+ import type { 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(): Promise<NativeIntegrityReport>;
17
+ // `Object` (not CodegenTypes.UnsafeObject) so older codegen versions, e.g.
18
+ // React Native 0.79, can parse the spec. Codegen maps it to a generic object.
19
+ checkIntegrity(options: Object): 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
- status: 'unknown',
67
- signals: [],
68
- platform: Platform.OS,
69
- reason: 'timeout',
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
- status: 'unknown',
82
- signals: [],
83
- platform: Platform.OS,
84
- reason: 'native_error',
85
- error: message,
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
  }
@@ -10,6 +10,8 @@ export async function checkIntegrity(
10
10
  return {
11
11
  status: 'unknown',
12
12
  signals: [],
13
+ ignored: [],
14
+ durationMs: 0,
13
15
  platform: Platform.OS,
14
16
  reason: 'unsupported_platform',
15
17
  };
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';