@gatekeeperx/cordova-plugin-devicex 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/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ > **Nota:** La versión del plugin Cordova es independiente de la versión del SDK nativo (AAR).
4
+ > Cada release del plugin indica qué versión del SDK nativo incluye.
5
+
6
+ ## [0.1.0] - 2026-02-10
7
+
8
+ ### Added
9
+ - `getPluginVersion()` — retorna la versión del plugin Cordova (string estático).
10
+ - Versionamiento independiente del plugin Cordova vs SDK nativo.
11
+
12
+ ### Changed
13
+ - Plugin Cordova ahora usa versión `0.1.0` (antes usaba la misma del SDK nativo).
14
+ - `getVersion()` sigue retornando la versión del **SDK nativo** (AAR).
15
+
16
+ ### SDK nativo incluido
17
+ - Device Intelligence SDK: **1.3.x** (AAR)
18
+ - Bridge: `devicex-bridge.aar`
19
+
20
+ ---
21
+
22
+ ## [1.0.0] - 2026-01-19 (versión legacy, alineada con SDK)
23
+
24
+ ### Added
25
+ - Initial release
26
+ - Android support with cordova-android >= 11
27
+ - Pre-compiled AAR bridge (Kotlin)
28
+ - Device Intelligence SDK integration
29
+ - Auto-configuration via plugin variables
30
+ - TypeScript definitions
31
+
32
+ ### SDK nativo incluido
33
+ - Device Intelligence SDK: **1.0.0** (AAR)
package/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Apache License 2.0
2
+ Copyright 2026 GatekeeperX
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # @gatekeeperx/cordova-plugin-devicex
2
+
3
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
+
5
+ Plugin oficial de Cordova para la integración del SDK **GatekeeperX Device Intelligence** en aplicaciones Android. Proporciona capacidades avanzadas de fingerprinting, detección de fraude (root, emuladores, hooks) y telemetría de seguridad.
6
+
7
+ ---
8
+
9
+ ## 📋 Requisitos
10
+
11
+ Para integrar este plugin, asegúrate de cumplir con los siguientes requisitos:
12
+
13
+ - **Cordova Android**: Versión `11.0.0` o superior.
14
+ - **Java**: JDK 11 o 17.
15
+ - **Android SDK**: API Level 21+ soportado (API 31+ recomendado).
16
+ - **GatekeeperX Credentials**: Tenant ID y API Key válidos.
17
+
18
+ ---
19
+
20
+ ## 🔧 Instalación
21
+
22
+
23
+ ### Prerequisitos
24
+
25
+ Asegúrate de tener instalado Node.js y npm en tu máquina. ejecuta este comando para instalar el plugin:
26
+
27
+
28
+ ```bash
29
+ npm i @gatekeeperx/cordova-plugin-devicex
30
+ ```
31
+
32
+ ### Instalación
33
+
34
+ Instala el plugin utilizando el CLI de Cordova o Ionic. Esto configurará automáticamente las dependencias nativas en tu proyecto.
35
+
36
+ ```bash
37
+ # Usando Ionic CLI (Recomendado)
38
+ ionic cordova plugin add @gatekeeperx/cordova-plugin-devicex
39
+
40
+ # Usando Cordova CLI
41
+ cordova plugin add @gatekeeperx/cordova-plugin-devicex
42
+ ```
43
+
44
+ ### Configuración de Plataforma
45
+
46
+ Asegúrate de tener la plataforma Android configurada correctamente:
47
+
48
+ ```bash
49
+ cordova platform add android@11
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 📚 API Reference
55
+
56
+ El plugin expone el objeto global `Devicex` una vez que el evento `deviceready` se ha disparado.
57
+
58
+ ### 1. `Devicex.configure(options)`
59
+ Inicializa el SDK con las credenciales de tu proyecto. **Debe llamarse antes que cualquier otro método.**
60
+
61
+ **Parámetros:**
62
+ - `options` (`ConfigureOptions`):
63
+ - `tenant` (string, requerido): ID de tu tenant.
64
+ - `apiKey` (string, requerido): Tu clave de API.
65
+ - `environment` (string, opcional): `'SANDBOX'`, o `'PRODUCTION'`.
66
+ - `organizationId` (string, opcional): Identificador de organización.
67
+
68
+ **Ejemplo:**
69
+ ```typescript
70
+ await Devicex.configure({
71
+ tenant: 'mi-tenant',
72
+ apiKey: 'tu-api-key',
73
+ environment: 'PRODUCTION'
74
+ });
75
+ ```
76
+
77
+ ### 2. `Devicex.sendEvent(name, properties?, headers?)`
78
+ Envía un evento de telemetría con señales de seguridad adjuntas automáticamente.
79
+
80
+ **Parámetros:**
81
+ - `name` (string, requerido): Nombre del evento (ej: `'login'`, `'checkout'`).
82
+ - `properties` (object, opcional): Metadatos adicionales del evento.
83
+ - `headers` (object, opcional): Cabeceras HTTP personalizadas.
84
+
85
+ **Ejemplo:**
86
+ ```typescript
87
+ const result = await Devicex.sendEvent('login', { user: 'id_123' });
88
+ if (result.ok) {
89
+ console.log('DeviceX ID:', result.deviceXId);
90
+ }
91
+ ```
92
+
93
+ ### 3. `Devicex.isInitialized()`
94
+ Verifica si el SDK ha sido configurado correctamente.
95
+
96
+ **Retorna:** `Promise<boolean>`
97
+
98
+ ### 4. `Devicex.getVersion()`
99
+ Obtiene la versión del SDK nativo subyacente.
100
+
101
+ **Retorna:** `Promise<string>`
102
+
103
+ ---
104
+
105
+ ## 🚀 Uso Rápido (Ionic/Angular)
106
+
107
+ ```typescript
108
+ // En app.component.ts
109
+ async initializeApp() {
110
+ await this.platform.ready();
111
+
112
+ if (this.platform.is('cordova')) {
113
+ declare const Devicex: any;
114
+
115
+ try {
116
+ await Devicex.configure({
117
+ tenant: 'gatekeeperx', // tu tenant
118
+ apiKey: 'YOUR_API_KEY', // tu api key
119
+ environment: 'PRODUCTION' // PRODUCTION o SANDBOX
120
+ });
121
+ console.log('DeviceX Ready');
122
+ } catch (err) {
123
+ console.error('DeviceX Init Error', err);
124
+ }
125
+ }
126
+ }
127
+
128
+
129
+ async login() {
130
+ //,,, logica previo a enviar el login
131
+
132
+ loading.present();
133
+ try {
134
+ // Enviar evento de login para obtener deviceXId
135
+ const result = await this.deviceX.sendEvent('login', {
136
+ username: this.username,
137
+ method: 'password',
138
+ timestamp: new Date().toISOString(),
139
+ screen: 'login'
140
+ });
141
+ console.log('Evento enviado, deviceXId:', result.deviceXId);
142
+
143
+
144
+ /** EVALUACION DE RIESGO
145
+ * Evaluar riesgo con GatekeeperX - debe hacerse back to back, no en el cliente.
146
+ * debe ser dentro del proceso del login en el backend
147
+ * si la decision es allow, se puede continuar con el login
148
+ * si la decision es deny, se debe denegar el acceso
149
+ * si la decision es unknown, se debe denegar el acceso
150
+ */
151
+
152
+ //invocar proceso de login en el backend
153
+ const response = await this.http.post(BACKEND_URL, {
154
+ username: this.username,
155
+ password: this.password,
156
+ deviceXId: result.deviceXId
157
+ });
158
+ console.log('Login response:', JSON.stringify(response, null, 2));
159
+
160
+ loading.dismiss();
161
+
162
+
163
+ console.log('Login event result:', JSON.stringify(result, null, 2));
164
+
165
+ } catch (error) {
166
+ loading.dismiss();
167
+ console.error('Login event error:', error);
168
+ this.showAlert('Error', 'No se pudo enviar el evento de login', 'error');
169
+ }
170
+ }
171
+ ```
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@gatekeeperx/cordova-plugin-devicex",
3
+ "version": "0.1.0",
4
+ "description": "GatekeeperX Device Intelligence Cordova plugin (Android)",
5
+ "license": "Apache-2.0",
6
+ "keywords": [
7
+ "cordova",
8
+ "cordova-android",
9
+ "gatekeeperx",
10
+ "device",
11
+ "security",
12
+ "intelligence"
13
+ ],
14
+ "engines": [
15
+ {
16
+ "name": "cordova-android",
17
+ "version": ">=11.0.0"
18
+ }
19
+ ],
20
+ "cordova": {
21
+ "id": "cordova-plugin-devicex",
22
+ "platforms": [
23
+ "android"
24
+ ]
25
+ },
26
+ "main": "www/devicex.js",
27
+ "types": "types/index.d.ts",
28
+ "files": [
29
+ "plugin.xml",
30
+ "www/",
31
+ "types/",
32
+ "src/android/",
33
+ "README.md",
34
+ "CHANGELOG.md",
35
+ "LICENSE"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/gatekeeperx/deviceX-plugins.git"
40
+ },
41
+ "author": "GatekeeperX",
42
+ "bugs": {
43
+ "url": "https://github.com/gatekeeperx/deviceX-plugins/issues"
44
+ },
45
+ "homepage": "https://github.com/gatekeeperx/deviceX-plugins/tree/master/cordova/cordova-plugin-devicex"
46
+ }
package/plugin.xml ADDED
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <plugin id="@gatekeeperx/cordova-plugin-devicex"
3
+ xmlns="http://apache.org/cordova/ns/plugins/1.0"
4
+ xmlns:android="http://schemas.android.com/apk/res/android" version="0.1.0">
5
+ <name>Devicex</name>
6
+ <description>GatekeeperX Device Intelligence Cordova plugin (Android)</description>
7
+ <license>Apache-2.0</license>
8
+ <keywords>cordova, gatekeeperx, device, intelligence, security</keywords>
9
+
10
+ <js-module name="Devicex" src="www/devicex.js">
11
+ <clobbers target="DeviceX" />
12
+ </js-module>
13
+
14
+ <engines>
15
+ <engine name="cordova-android" version=">=11.0.0" />
16
+ </engines>
17
+
18
+ <platform name="android">
19
+ <!-- Runtime dependencies -->
20
+ <!-- Kotlin stdlib alineado con el bridge -->
21
+ <framework src="org.jetbrains.kotlin:kotlin-stdlib:1.9.25" />
22
+ <framework src="org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" />
23
+
24
+ <!-- Kotlinx Serialization (requerido por Ktor) -->
25
+ <framework src="org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2" />
26
+
27
+ <!-- Ktor Client (HTTP engine del SDK) -->
28
+ <framework src="io.ktor:ktor-client-cio:2.3.7" />
29
+ <framework src="io.ktor:ktor-client-logging:2.3.7" />
30
+ <framework src="io.ktor:ktor-client-content-negotiation:2.3.7" />
31
+ <framework src="io.ktor:ktor-serialization-kotlinx-json:2.3.7" />
32
+ <!-- AARs locales -->
33
+ <lib-file src="src/android/libs/devicex-bridge.aar" arch="device" />
34
+ <lib-file src="src/android/libs/devicex-1.3.1.aar" arch="device" />
35
+
36
+ <!-- Hook para configurar Gradle -->
37
+ <hook type="after_plugin_install" src="src/android/hooks/configure-gradle.js" />
38
+
39
+ <!-- Registrar el plugin nativo en config.xml -->
40
+ <config-file target="res/xml/config.xml" parent="/*">
41
+ <feature name="Devicex">
42
+ <param name="android-package" value="com.gatekeeperx.cordova.DevicexPlugin" />
43
+ <param name="onload" value="true" />
44
+ </feature>
45
+ </config-file>
46
+
47
+ <!-- Incluir las clases Java del plugin -->
48
+ <source-file src="src/android/com/gatekeeperx/cordova/DevicexPlugin.java" target-dir="src/com/gatekeeperx/cordova" />
49
+ <source-file src="src/android/com/gatekeeperx/cordova/GatekeeperXClient.java" target-dir="src/com/gatekeeperx/cordova" />
50
+ </platform>
51
+ </plugin>
@@ -0,0 +1,240 @@
1
+ package com.gatekeeperx.cordova;
2
+
3
+ import android.content.Context;
4
+
5
+ import org.apache.cordova.CallbackContext;
6
+ import org.apache.cordova.CordovaInterface;
7
+ import org.apache.cordova.CordovaPlugin;
8
+ import org.apache.cordova.CordovaWebView;
9
+ import org.json.JSONArray;
10
+ import org.json.JSONException;
11
+ import org.json.JSONObject;
12
+
13
+ import java.util.ArrayList;
14
+ import java.util.HashMap;
15
+ import java.util.Iterator;
16
+ import java.util.List;
17
+ import java.util.Map;
18
+
19
+ public class DevicexPlugin extends CordovaPlugin {
20
+
21
+ @Override
22
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
23
+ super.initialize(cordova, webView);
24
+ try {
25
+ Context ctx = cordova.getActivity().getApplicationContext();
26
+ android.util.Log.d("DevicexPlugin", "Reading meta-data from manifest...");
27
+
28
+ android.content.pm.ApplicationInfo ai = ctx.getPackageManager()
29
+ .getApplicationInfo(ctx.getPackageName(), android.content.pm.PackageManager.GET_META_DATA);
30
+ android.os.Bundle meta = ai.metaData;
31
+
32
+ if (meta == null) {
33
+ android.util.Log.e("DevicexPlugin", "No meta-data found in manifest");
34
+ return;
35
+ }
36
+
37
+ String tenant = meta.getString("com.gatekeeperx.deviceintelligence.TENANT");
38
+ String apiKey = meta.getString("com.gatekeeperx.deviceintelligence.API_KEY");
39
+ String env = meta.getString("com.gatekeeperx.deviceintelligence.ENVIRONMENT");
40
+ String orgId = meta.getString("com.gatekeeperx.deviceintelligence.ORGANIZATION_ID");
41
+
42
+ android.util.Log.d("DevicexPlugin", "Tenant: " + tenant + ", Env: " + env);
43
+
44
+ if (tenant == null || tenant.trim().isEmpty() || apiKey == null || apiKey.trim().isEmpty()) {
45
+ android.util.Log.d("DevicexPlugin", "Auto-config skipped: no credentials provided");
46
+ return;
47
+ }
48
+
49
+ boolean ok = DevicexBridge.INSTANCE.configure(
50
+ ctx,
51
+ tenant.trim(),
52
+ apiKey.trim(),
53
+ env != null ? env.trim() : "PRODUCTION",
54
+ orgId != null ? orgId.trim() : null,
55
+ null);
56
+
57
+ android.util.Log.d("DevicexPlugin", "Manual configure result: " + ok);
58
+ } catch (Throwable e) {
59
+ android.util.Log.e("DevicexPlugin", "Error initializing: " + e.getMessage(), e);
60
+ }
61
+ }
62
+
63
+ @Override
64
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
65
+ switch (action) {
66
+ case "configure":
67
+ configure(args, callbackContext);
68
+ return true;
69
+ case "sendEvent":
70
+ sendEvent(args, callbackContext);
71
+ return true;
72
+ case "isInitialized":
73
+ try {
74
+ boolean value = DevicexBridge.INSTANCE.isInitialized();
75
+ callbackContext.success(new JSONObject().put("value", value));
76
+ } catch (JSONException e) {
77
+ callbackContext.error(e.getMessage());
78
+ }
79
+ return true;
80
+ case "getVersion":
81
+ try {
82
+ String value = DevicexBridge.INSTANCE.getVersion();
83
+ callbackContext.success(new JSONObject().put("value", value));
84
+ } catch (JSONException e) {
85
+ callbackContext.error(e.getMessage());
86
+ }
87
+ return true;
88
+ // case "evaluateRisk":
89
+ // evaluateRisk(args, callbackContext);
90
+ // return true;
91
+ default:
92
+ return false;
93
+ }
94
+ }
95
+
96
+ private void configure(JSONArray args, CallbackContext cb) {
97
+ try {
98
+ JSONObject obj = args.optJSONObject(0);
99
+ if (obj == null)
100
+ obj = new JSONObject();
101
+ Context ctx = cordova.getActivity().getApplicationContext();
102
+ String tenant = obj.optString("tenant", "").trim();
103
+ String apiKey = obj.optString("apiKey", "").trim();
104
+ String orgId = obj.optString("organizationId", "").trim();
105
+ String env = obj.optString("environment", "PRODUCTION").trim();
106
+ JSONObject headersObj = obj.optJSONObject("headers");
107
+ Map<String, String> headers = headersObj != null ? toStringMap(headersObj) : new HashMap<>();
108
+
109
+ if (tenant.isEmpty() || apiKey.isEmpty()) {
110
+ cb.error("tenant and apiKey are required");
111
+ return;
112
+ }
113
+
114
+ boolean ok = DevicexBridge.INSTANCE.configure(
115
+ ctx,
116
+ tenant,
117
+ apiKey,
118
+ env,
119
+ orgId.isEmpty() ? null : orgId,
120
+ headers);
121
+ if (ok)
122
+ cb.success();
123
+ else
124
+ cb.error("configure failed");
125
+ } catch (Throwable e) {
126
+ cb.error(e.getMessage());
127
+ }
128
+ }
129
+
130
+ private void sendEvent(JSONArray args, final CallbackContext cb) {
131
+ try {
132
+ JSONObject obj = args.optJSONObject(0);
133
+ if (obj == null)
134
+ obj = new JSONObject();
135
+ String name = obj.optString("name", "").trim();
136
+ if (name.isEmpty()) {
137
+ cb.error("name is required");
138
+ return;
139
+ }
140
+ JSONObject propsObj = obj.optJSONObject("properties");
141
+ JSONObject headersObj = obj.optJSONObject("headers");
142
+ Map<String, Object> properties = propsObj != null ? toAnyMap(propsObj) : new HashMap<String, Object>();
143
+ Map<String, String> headers = headersObj != null ? toStringMap(headersObj) : new HashMap<String, String>();
144
+
145
+ @SuppressWarnings("unchecked")
146
+ DevicexBridge.EventCallback callback = new DevicexBridge.EventCallback() {
147
+ @Override
148
+ public void onResult(Map result) {
149
+ try {
150
+ cb.success(new JSONObject(result));
151
+ } catch (Throwable t) {
152
+ cb.error(t.getMessage());
153
+ }
154
+ }
155
+ };
156
+ DevicexBridge.INSTANCE.sendEvent(name, properties, headers, callback);
157
+ } catch (Throwable e) {
158
+ cb.error(e.getMessage());
159
+ }
160
+ }
161
+
162
+ // Helpers
163
+ private static Map<String, String> toStringMap(JSONObject obj) {
164
+ Map<String, String> map = new HashMap<>();
165
+ Iterator<String> it = obj.keys();
166
+ while (it.hasNext()) {
167
+ String k = it.next();
168
+ String v = obj.optString(k, null);
169
+ if (v != null)
170
+ map.put(k, v);
171
+ }
172
+ return map;
173
+ }
174
+
175
+ private static Map<String, Object> toAnyMap(JSONObject obj) {
176
+ Map<String, Object> map = new HashMap<>();
177
+ Iterator<String> it = obj.keys();
178
+ while (it.hasNext()) {
179
+ String k = it.next();
180
+ Object v = obj.opt(k);
181
+ map.put(k, fromJson(v));
182
+ }
183
+ return map;
184
+ }
185
+
186
+ private static Object fromJson(Object v) {
187
+ if (v == null || v == JSONObject.NULL)
188
+ return null;
189
+ if (v instanceof JSONObject)
190
+ return toAnyMap((JSONObject) v);
191
+ if (v instanceof JSONArray)
192
+ return toList((JSONArray) v);
193
+ if (v instanceof String || v instanceof Boolean || v instanceof Integer || v instanceof Long
194
+ || v instanceof Double)
195
+ return v;
196
+ return String.valueOf(v);
197
+ }
198
+
199
+ private static List<Object> toList(JSONArray arr) {
200
+ List<Object> list = new ArrayList<>();
201
+ for (int i = 0; i < arr.length(); i++) {
202
+ Object v = arr.opt(i);
203
+ list.add(fromJson(v));
204
+ }
205
+ return list;
206
+ }
207
+
208
+ // private void evaluateRisk(JSONArray args, final CallbackContext cb) {
209
+ // try {
210
+ // JSONObject obj = args.optJSONObject(0);
211
+ // if (obj == null)
212
+ // obj = new JSONObject();
213
+
214
+ // String apiUrl = obj.optString("apiUrl", "");
215
+ // String orgId = obj.optString("organizationId", "");
216
+ // String apiKey = obj.optString("apiKey", "");
217
+ // JSONObject payload = obj.optJSONObject("payload");
218
+
219
+ // if (apiUrl.isEmpty() || orgId.isEmpty() || payload == null) {
220
+ // cb.error("apiUrl, organizationId and payload are required");
221
+ // return;
222
+ // }
223
+
224
+ // GatekeeperXClient.evaluateRisk(apiUrl, orgId, apiKey, payload, new
225
+ // GatekeeperXClient.Callback() {
226
+ // @Override
227
+ // public void onSuccess(JSONObject response) {
228
+ // cb.success(response);
229
+ // }
230
+
231
+ // @Override
232
+ // public void onError(String error) {
233
+ // cb.error(error);
234
+ // }
235
+ // });
236
+ // } catch (Exception e) {
237
+ // cb.error(e.getMessage());
238
+ // }
239
+ // }
240
+ }
@@ -0,0 +1,110 @@
1
+ package com.gatekeeperx.cordova;
2
+
3
+ import android.os.AsyncTask;
4
+ import android.util.Log;
5
+
6
+ import org.json.JSONObject;
7
+
8
+ import java.io.BufferedReader;
9
+ import java.io.InputStreamReader;
10
+ import java.io.OutputStream;
11
+ import java.net.HttpURLConnection;
12
+ import java.net.URL;
13
+
14
+ /**
15
+ * Cliente HTTP para llamadas al API de GatekeeperX.
16
+ * Ejecuta requests en background para evitar bloquear el UI thread.
17
+ */
18
+ public class GatekeeperXClient {
19
+
20
+ private static final String TAG = "GatekeeperXClient";
21
+ private static final int CONNECT_TIMEOUT = 30000;
22
+ private static final int READ_TIMEOUT = 30000;
23
+
24
+ public interface Callback {
25
+ void onSuccess(JSONObject response);
26
+
27
+ void onError(String error);
28
+ }
29
+
30
+ /**
31
+ * Evalúa el riesgo de un evento enviando datos al API de GatekeeperX.
32
+ *
33
+ * @param apiUrl URL del endpoint (ej:
34
+ * https://api.gatekeeperx.com/gatekeeperx/events/v1)
35
+ * @param organizationId ID de la organización
36
+ * @param apiKey API Key para autenticación (opcional)
37
+ * @param payload JSON con los datos del evento
38
+ * @param callback Callback para manejar respuesta/error
39
+ */
40
+ private static void evaluateRisk(
41
+ final String apiUrl,
42
+ final String organizationId,
43
+ final String apiKey,
44
+ final JSONObject payload,
45
+ final Callback callback) {
46
+
47
+ new AsyncTask<Void, Void, Object>() {
48
+ @Override
49
+ protected Object doInBackground(Void... voids) {
50
+ HttpURLConnection conn = null;
51
+ try {
52
+ URL url = new URL(apiUrl);
53
+ conn = (HttpURLConnection) url.openConnection();
54
+ conn.setRequestMethod("POST");
55
+ conn.setRequestProperty("Content-Type", "application/json");
56
+ conn.setRequestProperty("X-Organization-Id", organizationId);
57
+ if (apiKey != null && !apiKey.isEmpty()) {
58
+ conn.setRequestProperty("X-Api-Key", apiKey);
59
+ }
60
+ conn.setDoOutput(true);
61
+ conn.setConnectTimeout(CONNECT_TIMEOUT);
62
+ conn.setReadTimeout(READ_TIMEOUT);
63
+
64
+ OutputStream os = conn.getOutputStream();
65
+ os.write(payload.toString().getBytes("UTF-8"));
66
+ os.close();
67
+
68
+ int responseCode = conn.getResponseCode();
69
+ BufferedReader br;
70
+ if (responseCode >= 200 && responseCode < 300) {
71
+ br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
72
+ } else {
73
+ br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "UTF-8"));
74
+ }
75
+
76
+ StringBuilder response = new StringBuilder();
77
+ String line;
78
+ while ((line = br.readLine()) != null) {
79
+ response.append(line);
80
+ }
81
+ br.close();
82
+
83
+ Log.d(TAG, "Response code: " + responseCode);
84
+ Log.d(TAG, "Response body: " + response.toString());
85
+
86
+ if (responseCode >= 200 && responseCode < 300) {
87
+ return new JSONObject(response.toString());
88
+ } else {
89
+ return "HTTP " + responseCode + ": " + response.toString();
90
+ }
91
+ } catch (Exception e) {
92
+ Log.e(TAG, "evaluateRisk error: " + e.getMessage(), e);
93
+ return e.getMessage();
94
+ } finally {
95
+ if (conn != null)
96
+ conn.disconnect();
97
+ }
98
+ }
99
+
100
+ @Override
101
+ protected void onPostExecute(Object result) {
102
+ if (result instanceof JSONObject) {
103
+ callback.onSuccess((JSONObject) result);
104
+ } else {
105
+ callback.onError((String) result);
106
+ }
107
+ }
108
+ }.execute();
109
+ }
110
+ }
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ module.exports = function (context) {
7
+ const platforms = context.opts.cordova.platforms;
8
+ if (platforms.indexOf('android') === -1) return;
9
+
10
+ const buildGradlePath = path.join(
11
+ context.opts.projectRoot,
12
+ 'platforms',
13
+ 'android',
14
+ 'app',
15
+ 'build.gradle'
16
+ );
17
+
18
+ if (!fs.existsSync(buildGradlePath)) {
19
+ console.log('[devicex] build.gradle no encontrado');
20
+ return;
21
+ }
22
+
23
+ let buildGradle = fs.readFileSync(buildGradlePath, 'utf8');
24
+
25
+ // Agregar repositorio flatDir si no existe en el bloque principal
26
+ if (buildGradle.indexOf('flatDir') === -1) {
27
+ const repoBlock = `
28
+ repositories {
29
+ flatDir {
30
+ dirs 'libs'
31
+ }
32
+ }
33
+ `;
34
+ // Insertar antes del último bloque dependencies (el principal)
35
+ const lastDepsIndex = buildGradle.lastIndexOf('dependencies {');
36
+ if (lastDepsIndex !== -1) {
37
+ buildGradle = buildGradle.slice(0, lastDepsIndex) +
38
+ repoBlock + '\n' +
39
+ buildGradle.slice(lastDepsIndex);
40
+ }
41
+ }
42
+
43
+ // Agregar dependencias de AARs después de fileTree si no existen
44
+ if (buildGradle.indexOf('devicex-bridge') === -1) {
45
+ const depsBlock = ` implementation files('libs/devicex-bridge.aar')
46
+ implementation files('libs/devicex-1.3.1.aar')
47
+
48
+ // Dependencias del SDK Devicex
49
+ implementation 'io.ktor:ktor-client-core:2.3.7'
50
+ implementation 'io.ktor:ktor-client-okhttp:2.3.7'
51
+ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2'
52
+ `;
53
+ // Buscar después de fileTree en el bloque principal
54
+ buildGradle = buildGradle.replace(
55
+ /(implementation fileTree\(dir: 'libs', include: '\*\.jar'\))/,
56
+ '$1\n' + depsBlock
57
+ );
58
+ }
59
+
60
+ fs.writeFileSync(buildGradlePath, buildGradle, 'utf8');
61
+ console.log('[devicex] build.gradle configurado con AARs locales');
62
+ };
@@ -0,0 +1,57 @@
1
+ // Type definitions for @gatekeeperx/cordova-plugin-devicex
2
+ // Project: https://npmjs.com/package/@gatekeeperx/cordova-plugin-devicex
3
+ // Definitions by: GatekeeperX
4
+
5
+ export type Environment = 'DEV' | 'DEVELOPMENT' | 'SANDBOX' | 'STAGE' | 'STAGING' | 'PROD' | 'PRODUCTION';
6
+
7
+ export interface ConfigureOptions {
8
+ tenant: string;
9
+ apiKey: string;
10
+ organizationId?: string;
11
+ environment?: Environment;
12
+ headers?: Record<string, string>;
13
+ }
14
+
15
+ export interface EventSuccess {
16
+ ok: true;
17
+ code: number;
18
+ deviceXId: string;
19
+ message?: string;
20
+ }
21
+
22
+ export interface EventFailure {
23
+ ok: false;
24
+ errorCode: string;
25
+ errorMessage: string;
26
+ httpCode?: number | null;
27
+ }
28
+
29
+ export type EventResult = EventSuccess | EventFailure;
30
+
31
+ export interface RiskAssessment {
32
+ isRooted: boolean;
33
+ isFridaDetected: boolean;
34
+ isXposedDetected: boolean;
35
+ isMagiskDetected: boolean;
36
+ isEmulator: boolean;
37
+ isDebuggerAttached: boolean;
38
+ isHookingDetected: boolean;
39
+ riskScore: number;
40
+ riskLevel: string;
41
+ }
42
+
43
+ declare const Devicex: {
44
+ configure(options: ConfigureOptions): Promise<void>;
45
+ sendEvent(name: string, properties?: Record<string, any>, headers?: Record<string, string>): Promise<EventResult>;
46
+ getRiskAssessment(): Promise<RiskAssessment>;
47
+ isInitialized(): Promise<boolean>;
48
+
49
+ /** Retorna la versión del SDK nativo (AAR). Ej: "1.3.1" */
50
+ getVersion(): Promise<string>;
51
+
52
+ /** Retorna la versión del plugin Cordova (wrapper). Ej: "0.1.0" */
53
+ getPluginVersion(): string;
54
+ };
55
+
56
+ export default Devicex;
57
+ export as namespace Devicex;
package/www/devicex.js ADDED
@@ -0,0 +1,51 @@
1
+ var exec = require('cordova/exec');
2
+ var PLUGIN = 'Devicex';
3
+
4
+ /**
5
+ * Versión del plugin Cordova (wrapper).
6
+ * Independiente de la versión del SDK nativo (AAR).
7
+ *
8
+ * - Plugin Cordova: 0.1.0 (este valor)
9
+ * - SDK nativo: se obtiene con getVersion() (desde el AAR)
10
+ */
11
+ var PLUGIN_VERSION = '0.1.0';
12
+
13
+ function call(action, args) {
14
+ return new Promise(function (resolve, reject) {
15
+ exec(function (res) { resolve(res); }, function (err) { reject(err); }, PLUGIN, action, args || []);
16
+ });
17
+ }
18
+
19
+ exports.configure = function (options) {
20
+ options = options || {};
21
+ return call('configure', [options]);
22
+ };
23
+
24
+ exports.sendEvent = function (name, properties, headers) {
25
+ if (!name || typeof name !== 'string') {
26
+ return Promise.reject('name is required');
27
+ }
28
+ return call('sendEvent', [{ name: name, properties: properties || {}, headers: headers || {} }]);
29
+ };
30
+
31
+ exports.isInitialized = function () {
32
+ return call('isInitialized', []).then(function (res) { return res && res.value === true; });
33
+ };
34
+
35
+ /**
36
+ * Retorna la versión del SDK nativo (AAR de Device Intelligence).
37
+ * Ejemplo: "1.3.1"
38
+ */
39
+ exports.getVersion = function () {
40
+ return call('getVersion', []).then(function (res) { return (res && res.value) || ''; });
41
+ };
42
+
43
+ /**
44
+ * Retorna la versión del plugin Cordova (wrapper JS + bridge).
45
+ * Ejemplo: "0.1.0"
46
+ *
47
+ * Esta versión es independiente del SDK nativo y sigue su propio ciclo de releases.
48
+ */
49
+ exports.getPluginVersion = function () {
50
+ return PLUGIN_VERSION;
51
+ };