react-native-dev-guard 1.0.0 → 1.0.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.
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.useDevGuard = exports.DevGuardProvider = void 0;
6
+ exports.resolveBrandingFooter = exports.useDevGuard = exports.DevGuardProvider = void 0;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const react_1 = require("react");
9
9
  const react_native_1 = require("react-native");
@@ -21,10 +21,10 @@ const DiagnosticOverlay_1 = require("./components/DiagnosticOverlay");
21
21
  const DevGuardLogger_1 = require("./services/DevGuardLogger");
22
22
  const GuardEnforcement_1 = require("./services/GuardEnforcement");
23
23
  const RemoteWipeService_1 = require("./services/RemoteWipeService");
24
+ const obf_1 = require("./internal/obf");
24
25
  const react_native_2 = require("react-native");
25
26
  const react_native_safe_area_context_1 = require("react-native-safe-area-context");
26
27
  const { DevGuard: DevGuardNative } = react_native_1.NativeModules;
27
- const API_URL = 'https://api.devguard.uk/devguard';
28
28
  const DevGuardContext = (0, react_1.createContext)(undefined);
29
29
  const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = true, failSafe = 'open' }) => {
30
30
  const authSecret = secret ?? apiKey;
@@ -35,6 +35,7 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
35
35
  const heartbeatTimer = (0, react_1.useRef)(null);
36
36
  const lastSyncTime = (0, react_1.useRef)(0);
37
37
  const [showDiagnostics, setShowDiagnostics] = (0, react_1.useState)(false);
38
+ const isCompromisedRef = (0, react_1.useRef)(false);
38
39
  const lastLifecycleSyncTime = (0, react_1.useRef)(0);
39
40
  // Mirror of `response` for use inside long-lived closures (AppState listener,
40
41
  // heartbeat). Those callbacks are registered once, so reading the `response`
@@ -42,6 +43,7 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
42
43
  // telemetry consent and lifecycle gating. The ref always holds the latest.
43
44
  // (Parity with Flutter, where these reads hit the `_cachedResponse` field.)
44
45
  const responseRef = (0, react_1.useRef)(null);
46
+ const verifyChainRef = (0, react_1.useRef)(Promise.resolve());
45
47
  (0, react_1.useEffect)(() => {
46
48
  responseRef.current = response;
47
49
  }, [response]);
@@ -94,144 +96,154 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
94
96
  return true;
95
97
  };
96
98
  const verify = async (force = false, trigger) => {
97
- if (!shouldSync(force, trigger)) {
98
- console.log('DevGuard: Sync skipped due to policy.');
99
- return;
100
- }
101
- try {
102
- const hardware = HardwareService_1.HardwareService.getInstance();
103
- const deviceId = await hardware.getDeviceId();
104
- // Use the ref (not the possibly-stale closed-over `response`) so advanced
105
- // telemetry consent is honored on heartbeat/lifecycle-triggered syncs.
106
- const metadata = await hardware.getMetadata(responseRef.current?.betaFeatures);
107
- const timestamp = Date.now().toString();
108
- // Align payload with Flutter: Metadata map is the core payload 'p'
109
- const payload = JSON.stringify({
110
- ...metadata,
111
- projectId,
112
- timestamp: parseInt(timestamp, 10)
113
- });
114
- // GZip + Base64 Tunneling (v1-gzip)
115
- const compressed = pako_1.default.gzip(payload);
116
- const uint8ToBase64 = (data) => {
117
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
118
- let result = '';
119
- let i;
120
- const l = data.length;
121
- for (i = 2; i < l; i += 3) {
122
- result += chars[data[i - 2] >> 2];
123
- result += chars[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)];
124
- result += chars[((data[i - 1] & 0x0f) << 2) | (data[i] >> 6)];
125
- result += chars[data[i] & 0x3f];
126
- }
127
- if (i === l + 1) {
128
- result += chars[data[i - 2] >> 2];
129
- result += chars[(data[i - 2] & 0x03) << 4];
130
- result += '==';
131
- }
132
- else if (i === l) {
133
- result += chars[data[i - 2] >> 2];
134
- result += chars[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)];
135
- result += chars[(data[i - 1] & 0x0f) << 2];
136
- result += '=';
137
- }
138
- return result;
139
- };
140
- const base64Payload = uint8ToBase64(compressed);
141
- const signature = await DevGuardNative.generateSignature(projectId, parseInt(timestamp, 10));
142
- const fetchUrl = API_URL;
143
- const fetchOptions = {
144
- method: 'POST',
145
- headers: {
146
- 'Content-Type': 'application/json',
147
- 'X-DevGuard-Signature': signature,
148
- 'X-DevGuard-Timestamp': timestamp,
149
- 'X-DevGuard-Tunnel': 'v1-gzip',
150
- ...(authSecret ? { 'X-DevGuard-API-Key': authSecret } : {})
151
- },
152
- body: JSON.stringify({
153
- projectId,
154
- deviceId: metadata.deviceId,
155
- deviceName: metadata.deviceName,
156
- model: metadata.model,
157
- os: metadata.os,
158
- version: metadata.version,
159
- isPhysicalDevice: metadata.isPhysicalDevice,
160
- username: metadata.username,
161
- email: metadata.email,
162
- phone: metadata.phone,
163
- customData: metadata.customData,
164
- fingerprint: metadata.fingerprint,
165
- deviceToken: metadata.deviceToken,
166
- location: metadata.location,
167
- p: base64Payload
168
- })
169
- };
170
- const fetchResponse = await fetch(fetchUrl, fetchOptions);
171
- if (!fetchResponse.ok) {
172
- throw new Error('API_UNREACHABLE');
99
+ const run = async () => {
100
+ if (!shouldSync(force, trigger)) {
101
+ console.log(obf_1.Obf.syncSkipped);
102
+ return;
173
103
  }
174
- const responseText = await fetchResponse.text();
175
- const result = JSON.parse(responseText);
176
- // Verify Response Signature
177
- const responseSig = fetchResponse.headers.get('X-DevGuard-Response-Signature');
178
- if (responseSig) {
179
- const isValid = await DevGuardNative.verifyResponse(responseText, responseSig);
180
- if (!isValid) {
181
- if (result.betaFeatures?.bypassSignature === true) {
182
- console.warn('DevGuard: Response signature mismatch, but bypassSignature is ACTIVE.');
104
+ try {
105
+ const hardware = HardwareService_1.HardwareService.getInstance();
106
+ const deviceId = await hardware.getDeviceId();
107
+ // Use the ref (not the possibly-stale closed-over `response`) so advanced
108
+ // telemetry consent is honored on heartbeat/lifecycle-triggered syncs.
109
+ const metadata = await hardware.getMetadata(responseRef.current?.betaFeatures);
110
+ const timestamp = Date.now().toString();
111
+ // Align payload with Flutter: Metadata map is the core payload 'p'
112
+ const payload = JSON.stringify({
113
+ ...metadata,
114
+ [obf_1.Obf.projectId]: projectId,
115
+ timestamp: parseInt(timestamp, 10)
116
+ });
117
+ // GZip + Base64 Tunneling (v1-gzip)
118
+ const compressed = pako_1.default.gzip(payload);
119
+ const uint8ToBase64 = (data) => {
120
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
121
+ let result = '';
122
+ let i;
123
+ const l = data.length;
124
+ for (i = 2; i < l; i += 3) {
125
+ result += chars[data[i - 2] >> 2];
126
+ result += chars[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)];
127
+ result += chars[((data[i - 1] & 0x0f) << 2) | (data[i] >> 6)];
128
+ result += chars[data[i] & 0x3f];
129
+ }
130
+ if (i === l + 1) {
131
+ result += chars[data[i - 2] >> 2];
132
+ result += chars[(data[i - 2] & 0x03) << 4];
133
+ result += '==';
183
134
  }
184
- else {
185
- console.error('DevGuard: Response signature mismatch! Possible tampering detected.');
186
- setStatus('LOCKED');
187
- return;
135
+ else if (i === l) {
136
+ result += chars[data[i - 2] >> 2];
137
+ result += chars[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)];
138
+ result += chars[(data[i - 1] & 0x0f) << 2];
139
+ result += '=';
188
140
  }
141
+ return result;
142
+ };
143
+ const base64Payload = uint8ToBase64(compressed);
144
+ const signature = await DevGuardNative.generateSignature(projectId, parseInt(timestamp, 10));
145
+ const fetchUrl = obf_1.Obf.defaultApiUrl;
146
+ const fetchOptions = {
147
+ method: 'POST',
148
+ headers: {
149
+ [obf_1.Obf.contentType]: obf_1.Obf.appJson,
150
+ [obf_1.Obf.hdrSig]: signature,
151
+ [obf_1.Obf.hdrTs]: timestamp,
152
+ [obf_1.Obf.hdrTunnel]: obf_1.Obf.tunnelV1,
153
+ ...(authSecret ? { [obf_1.Obf.hdrApiKey]: authSecret } : {})
154
+ },
155
+ body: JSON.stringify({
156
+ [obf_1.Obf.projectId]: projectId,
157
+ [obf_1.Obf.deviceId]: metadata.deviceId,
158
+ [obf_1.Obf.deviceName]: metadata.deviceName,
159
+ [obf_1.Obf.model]: metadata.model,
160
+ [obf_1.Obf.os]: metadata.os,
161
+ [obf_1.Obf.version]: metadata.version,
162
+ [obf_1.Obf.isPhysicalDevice]: metadata.isPhysicalDevice,
163
+ [obf_1.Obf.username]: metadata.username,
164
+ [obf_1.Obf.email]: metadata.email,
165
+ [obf_1.Obf.phone]: metadata.phone,
166
+ [obf_1.Obf.customData]: metadata.customData,
167
+ [obf_1.Obf.fingerprint]: metadata.fingerprint,
168
+ [obf_1.Obf.deviceToken]: metadata.deviceToken,
169
+ [obf_1.Obf.location]: metadata.location,
170
+ [obf_1.Obf.payloadField]: base64Payload
171
+ })
172
+ };
173
+ const fetchResponse = await fetch(fetchUrl, fetchOptions);
174
+ if (!fetchResponse.ok) {
175
+ throw new Error(obf_1.Obf.apiUnreachable);
176
+ }
177
+ const responseText = await fetchResponse.text();
178
+ const result = JSON.parse(responseText);
179
+ // Verify Response Signature
180
+ const responseSig = fetchResponse.headers.get(obf_1.Obf.respSig);
181
+ if (responseSig) {
182
+ const isValid = await DevGuardNative.verifyResponse(responseText, responseSig);
183
+ if (!isValid) {
184
+ if (result[obf_1.Obf.betaFeatures]?.[obf_1.Obf.bypassSignature] === true) {
185
+ console.warn(obf_1.Obf.invalidSigBypassLog);
186
+ }
187
+ else {
188
+ console.error(obf_1.Obf.invalidSigLog);
189
+ const enforced = await GuardEnforcement_1.GuardEnforcement.apply({ ...result, status: 'LOCKED' }, metadata, isCompromisedRef.current);
190
+ responseRef.current = enforced;
191
+ setResponse(enforced);
192
+ setStatus('LOCKED');
193
+ return;
194
+ }
195
+ }
196
+ }
197
+ else {
198
+ console.warn(obf_1.Obf.missingSigLog);
199
+ }
200
+ // Client-side enforcement (emulator / compromised) — parity with Flutter.
201
+ const enforced = await GuardEnforcement_1.GuardEnforcement.apply(result, metadata, isCompromisedRef.current);
202
+ const finalStatus = String(enforced[obf_1.Obf.statusField] || 'ERROR').toUpperCase();
203
+ responseRef.current = enforced; // keep ref fresh for chained/triggered syncs
204
+ setResponse(enforced);
205
+ setStatus(finalStatus);
206
+ setShowWarning(finalStatus === 'WARNING');
207
+ lastSyncTime.current = Date.now();
208
+ CacheService_1.CacheService.saveResponse(projectId, enforced);
209
+ // Handle Beta Features (Parity with Flutter)
210
+ if (finalStatus === 'WARNING' && result.betaFeatures?.vibrateOnWarning === true) {
211
+ react_native_1.Vibration.vibrate();
212
+ }
213
+ const wipeNonce = result.betaFeatures?.wipeNonce;
214
+ if (typeof wipeNonce === 'number' && wipeNonce > 0) {
215
+ await executeRemoteWipe(wipeNonce);
216
+ }
217
+ // Heartbeat management
218
+ startHeartbeat(result);
219
+ // Handle Device Token (Parity with Flutter)
220
+ if (result.deviceToken) {
221
+ await DeviceTokenService_1.DeviceTokenService.getInstance().saveToken(result.deviceToken, result.betaFeatures?.secureEnclave === true);
222
+ }
223
+ // Handle Remote Commands (Parity with Flutter)
224
+ if (result.remoteCommand && result.remoteCommand !== 'none') {
225
+ await handleRemoteCommand(result.remoteCommand);
226
+ }
227
+ // Clear logs after successful sync
228
+ if (metadata.usageLogs && metadata.usageLogs.length > 0) {
229
+ await UsageLogger_1.UsageLogger.getInstance().clearLogs();
189
230
  }
190
231
  }
191
- else {
192
- console.warn('DevGuard: Missing server response signature.');
193
- }
194
- // Client-side enforcement (e.g. blockEmulators) — parity with Flutter.
195
- const enforced = GuardEnforcement_1.GuardEnforcement.apply(result, metadata);
196
- const finalStatus = String(enforced.status || 'ERROR').toUpperCase();
197
- responseRef.current = enforced; // keep ref fresh for chained/triggered syncs
198
- setResponse(enforced);
199
- setStatus(finalStatus);
200
- setShowWarning(finalStatus === 'WARNING');
201
- lastSyncTime.current = Date.now();
202
- CacheService_1.CacheService.saveResponse(projectId, enforced);
203
- // Handle Beta Features (Parity with Flutter)
204
- if (finalStatus === 'WARNING' && result.betaFeatures?.vibrateOnWarning === true) {
205
- react_native_1.Vibration.vibrate();
206
- }
207
- const wipeNonce = result.betaFeatures?.wipeNonce;
208
- if (typeof wipeNonce === 'number' && wipeNonce > 0) {
209
- await executeRemoteWipe(wipeNonce);
210
- }
211
- // Heartbeat management
212
- startHeartbeat(result);
213
- // Handle Device Token (Parity with Flutter)
214
- if (result.deviceToken) {
215
- await DeviceTokenService_1.DeviceTokenService.getInstance().saveToken(result.deviceToken, result.betaFeatures?.secureEnclave === true);
216
- }
217
- // Handle Remote Commands (Parity with Flutter)
218
- if (result.remoteCommand && result.remoteCommand !== 'none') {
219
- await handleRemoteCommand(result.remoteCommand);
220
- }
221
- // Clear logs after successful sync
222
- if (metadata.usageLogs && metadata.usageLogs.length > 0) {
223
- await UsageLogger_1.UsageLogger.getInstance().clearLogs();
224
- }
225
- }
226
- catch (e) {
227
- console.error('DevGuard Verification Failed:', e);
228
- if (failSafe === 'open' && status === 'PENDING') {
229
- setStatus('ACTIVE');
230
- }
231
- else if (failSafe === 'closed') {
232
- setStatus('LOCKED');
232
+ catch (e) {
233
+ console.error('DevGuard Verification Failed:', e);
234
+ if (failSafe === 'open' && status === 'PENDING') {
235
+ setStatus('ACTIVE');
236
+ }
237
+ else if (failSafe === 'closed') {
238
+ setStatus('LOCKED');
239
+ }
233
240
  }
234
- }
241
+ };
242
+ // Always serialize verify runs — never reset the chain on force, or parallel
243
+ // fetches race and a stale WARNING response can overwrite LOCKED (physical E2E).
244
+ const chained = verifyChainRef.current.then(() => run()).catch(() => { });
245
+ verifyChainRef.current = chained;
246
+ return chained;
235
247
  };
236
248
  const startHeartbeat = (res) => {
237
249
  if (heartbeatTimer.current)
@@ -287,20 +299,20 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
287
299
  const hashedKey = crypto_js_1.default.SHA256(key).toString();
288
300
  const timestamp = Date.now().toString();
289
301
  const signature = await DevGuardNative.generateSignature(projectId, parseInt(timestamp, 10));
290
- const payload = JSON.stringify({ projectId, providedKey: hashedKey });
291
- const fetchResponse = await fetch(`${API_URL}/unlock`, {
302
+ const payload = JSON.stringify({ [obf_1.Obf.projectId]: projectId, [obf_1.Obf.providedKey]: hashedKey });
303
+ const fetchResponse = await fetch(`${obf_1.Obf.defaultApiUrl}${obf_1.Obf.unlockPath}`, {
292
304
  method: 'POST',
293
305
  headers: {
294
- 'Content-Type': 'application/json',
295
- 'X-DevGuard-Signature': signature,
296
- 'X-DevGuard-Timestamp': timestamp,
297
- ...(authSecret ? { 'X-DevGuard-API-Key': authSecret } : {})
306
+ [obf_1.Obf.contentType]: obf_1.Obf.appJson,
307
+ [obf_1.Obf.hdrSig]: signature,
308
+ [obf_1.Obf.hdrTs]: timestamp,
309
+ ...(authSecret ? { [obf_1.Obf.hdrApiKey]: authSecret } : {})
298
310
  },
299
311
  body: payload
300
312
  });
301
313
  if (fetchResponse.ok) {
302
314
  const text = await fetchResponse.text();
303
- if (text === 'Unlocked') {
315
+ if (text === obf_1.Obf.unlocked) {
304
316
  await verify();
305
317
  return true;
306
318
  }
@@ -332,13 +344,10 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
332
344
  const deviceId = await HardwareService_1.HardwareService.getInstance().getDeviceId();
333
345
  await DevGuardLogger_1.DevGuardLogger.init(deviceId);
334
346
  const isJailbroken = await react_native_jailbreak_1.default.check();
347
+ isCompromisedRef.current = isJailbroken;
335
348
  if (isJailbroken) {
336
- console.warn('DevGuard Security Alert: Compromised device detected.');
337
- setResponse({
338
- status: 'LOCKED',
339
- title: 'Security Alert',
340
- message: 'This application cannot run on jailbroken or rooted devices for security reasons.',
341
- });
349
+ const locked = await GuardEnforcement_1.GuardEnforcement.apply({ status: 'ACTIVE' }, { isPhysicalDevice: true }, true);
350
+ setResponse(locked);
342
351
  setStatus('LOCKED');
343
352
  return;
344
353
  }
@@ -384,7 +393,7 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
384
393
  }, []);
385
394
  const isLocked = status === 'LOCKED' || status === 'EXPIRED';
386
395
  const showBugIcon = response?.betaFeatures?.showDiagnosticLogs === true;
387
- return ((0, jsx_runtime_1.jsxs)(DevGuardContext.Provider, { value: { status, verify, unlock, setDeviceUser, isLocked, response }, children: [showWarning && status === 'WARNING' && ((0, jsx_runtime_1.jsx)(SecurityToast_1.SecurityToast, { title: response?.title || 'Security Warning', message: response?.message })), autoProtect && isLocked ? ((0, jsx_runtime_1.jsx)(LockScreen_1.LockScreen, { status: status, title: response?.title, message: response?.message, contactEmail: response?.contactEmail, contactPhone: response?.contactPhone, contactWhatsapp: response?.contactWhatsapp, allowUnlock: response?.allowUnlock, onUnlock: unlock })) : ((0, jsx_runtime_1.jsxs)(react_native_2.View, { style: { flex: 1 }, children: [children, showBugIcon && ((0, jsx_runtime_1.jsx)(react_native_safe_area_context_1.SafeAreaInsetsContext.Consumer, { children: (insets) => {
396
+ return ((0, jsx_runtime_1.jsxs)(DevGuardContext.Provider, { value: { status, verify, unlock, setDeviceUser, isLocked, response }, children: [showWarning && status === 'WARNING' && ((0, jsx_runtime_1.jsx)(SecurityToast_1.SecurityToast, { title: response?.title || 'Security Warning', message: response?.message })), autoProtect && isLocked ? ((0, jsx_runtime_1.jsxs)(react_native_2.View, { style: { flex: 1 }, children: [(0, jsx_runtime_1.jsx)(LockScreen_1.LockScreen, { status: status, title: response?.title, message: response?.message, contactEmail: response?.contactEmail, contactPhone: response?.contactPhone, contactWhatsapp: response?.contactWhatsapp, allowUnlock: response?.allowUnlock === true, branding: response?.branding, onUnlock: unlock }), (0, jsx_runtime_1.jsx)(react_native_2.View, { style: styles.hiddenWhileLocked, pointerEvents: "none", children: children })] })) : ((0, jsx_runtime_1.jsxs)(react_native_2.View, { style: { flex: 1 }, children: [children, showBugIcon && ((0, jsx_runtime_1.jsx)(react_native_safe_area_context_1.SafeAreaInsetsContext.Consumer, { children: (insets) => {
388
397
  const safeInsets = insets || { top: 0, bottom: 0, right: 0, left: 0 };
389
398
  return ((0, jsx_runtime_1.jsx)(react_native_2.TouchableOpacity, { style: [
390
399
  styles.bugIcon,
@@ -394,6 +403,13 @@ const DevGuardProvider = ({ children, projectId, secret, apiKey, autoProtect = t
394
403
  };
395
404
  exports.DevGuardProvider = DevGuardProvider;
396
405
  const styles = react_native_2.StyleSheet.create({
406
+ hiddenWhileLocked: {
407
+ position: 'absolute',
408
+ width: 1,
409
+ height: 1,
410
+ opacity: 0,
411
+ overflow: 'hidden',
412
+ },
397
413
  bugIcon: {
398
414
  position: 'absolute',
399
415
  right: 20,
@@ -417,3 +433,5 @@ const useDevGuard = () => {
417
433
  return context;
418
434
  };
419
435
  exports.useDevGuard = useDevGuard;
436
+ var branding_1 = require("./types/branding");
437
+ Object.defineProperty(exports, "resolveBrandingFooter", { enumerable: true, get: function () { return branding_1.resolveBrandingFooter; } });
@@ -0,0 +1,72 @@
1
+ /** Auto-generated by tool/genObf.js — do not edit by hand. */
2
+ export declare const Obf: {
3
+ readonly symX9: string;
4
+ readonly symV2: string;
5
+ readonly symS3: string;
6
+ readonly symG4: string;
7
+ readonly symH5: string;
8
+ readonly symX6: string;
9
+ readonly symD7: string;
10
+ readonly symR8: string;
11
+ readonly symE1: string;
12
+ readonly contentType: string;
13
+ readonly appJson: string;
14
+ readonly hdrSig: string;
15
+ readonly hdrTs: string;
16
+ readonly hdrTunnel: string;
17
+ readonly hdrApiKey: string;
18
+ readonly respSig: string;
19
+ readonly tunnelV1: string;
20
+ readonly projectId: string;
21
+ readonly deviceId: string;
22
+ readonly deviceName: string;
23
+ readonly model: string;
24
+ readonly os: string;
25
+ readonly version: string;
26
+ readonly isPhysicalDevice: string;
27
+ readonly username: string;
28
+ readonly email: string;
29
+ readonly phone: string;
30
+ readonly customData: string;
31
+ readonly fingerprint: string;
32
+ readonly deviceToken: string;
33
+ readonly location: string;
34
+ readonly payloadField: string;
35
+ readonly unlockPath: string;
36
+ readonly providedKey: string;
37
+ readonly unlocked: string;
38
+ readonly defaultApiUrl: string;
39
+ readonly betaFeatures: string;
40
+ readonly bypassSignature: string;
41
+ readonly statusField: string;
42
+ readonly emulatorTitle: string;
43
+ readonly emulatorMessage: string;
44
+ readonly securityTitle: string;
45
+ readonly compromisedMessage: string;
46
+ readonly compromisedLog: string;
47
+ readonly missingSigLog: string;
48
+ readonly invalidSigBypassLog: string;
49
+ readonly invalidSigLog: string;
50
+ readonly invalidUnlockKey: string;
51
+ readonly enterUnlockKey: string;
52
+ readonly licenseKeyHint: string;
53
+ readonly cancelLabel: string;
54
+ readonly unlockLabel: string;
55
+ readonly whatsappSupport: string;
56
+ readonly emailSupport: string;
57
+ readonly callSupport: string;
58
+ readonly defaultLockMessage: string;
59
+ readonly contactOpenError: string;
60
+ readonly poweredBy: string;
61
+ readonly securedBy: string;
62
+ readonly brandDefault: string;
63
+ readonly brandWebsite: string;
64
+ readonly apiUnreachable: string;
65
+ readonly syncSkipped: string;
66
+ };
67
+ /** Native policy gate result codes (dg_e1). */
68
+ export declare const PolicyLock: {
69
+ readonly allow: 0;
70
+ readonly emulator: 1;
71
+ readonly compromised: 2;
72
+ };
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /** Auto-generated by tool/genObf.js — do not edit by hand. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.PolicyLock = exports.Obf = void 0;
5
+ const MASK = [0xF1, 0x3A, 0xC7, 0x82, 0x5E, 0xD9, 0x44, 0xAB];
6
+ function _d(encoded) {
7
+ const bytes = Uint8Array.from(encoded.map((b, i) => b ^ MASK[i % MASK.length]));
8
+ let out = '';
9
+ let i = 0;
10
+ while (i < bytes.length) {
11
+ const c = bytes[i++];
12
+ if (c < 0x80)
13
+ out += String.fromCharCode(c);
14
+ else if (c < 0xe0)
15
+ out += String.fromCharCode(((c & 0x1f) << 6) | (bytes[i++] & 0x3f));
16
+ else if (c < 0xf0) {
17
+ out += String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i++] & 0x3f) << 6) | (bytes[i++] & 0x3f));
18
+ }
19
+ else {
20
+ const code = ((c & 0x07) << 18) |
21
+ ((bytes[i++] & 0x3f) << 12) |
22
+ ((bytes[i++] & 0x3f) << 6) |
23
+ (bytes[i++] & 0x3f);
24
+ const offset = code - 0x10000;
25
+ out += String.fromCharCode(0xd800 + (offset >> 10), 0xdc00 + (offset & 0x3ff));
26
+ }
27
+ }
28
+ return out;
29
+ }
30
+ exports.Obf = {
31
+ get symX9() { return _d([149, 93, 152, 250, 103]); },
32
+ get symV2() { return _d([149, 93, 152, 244, 108]); },
33
+ get symS3() { return _d([149, 93, 152, 241, 109]); },
34
+ get symG4() { return _d([149, 93, 152, 229, 106]); },
35
+ get symH5() { return _d([149, 93, 152, 234, 107]); },
36
+ get symX6() { return _d([149, 93, 152, 250, 104]); },
37
+ get symD7() { return _d([149, 93, 152, 230, 105]); },
38
+ get symR8() { return _d([149, 93, 152, 240, 102]); },
39
+ get symE1() { return _d([149, 93, 152, 231, 111]); },
40
+ get contentType() { return _d([178, 85, 169, 246, 59, 183, 48, 134, 165, 67, 183, 231]); },
41
+ get appJson() { return _d([144, 74, 183, 238, 55, 186, 37, 223, 152, 85, 169, 173, 52, 170, 43, 197]); },
42
+ get hdrSig() { return _d([169, 23, 131, 231, 40, 158, 49, 202, 131, 94, 234, 209, 55, 190, 42, 202, 133, 79, 181, 231]); },
43
+ get hdrTs() { return _d([169, 23, 131, 231, 40, 158, 49, 202, 131, 94, 234, 214, 55, 180, 33, 216, 133, 91, 170, 242]); },
44
+ get hdrTunnel() { return _d([169, 23, 131, 231, 40, 158, 49, 202, 131, 94, 234, 214, 43, 183, 42, 206, 157]); },
45
+ get hdrApiKey() { return _d([169, 23, 131, 231, 40, 158, 49, 202, 131, 94, 234, 195, 14, 144, 105, 224, 148, 67]); },
46
+ get respSig() { return _d([137, 23, 163, 231, 40, 190, 49, 202, 131, 94, 234, 240, 59, 170, 52, 196, 159, 73, 162, 175, 45, 176, 35, 197, 144, 78, 178, 240, 59]); },
47
+ get tunnelV1() { return _d([135, 11, 234, 229, 36, 176, 52]); },
48
+ get projectId() { return _d([129, 72, 168, 232, 59, 186, 48, 226, 149]); },
49
+ get deviceId() { return _d([149, 95, 177, 235, 61, 188, 13, 207]); },
50
+ get deviceName() { return _d([149, 95, 177, 235, 61, 188, 10, 202, 156, 95]); },
51
+ get model() { return _d([156, 85, 163, 231, 50]); },
52
+ get os() { return _d([158, 73]); },
53
+ get version() { return _d([135, 95, 181, 241, 55, 182, 42]); },
54
+ get isPhysicalDevice() { return _d([152, 73, 151, 234, 39, 170, 45, 200, 144, 86, 131, 231, 40, 176, 39, 206]); },
55
+ get username() { return _d([132, 73, 162, 240, 48, 184, 41, 206]); },
56
+ get email() { return _d([148, 87, 166, 235, 50]); },
57
+ get phone() { return _d([129, 82, 168, 236, 59]); },
58
+ get customData() { return _d([146, 79, 180, 246, 49, 180, 0, 202, 133, 91]); },
59
+ get fingerprint() { return _d([151, 83, 169, 229, 59, 171, 52, 217, 152, 84, 179]); },
60
+ get deviceToken() { return _d([149, 95, 177, 235, 61, 188, 16, 196, 154, 95, 169]); },
61
+ get location() { return _d([157, 85, 164, 227, 42, 176, 43, 197]); },
62
+ get payloadField() { return _d([129]); },
63
+ get unlockPath() { return _d([222, 79, 169, 238, 49, 186, 47]); },
64
+ get providedKey() { return _d([129, 72, 168, 244, 55, 189, 33, 207, 186, 95, 190]); },
65
+ get unlocked() { return _d([164, 84, 171, 237, 61, 178, 33, 207]); },
66
+ get defaultApiUrl() { return _d([153, 78, 179, 242, 45, 227, 107, 132, 144, 74, 174, 172, 58, 188, 50, 204, 132, 91, 181, 230, 112, 172, 47, 132, 149, 95, 177, 229, 43, 184, 54, 207]); },
67
+ get betaFeatures() { return _d([147, 95, 179, 227, 24, 188, 37, 223, 132, 72, 162, 241]); },
68
+ get bypassSignature() { return _d([147, 67, 183, 227, 45, 170, 23, 194, 150, 84, 166, 246, 43, 171, 33]); },
69
+ get statusField() { return _d([130, 78, 166, 246, 43, 170]); },
70
+ get emulatorTitle() { return _d([180, 87, 178, 238, 63, 173, 43, 217, 209, 126, 162, 246, 59, 186, 48, 206, 149]); },
71
+ get emulatorMessage() { return _d([165, 82, 174, 241, 126, 184, 52, 219, 157, 83, 164, 227, 42, 176, 43, 197, 209, 89, 166, 236, 48, 182, 48, 139, 131, 79, 169, 162, 49, 183, 100, 206, 156, 79, 171, 227, 42, 182, 54, 216, 209, 85, 181, 162, 45, 176, 41, 222, 157, 91, 179, 237, 44, 170, 100, 205, 158, 72, 231, 241, 59, 186, 49, 217, 152, 78, 190, 162, 44, 188, 37, 216, 158, 84, 180, 172]); },
72
+ get securityTitle() { return _d([162, 95, 164, 247, 44, 176, 48, 210, 209, 123, 171, 231, 44, 173]); },
73
+ get compromisedMessage() { return _d([165, 82, 174, 241, 126, 184, 52, 219, 157, 83, 164, 227, 42, 176, 43, 197, 209, 89, 166, 236, 48, 182, 48, 139, 131, 79, 169, 162, 49, 183, 100, 193, 144, 83, 171, 224, 44, 182, 47, 206, 159, 26, 168, 240, 126, 171, 43, 196, 133, 95, 163, 162, 58, 188, 50, 194, 146, 95, 180, 162, 56, 182, 54, 139, 130, 95, 164, 247, 44, 176, 48, 210, 209, 72, 162, 227, 45, 182, 42, 216, 223]); },
74
+ get compromisedLog() { return _d([181, 95, 177, 197, 43, 184, 54, 207, 209, 105, 162, 225, 43, 171, 45, 223, 136, 26, 134, 238, 59, 171, 48, 145, 209, 121, 168, 239, 46, 171, 43, 198, 152, 73, 162, 230, 126, 189, 33, 221, 152, 89, 162, 162, 58, 188, 48, 206, 146, 78, 162, 230, 112]); },
75
+ get missingSigLog() { return _d([181, 95, 177, 197, 43, 184, 54, 207, 209, 105, 162, 225, 43, 171, 45, 223, 136, 26, 134, 238, 59, 171, 48, 145, 209, 119, 174, 241, 45, 176, 42, 204, 209, 73, 162, 240, 40, 188, 54, 139, 131, 95, 180, 242, 49, 183, 55, 206, 209, 73, 174, 229, 48, 184, 48, 222, 131, 95, 233]); },
76
+ get invalidSigBypassLog() { return _d([181, 95, 177, 197, 43, 184, 54, 207, 209, 105, 162, 225, 43, 171, 45, 223, 136, 26, 144, 227, 44, 183, 45, 197, 150, 0, 231, 203, 48, 175, 37, 199, 152, 94, 231, 241, 59, 171, 50, 206, 131, 26, 181, 231, 45, 169, 43, 197, 130, 95, 231, 241, 55, 190, 42, 202, 133, 79, 181, 231, 114, 249, 38, 222, 133, 26, 165, 251, 46, 184, 55, 216, 162, 83, 160, 236, 63, 173, 49, 217, 148, 26, 174, 241, 126, 152, 7, 255, 184, 108, 130, 172]); },
77
+ get invalidSigLog() { return _d([181, 95, 177, 197, 43, 184, 54, 207, 209, 105, 162, 225, 43, 171, 45, 223, 136, 26, 134, 238, 59, 171, 48, 145, 209, 115, 169, 244, 63, 181, 45, 207, 209, 73, 162, 240, 40, 188, 54, 139, 131, 95, 180, 242, 49, 183, 55, 206, 209, 73, 174, 229, 48, 184, 48, 222, 131, 95, 230, 162, 14, 182, 55, 216, 152, 88, 171, 231, 126, 173, 37, 198, 129, 95, 181, 235, 48, 190, 100, 207, 148, 78, 162, 225, 42, 188, 32, 133]); },
78
+ get invalidUnlockKey() { return _d([184, 84, 177, 227, 50, 176, 32, 139, 132, 84, 171, 237, 61, 178, 100, 192, 148, 67, 233, 162, 14, 181, 33, 202, 130, 95, 231, 246, 44, 160, 100, 202, 150, 91, 174, 236, 112]); },
79
+ get enterUnlockKey() { return _d([1, 165, 83, 19, 126, 156, 42, 223, 148, 72, 231, 215, 48, 181, 43, 200, 154, 26, 140, 231, 39]); },
80
+ get licenseKeyHint() { return _d([180, 84, 179, 231, 44, 249, 8, 194, 146, 95, 169, 241, 59, 249, 15, 206, 136]); },
81
+ get cancelLabel() { return _d([178, 91, 169, 225, 59, 181]); },
82
+ get unlockLabel() { return _d([164, 84, 171, 237, 61, 178]); },
83
+ get whatsappSupport() { return _d([166, 82, 166, 246, 45, 152, 52, 219, 209, 105, 178, 242, 46, 182, 54, 223]); },
84
+ get emailSupport() { return _d([180, 87, 166, 235, 50, 249, 23, 222, 129, 74, 168, 240, 42]); },
85
+ get callSupport() { return _d([178, 91, 171, 238, 126, 138, 49, 219, 129, 85, 181, 246]); },
86
+ get defaultLockMessage() { return _d([165, 82, 174, 241, 126, 184, 52, 219, 157, 83, 164, 227, 42, 176, 43, 197, 209, 82, 166, 241, 126, 187, 33, 206, 159, 26, 181, 231, 51, 182, 48, 206, 157, 67, 231, 238, 49, 186, 47, 206, 149, 26, 165, 251, 126, 173, 44, 206, 209, 94, 162, 244, 59, 181, 43, 219, 148, 72, 233]); },
87
+ get contactOpenError() { return _d([178, 85, 178, 238, 58, 249, 42, 196, 133, 26, 168, 242, 59, 183, 100, 223, 153, 95, 231, 225, 49, 183, 48, 202, 146, 78, 231, 227, 46, 169, 40, 194, 146, 91, 179, 235, 49, 183, 106, 139, 161, 86, 162, 227, 45, 188, 100, 198, 144, 81, 162, 162, 45, 172, 54, 206, 209, 83, 179, 162, 55, 170, 100, 194, 159, 73, 179, 227, 50, 181, 33, 207, 209, 91, 169, 230, 126, 186, 43, 197, 151, 83, 160, 247, 44, 188, 32, 133]); },
88
+ get poweredBy() { return _d([161, 85, 176, 231, 44, 188, 32, 139, 147, 67]); },
89
+ get securedBy() { return _d([162, 95, 164, 247, 44, 188, 32, 139, 147, 67]); },
90
+ get brandDefault() { return _d([181, 95, 177, 197, 43, 184, 54, 207]); },
91
+ get brandWebsite() { return _d([153, 78, 179, 242, 45, 227, 107, 132, 149, 95, 177, 229, 43, 184, 54, 207, 223, 79, 172]); },
92
+ get apiUnreachable() { return _d([176, 106, 142, 221, 11, 151, 22, 238, 176, 121, 143, 195, 28, 149, 1]); },
93
+ get syncSkipped() { return _d([181, 95, 177, 197, 43, 184, 54, 207, 203, 26, 148, 251, 48, 186, 100, 216, 154, 83, 183, 242, 59, 189, 100, 207, 132, 95, 231, 246, 49, 249, 52, 196, 157, 83, 164, 251, 112]); },
94
+ };
95
+ /** Native policy gate result codes (dg_e1). */
96
+ exports.PolicyLock = {
97
+ allow: 0,
98
+ emulator: 1,
99
+ compromised: 2,
100
+ };
@@ -32,22 +32,17 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.CacheService = void 0;
40
37
  const Keychain = __importStar(require("react-native-keychain"));
41
- const crypto_js_1 = __importDefault(require("crypto-js"));
38
+ const cacheCrypto_1 = require("./cacheCrypto");
42
39
  class CacheService {
43
40
  static SERVICE_NAME = 'devguard.cache.service';
44
41
  static WIPE_SERVICE = 'devguard.wipe.service';
45
42
  static async saveResponse(projectId, response) {
46
43
  try {
47
44
  const dataString = JSON.stringify(response);
48
- // Use projectId as the encryption key for the payload
49
- const encrypted = crypto_js_1.default.AES.encrypt(dataString, projectId).toString();
50
- // Store in secure keychain
45
+ const encrypted = (0, cacheCrypto_1.encryptCachePayload)(projectId, dataString);
51
46
  await Keychain.setGenericPassword(`devguard_${projectId}`, encrypted, { service: this.SERVICE_NAME });
52
47
  }
53
48
  catch (e) {
@@ -58,10 +53,7 @@ class CacheService {
58
53
  try {
59
54
  const credentials = await Keychain.getGenericPassword({ service: this.SERVICE_NAME });
60
55
  if (credentials && credentials.username === `devguard_${projectId}`) {
61
- const encrypted = credentials.password;
62
- // Decrypt using projectId
63
- const bytes = crypto_js_1.default.AES.decrypt(encrypted, projectId);
64
- const decrypted = bytes.toString(crypto_js_1.default.enc.Utf8);
56
+ const decrypted = (0, cacheCrypto_1.decryptCachePayload)(projectId, credentials.password);
65
57
  if (decrypted) {
66
58
  return JSON.parse(decrypted);
67
59
  }
@@ -1,19 +1,10 @@
1
1
  /**
2
2
  * Client-side policy enforcement applied to a server response before it is
3
- * shown to the user. Mirrors the Flutter plugin's `GuardEnforcement` so both
4
- * platforms behave identically.
3
+ * shown to the user. Mirrors Flutter `GuardEnforcement` + native `dg_e1`.
5
4
  */
6
5
  export declare class GuardEnforcement {
7
- /**
8
- * Applies enforcement rules to a fresh server response.
9
- *
10
- * - `blockEmulators`: if the project blocks emulators and the current device
11
- * is not a physical device, the response is overridden to LOCKED.
12
- *
13
- * @param response The parsed server response (mutated copy returned).
14
- * @param metadata The device metadata collected for this sync.
15
- */
6
+ private static evaluatePolicy;
16
7
  static apply(response: any, metadata: {
17
8
  isPhysicalDevice?: boolean;
18
- }): any;
9
+ }, isCompromised?: boolean): Promise<any>;
19
10
  }