From 0e35bdd5d001e7b7455ab48c77d6d90762b98d05 Mon Sep 17 00:00:00 2001 From: andre Date: Fri, 13 Feb 2026 13:21:07 +0100 Subject: [PATCH] improve plugin creation: add input sanitization, logging, and error handling --- android/build.gradle | 2 + .../webidmetaplugin/WebIdMetaPluginModule.kt | 109 ++++++++++++------ example/package.json | 4 +- example/src/App.tsx | 47 ++++---- yarn.lock | 16 ++- 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 3e463b6..2848888 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -68,6 +68,8 @@ dependencies { // For < 0.71, this will be from the local maven repo // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin //noinspection GradleDynamicVersion + implementation "com.google.code.gson:gson:2.10.1" + implementation("com.facebook.react:react-android") def product_catalog_version = "15.1.1" diff --git a/android/src/main/java/com/webidmetaplugin/WebIdMetaPluginModule.kt b/android/src/main/java/com/webidmetaplugin/WebIdMetaPluginModule.kt index f710a0f..134185e 100644 --- a/android/src/main/java/com/webidmetaplugin/WebIdMetaPluginModule.kt +++ b/android/src/main/java/com/webidmetaplugin/WebIdMetaPluginModule.kt @@ -21,6 +21,11 @@ import de.webidsolutions.plugin_core.IProductPluginWebId import de.webidsolutions.video_ident.plugin.videocall.VideoOptionsConfig import de.webidsolutions.video_ident_product_plugin.VideoIdentProductPlugin import java.net.URI +import com.facebook.react.bridge.UiThreadUtil +import android.util.Log +import com.google.gson.Gson + +private const val TAG = "WebIdMetaPlugin" class WebIdMetaPluginModule( private val reactContext: ReactApplicationContext @@ -63,48 +68,78 @@ class WebIdMetaPluginModule( plugins: ReadableArray, promise: Promise ) { + val cleanUri = uri.trim() + val cleanUsername = username.trim() + val cleanApiKey = apiKey.trim() + + val shaPinsList = shaPins.toArrayList().map { it.toString() } + val pluginsList = plugins.toArrayList().map { it.toString() } + + Log.i(TAG, "createMetaPlugin uri=$cleanUri user=$cleanUsername apiKeyLen=${cleanApiKey.length} apiKeyTail=${cleanApiKey.takeLast(4)}") + Log.i(TAG, "createMetaPlugin shaPins len=${shaPinsList.size} first=${shaPinsList.firstOrNull()?.take(20)}...") + Log.i(TAG, "createMetaPlugin plugins(raw) len=${pluginsList.size} values=$pluginsList") + try { val environment = WebIdSdkEnvironment( - URI.create(uri), - *shaPins.toArrayList().map { it.toString() }.toTypedArray() + URI.create(cleanUri), + *shaPinsList.toTypedArray() ) - val selectedPlugins = ArrayList() + val selectedPlugins = ArrayList() - plugins.toArrayList().forEach { - when (it.toString()) { + pluginsList.forEach { p -> + when (p) { "AutoIdOnServer" -> selectedPlugins.add(AutoIdentOnServerProductPlugin()) "PayOnServer" -> selectedPlugins.add(PayOnServerProductPlugin()) "VideoId" -> selectedPlugins.add(VideoIdentProductPlugin(config = VideoOptionsConfig())) "EIdOnServer" -> selectedPlugins.add(EIdOnServerProductPlugin()) + else -> Log.w(TAG, "createMetaPlugin unknown plugin string='$p'") } } + Log.i(TAG, "createMetaPlugin plugins(mapped) len=${selectedPlugins.size} classes=${selectedPlugins.map { it.javaClass.simpleName }}") + metaPlugin = WebIdMetaPlugin( environment, - username, - apiKey, + cleanUsername, + cleanApiKey, reactApplicationContext, selectedPlugins ) promise.resolve("MetaPluginCreated") } catch (e: Exception) { - promise.reject("CREATE_ERROR", e.message) + Log.e(TAG, "createMetaPlugin failed: ${e.message}", e) + promise.reject("CREATE_ERROR", e.message, e) } } + // ------------------------------------------------ // VERIFY // ------------------------------------------------ @ReactMethod fun verifyActionId(actionId: String, promise: Promise) { + Log.i(TAG, "verifyActionId() called actionId=$actionId") + try { - val result = metaPlugin?.verify(actionId) - promise.resolve(result.toString()) + val plugin = metaPlugin + if (plugin == null) { + Log.w(TAG, "verifyActionId() blocked: SDK_NOT_INITIALIZED") + promise.reject("SDK_NOT_INITIALIZED", "MetaPlugin not created. Call createMetaPlugin() first.") + return + } + + val result = plugin.verify(actionId) + + val json = Gson().toJson(result) + Log.i(TAG, "verifyActionId() success, jsonLen=${json.length}") + + promise.resolve(json) } catch (e: Exception) { - promise.reject("VERIFY_ERROR", e.message) + Log.e(TAG, "verifyActionId() failed: ${e.message}", e) + promise.reject("VERIFY_ERROR", e.message, e) } } @@ -127,37 +162,39 @@ class WebIdMetaPluginModule( return } - try { - val fragment = WebIdResultHostFragment.getOrCreate( - activity = activity, - onResult = { resultCode, data -> - try { - val result = data?.let { - IProductPluginWebId.getProductPluginResult(it, resultCode) - } + UiThreadUtil.runOnUiThread { + try { + val fragment = WebIdResultHostFragment.getOrCreate( + activity = activity, + onResult = { resultCode, data -> + try { + val result = data?.let { + IProductPluginWebId.getProductPluginResult(it, resultCode) + } - if (result?.error == null) { - sendEvent("WebIdSdkEvent.finishedSuccess", result?.info ?: "") - } else { - sendEvent("WebIdSdkEvent.finishedFailed", result.error.toString()) + if (result?.error == null) { + sendEvent("WebIdSdkEvent.finishedSuccess", result?.info ?: "") + } else { + sendEvent("WebIdSdkEvent.finishedFailed", result.error.toString()) + } + } catch (e: Exception) { + sendEvent("WebIdSdkEvent.finishedFailed", e.message ?: "Unknown") } - } catch (e: Exception) { - sendEvent("WebIdSdkEvent.finishedFailed", e.message ?: "Unknown") } - } - ) + ) - val customTheme: Int? = null + val customTheme: Int? = null - plugin.startPlugin( - activity, - fragment.launcher, - customTheme - ) + plugin.startPlugin( + activity, + fragment.launcher, + customTheme + ) - promise.resolve("PluginStarted") - } catch (e: Exception) { - promise.reject("START_ERROR", e.message) + promise.resolve("PluginStarted") + } catch (e: Exception) { + promise.reject("START_ERROR", e.message, e) + } } } diff --git a/example/package.json b/example/package.json index 99547cc..54f4bac 100644 --- a/example/package.json +++ b/example/package.json @@ -10,8 +10,8 @@ "build:ios": "react-native build-ios --scheme WebIdMetaPluginExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" }, "dependencies": { - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "19.2.3", + "react-dom": "19.2.3", "react-native": "0.84.0", "react-native-web-id-meta-plugin": "file:.." }, diff --git a/example/src/App.tsx b/example/src/App.tsx index a3fda1c..d296e2e 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -25,11 +25,20 @@ import BouncyCheckbox from 'react-native-bouncy-checkbox'; export default function App() { const [actionId, onChangeActionId] = React.useState(''); - var selectedPlugins: Set = new Set(); + const [selectedPlugins, setSelectedPlugins] = React.useState([]); + + const togglePlugin = (name: string, isChecked: boolean) => { + setSelectedPlugins((prev) => { + const next = new Set(prev); + if (isChecked) next.add(name); + else next.delete(name); + return Array.from(next); + }); + }; webIdEventEmitter.addListener(WebIdSdkEvent.finishedFailed, (eventData) => { let result = parseJSONToType(eventData); - if (result != undefined) { + if (result !== undefined) { console.log( 'received failed event with result code: ', result.genericResultCode @@ -39,7 +48,7 @@ export default function App() { webIdEventEmitter.addListener(WebIdSdkEvent.finishedSuccess, (eventData) => { let result = parseJSONToType(eventData); - if (result != undefined) { + if (result !== undefined) { console.log( 'received failed event with process finished: ', result.processFinished @@ -63,13 +72,7 @@ export default function App() { }; const onCreateMetaPlugin = () => { - createMetaPlugin( - URL, - USERNAME, - API_KEY, - [CERT_BASE64], - Array.from(selectedPlugins) - ) + createMetaPlugin(URL, USERNAME, API_KEY, [CERT_BASE64], selectedPlugins) .then((data) => { showAlert('Success', data); }) @@ -125,44 +128,36 @@ export default function App() { - isChecked === true - ? selectedPlugins.add('AutoIdOnServer') - : selectedPlugins.delete('AutoIdOnServer') + togglePlugin('AutoIdOnServer', isChecked) } /> AutoIdOnServer - isChecked === true - ? selectedPlugins.add('PayOnServer') - : selectedPlugins.delete('PayOnServer') + togglePlugin('PayOnServer', isChecked) } /> PayOnServer - isChecked === true - ? selectedPlugins.add('VideoId') - : selectedPlugins.delete('VideoId') + togglePlugin('VideoId', isChecked) } /> VideoId - isChecked === true - ? selectedPlugins.add('EIdOnServer') - : selectedPlugins.delete('EIdOnServer') + togglePlugin('EIdOnServer', isChecked) } /> EIdOnServer diff --git a/yarn.lock b/yarn.lock index 6b3bbbb..2a0f0eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6532,6 +6532,13 @@ react-dom@^19.2.4: dependencies: scheduler "^0.27.0" +react-dom@19.2.3: + version "19.2.3" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz" + integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg== + dependencies: + scheduler "^0.27.0" + react-is@^16.13.1: version "16.13.1" @@ -6579,8 +6586,8 @@ react-native-dotenv@^3.4.9: version "0.0.1" resolved "file:example" dependencies: - react "^19.2.4" - react-dom "^19.2.4" + react "19.2.3" + react-dom "19.2.3" react-native "0.84.0" react-native-web-id-meta-plugin "file:.." @@ -6638,6 +6645,11 @@ react@*, react@^19.2.3, react@^19.2.4: resolved "https://registry.npmjs.org/react/-/react-19.2.4.tgz" integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ== +react@19.2.3: + version "19.2.3" + resolved "https://registry.npmjs.org/react/-/react-19.2.3.tgz" + integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== + read-pkg-up@^3.0.0: version "3.0.0" dependencies: