From 315e7c9c7f53d469721e8cbf5248b2677fa5759b Mon Sep 17 00:00:00 2001 From: Alexande B Date: Thu, 7 Dec 2023 10:14:34 +0100 Subject: [PATCH 1/6] feat: migrate to hcaptcha-loader --- sdk/build.gradle | 22 ++- sdk/src/main/html/hcaptcha.html | 172 +++++++++--------- .../hcaptcha/sdk/HCaptchaDialogFragment.java | 1 + .../hcaptcha/sdk/HCaptchaHeadlessWebView.java | 1 + .../hcaptcha/sdk/HCaptchaWebViewHelper.java | 23 ++- .../sdk/HCaptchaWebViewHelperTest.java | 10 + 6 files changed, 141 insertions(+), 88 deletions(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index f78c5ddc..3808fc66 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -7,6 +7,11 @@ plugins { id "com.github.spotbugs" version "5.2.3" id "org.owasp.dependencycheck" version "7.1.1" id "org.sonarqube" version "3.4.0.2513" + id "de.undercouch.download" version "5.5.0" +} + +ext { + hcaptchaLoaderVersion = "1.1.3" } android { @@ -31,6 +36,7 @@ android { versionName "4.0.0" buildConfigField 'String', 'VERSION_NAME', "\"${defaultConfig.versionName}_${defaultConfig.versionCode}\"" + buildConfigField 'String', 'LOADER_VERSION', "\"${hcaptchaLoaderVersion}\"" consumerProguardFiles "consumer-rules.pro" proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' @@ -113,6 +119,14 @@ project.afterEvaluate { long MAX_AAR_SIZE_KB = 200 +tasks.register('downloadHCaptchaLoaderJs', Download) { + src "https://www.unpkg.com/@hcaptcha/loader@${hcaptchaLoaderVersion}/dist/index.mjs" + dest layout.buildDirectory.file("generated/assets/hcaptcha/loader.mjs") + onlyIfModified true +} + +android.sourceSets.main.assets.srcDirs += [layout.buildDirectory.file("generated/assets")] + android.libraryVariants.all { variant -> def variantName = variant.name.capitalize() project.task("report${variantName}AarSize") { @@ -153,6 +167,7 @@ android.libraryVariants.all { variant -> def html = file("$projectDir/src/main/html/hcaptcha.html") .readLines() .stream() + .map({l -> "${l.replaceAll('@LOADER_VERSION@', hcaptchaLoaderVersion)}"}) .map({l -> "\"${l.replaceAll('"', '\\\\"')}\\n\""}) .collect(java.util.stream.Collectors.joining("\n${' ' * 16}+ ")) @@ -167,8 +182,13 @@ android.libraryVariants.all { variant -> } } - // preBuild.dependsOn generateTask variant.registerJavaGeneratingTask(generateTask, outputDir) + tasks.named("package${variant.name.capitalize()}Assets") + .get() + .dependsOn(downloadHCaptchaLoaderJs) + tasks.named("merge${variant.name.capitalize()}Assets") + .get() + .dependsOn(downloadHCaptchaLoaderJs) } apply from: "$rootProject.projectDir/gradle/shared/code-quality.gradle" \ No newline at end of file diff --git a/sdk/src/main/html/hcaptcha.html b/sdk/src/main/html/hcaptcha.html index 770989c4..17301952 100644 --- a/sdk/src/main/html/hcaptcha.html +++ b/sdk/src/main/html/hcaptcha.html @@ -36,7 +36,9 @@ window.sysDebug = JSON.parse(window.JSDI.getSysDebug()); } - diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java index c964ccf7..a59e0331 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java @@ -194,6 +194,7 @@ public void onAnimationEnd(Animator animation) { @Override public void onLoaded() { + HCaptchaLog.d("DialogFragment.onLoaded"); assert webViewHelper != null; if (webViewHelper.getConfig().getSize() != HCaptchaSize.INVISIBLE) { diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java index 60cd0a60..91f445f3 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java @@ -70,6 +70,7 @@ public void onSuccess(final String token) { @Override public void onLoaded() { + HCaptchaLog.d("HeadlessWebView.onLoaded"); webViewLoaded = true; if (shouldResetOnLoad) { shouldResetOnLoad = false; diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java index e86b58c3..3dcfbd7f 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java @@ -22,6 +22,10 @@ import lombok.Getter; import lombok.NonNull; +import java.io.IOException; +import java.util.Collections; +import java.util.Objects; + final class HCaptchaWebViewHelper { @NonNull private final Context context; @@ -120,6 +124,9 @@ public boolean shouldRetry(HCaptchaException exception) { @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private class HCaptchaWebClient extends WebViewClient { + private final Uri loaderUri = Uri.parse( + "https://www.unpkg.com/@hcaptcha/loader@" + BuildConfig.LOADER_VERSION + "/dist/index.mjs" + ); @NonNull private final Handler handler; @@ -135,7 +142,21 @@ private String stripUrl(String url) { @Override public WebResourceResponse shouldInterceptRequest (final WebView view, final WebResourceRequest request) { final Uri requestUri = request.getUrl(); - if (requestUri != null && requestUri.getScheme() != null && requestUri.getScheme().equals("http")) { + if (loaderUri.equals(requestUri)) { + try { + return new WebResourceResponse( + "application/javascript", + "UTF-8", + 200, + "OK", + Collections.singletonMap("Access-Control-Allow-Origin", + Objects.toString(config.getHost(), "null")), + view.getContext().getAssets().open("hcaptcha/loader.mjs") + ); + } catch (IOException e) { + HCaptchaLog.w("WebViewHelper wasn't able to load loader.mjs from assets"); + } + } else if (requestUri != null && requestUri.getScheme() != null && requestUri.getScheme().equals("http")) { handler.post(() -> { webView.removeJavascriptInterface(HCaptchaJSInterface.JS_INTERFACE_TAG); webView.removeJavascriptInterface(HCaptchaDebugInfo.JS_INTERFACE_TAG); diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java index 4501c9e3..db214820 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java @@ -2,15 +2,18 @@ import static com.hcaptcha.sdk.AssertUtil.failAsNonReachable; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import android.content.Context; import android.app.Activity; import android.os.Handler; import android.os.Looper; import androidx.test.core.app.ActivityScenario; import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.platform.app.InstrumentationRegistry; import com.hcaptcha.sdk.test.TestActivity; @@ -18,6 +21,7 @@ import org.junit.Rule; import org.junit.Test; +import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -88,4 +92,10 @@ public void onFailure(HCaptchaException e) { assertTrue(failureLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS)); } + + @Test + public void testLoaderJsAssetPresence() throws IOException { + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertNotNull(appContext.getAssets().open("hcaptcha/loader.mjs")); + } } From 857257aafc8e597eeada2df9de5621f9c2e48995 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 6 Feb 2024 19:58:44 +0100 Subject: [PATCH 2/6] fix: put back removed error-callback --- sdk/build.gradle | 2 +- sdk/src/main/html/hcaptcha.html | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 3808fc66..9dc17505 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -23,7 +23,7 @@ android { } defaultConfig { - minSdkVersion 16 + minSdkVersion 16 // Android 4.1 targetSdkVersion 34 // See https://developer.android.com/studio/publish/versioning diff --git a/sdk/src/main/html/hcaptcha.html b/sdk/src/main/html/hcaptcha.html index 17301952..e76c9cde 100644 --- a/sdk/src/main/html/hcaptcha.html +++ b/sdk/src/main/html/hcaptcha.html @@ -118,6 +118,24 @@ }, 'open-callback': function openCallback() { return BridgeObject.onOpen(); + }, + 'error-callback': function errorCallback(error) { + switch(error) { + case "rate-limited": + return BridgeObject.onError(31); + case "network-error": + return BridgeObject.onError(7); + case "invalid-data": + return BridgeObject.onError(8); + case "challenge-error": + return BridgeObject.onError(9); + case "internal-error": + return BridgeObject.onError(10); + default: + // Error not handled? Log it for debugging purposes + console.error(error); + return BridgeObject.onError(29); + } } }; } @@ -128,13 +146,13 @@ loadAsync: true, async: true, }; - }; + } function getLoaderParams(config) { var result = getScriptParams(config); result.render = 'explicit'; result.sentry = config.sentry; - result.custom = !!config.customTheme + result.custom = !!config.customTheme; result.assethost = config.assethost; result.imghost = config.imghost; result.reportapi = config.reportapi; @@ -145,7 +163,7 @@ result.cleanup = true; return result; - }; + } var container = document.getElementById("hcaptcha-container"); container.addEventListener("click", function () { if (hcaptcha) { @@ -157,7 +175,7 @@ }); async function execute() { try { - const { response } = await hcaptcha.execute(getScriptParams(bridgeConfig)); // { async: true } + var { response } = await hcaptcha.execute(getScriptParams(bridgeConfig)); BridgeObject.onPass(response); } catch (error) { switch(error) { From 6c8d7e34c9573a7354576cef7f1ec66c65a1877d Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 6 Feb 2024 20:21:14 +0100 Subject: [PATCH 3/6] fix: move errorCallback to upper level --- sdk/src/main/html/hcaptcha.html | 55 +++++++++++++-------------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/sdk/src/main/html/hcaptcha.html b/sdk/src/main/html/hcaptcha.html index e76c9cde..99d15c81 100644 --- a/sdk/src/main/html/hcaptcha.html +++ b/sdk/src/main/html/hcaptcha.html @@ -84,6 +84,25 @@ hcaptcha.reset(); } + function errorCallback(error) { + switch(error) { + case "rate-limited": + return BridgeObject.onError(31); + case "network-error": + return BridgeObject.onError(7); + case "invalid-data": + return BridgeObject.onError(8); + case "challenge-error": + return BridgeObject.onError(9); + case "internal-error": + return BridgeObject.onError(10); + default: + // Error not handled? Log it for debugging purposes + console.error(error); + return BridgeObject.onError(29); + } + } + function getTheme(config) { var theme = config.theme; var customTheme = config.customTheme; @@ -119,24 +138,7 @@ 'open-callback': function openCallback() { return BridgeObject.onOpen(); }, - 'error-callback': function errorCallback(error) { - switch(error) { - case "rate-limited": - return BridgeObject.onError(31); - case "network-error": - return BridgeObject.onError(7); - case "invalid-data": - return BridgeObject.onError(8); - case "challenge-error": - return BridgeObject.onError(9); - case "internal-error": - return BridgeObject.onError(10); - default: - // Error not handled? Log it for debugging purposes - console.error(error); - return BridgeObject.onError(29); - } - } + 'error-callback': errorCallback }; } function getScriptParams(config) { @@ -178,22 +180,7 @@ var { response } = await hcaptcha.execute(getScriptParams(bridgeConfig)); BridgeObject.onPass(response); } catch (error) { - switch(error) { - case "rate-limited": - return BridgeObject.onError(31); - case "network-error": - return BridgeObject.onError(7); - case "invalid-data": - return BridgeObject.onError(8); - case "challenge-error": - return BridgeObject.onError(9); - case "internal-error": - return BridgeObject.onError(10); - default: - // Error not handled? Log it for debugging purposes - console.error(error); - return BridgeObject.onError(29); - } + errorCallback(error); } } async function loadApi(config) { From 7f421f01c58570b20860caea7ae502dbdef4373a Mon Sep 17 00:00:00 2001 From: Alexande B Date: Thu, 29 Feb 2024 22:29:50 +0100 Subject: [PATCH 4/6] feat: migrate to ES5 compatible loader --- sdk/build.gradle | 18 ++++++---- sdk/src/main/html/hcaptcha.html | 33 +++++++++---------- .../hcaptcha/sdk/HCaptchaWebViewHelper.java | 17 ++++++---- .../sdk/HCaptchaWebViewHelperTest.java | 19 +++++++++-- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 9dc17505..2fab4a89 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -11,7 +11,7 @@ plugins { } ext { - hcaptchaLoaderVersion = "1.1.3" + hcaptchaLoaderVersion = "1.2.3" } android { @@ -117,14 +117,20 @@ project.afterEvaluate { } } -long MAX_AAR_SIZE_KB = 200 +long MAX_AAR_SIZE_KB = 250 -tasks.register('downloadHCaptchaLoaderJs', Download) { - src "https://www.unpkg.com/@hcaptcha/loader@${hcaptchaLoaderVersion}/dist/index.mjs" - dest layout.buildDirectory.file("generated/assets/hcaptcha/loader.mjs") +tasks.register('downloadPolyfillsJs', Download) { + src "https://www.unpkg.com/@hcaptcha/loader@${hcaptchaLoaderVersion}/dist/polyfills.js" + dest layout.buildDirectory.file("generated/assets/hcaptcha/polyfills.js") onlyIfModified true } +tasks.register('downloadHCaptchaLoaderJs', Download) { + src "https://www.unpkg.com/@hcaptcha/loader@${hcaptchaLoaderVersion}/dist/index.es5.js" + dest layout.buildDirectory.file("generated/assets/hcaptcha/loader.js") + onlyIfModified true +}.get().dependsOn('downloadPolyfillsJs') + android.sourceSets.main.assets.srcDirs += [layout.buildDirectory.file("generated/assets")] android.libraryVariants.all { variant -> @@ -150,7 +156,7 @@ android.libraryVariants.all { variant -> var aarFile = variant.packageLibraryProvider.get().archiveFile.get().getAsFile() long aarSizeKb = aarFile.length() / 1024 if (aarSizeKb > MAX_AAR_SIZE_KB) { - throw new GradleException("${aarPath} size exceeded! ${aarSizeKb}Kbyte > ${MAX_AAR_SIZE_KB}Kbyte") + throw new GradleException("${aarFile} size exceeded! ${aarSizeKb}Kbyte > ${MAX_AAR_SIZE_KB}Kbyte") } } }) diff --git a/sdk/src/main/html/hcaptcha.html b/sdk/src/main/html/hcaptcha.html index 99d15c81..a200fc20 100644 --- a/sdk/src/main/html/hcaptcha.html +++ b/sdk/src/main/html/hcaptcha.html @@ -36,9 +36,9 @@ window.sysDebug = JSON.parse(window.JSDI.getSysDebug()); } - + + diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java index 3dcfbd7f..c2819aa2 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaWebViewHelper.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; final class HCaptchaWebViewHelper { @@ -124,15 +126,17 @@ public boolean shouldRetry(HCaptchaException exception) { @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private class HCaptchaWebClient extends WebViewClient { - private final Uri loaderUri = Uri.parse( - "https://www.unpkg.com/@hcaptcha/loader@" + BuildConfig.LOADER_VERSION + "/dist/index.mjs" - ); + private final Map assetsCache = new HashMap<>(); @NonNull private final Handler handler; HCaptchaWebClient(@NonNull Handler handler) { this.handler = handler; + + final String baseUrl = "https://unpkg.com/@hcaptcha/loader@" + BuildConfig.LOADER_VERSION + "/dist"; + assetsCache.put(Uri.parse(baseUrl + "/index.es5.js"), "hcaptcha/loader.js"); + assetsCache.put(Uri.parse(baseUrl + "/polyfills.js"), "hcaptcha/polyfills.js"); } private String stripUrl(String url) { @@ -142,7 +146,8 @@ private String stripUrl(String url) { @Override public WebResourceResponse shouldInterceptRequest (final WebView view, final WebResourceRequest request) { final Uri requestUri = request.getUrl(); - if (loaderUri.equals(requestUri)) { + final String assetPath = assetsCache.get(requestUri); + if (assetPath != null) { try { return new WebResourceResponse( "application/javascript", @@ -151,10 +156,10 @@ public WebResourceResponse shouldInterceptRequest (final WebView view, final Web "OK", Collections.singletonMap("Access-Control-Allow-Origin", Objects.toString(config.getHost(), "null")), - view.getContext().getAssets().open("hcaptcha/loader.mjs") + view.getContext().getAssets().open(assetPath) ); } catch (IOException e) { - HCaptchaLog.w("WebViewHelper wasn't able to load loader.mjs from assets"); + HCaptchaLog.w("WebViewHelper wasn't able to load " + assetPath + " from assets"); } } else if (requestUri != null && requestUri.getScheme() != null && requestUri.getScheme().equals("http")) { handler.post(() -> { diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java index db214820..259ff4bc 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java @@ -7,11 +7,13 @@ import static org.mockito.Mockito.mock; import android.content.Context; +import android.content.res.AssetManager; import android.app.Activity; import android.os.Handler; import android.os.Looper; import androidx.test.core.app.ActivityScenario; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.platform.app.InstrumentationRegistry; @@ -22,6 +24,7 @@ import org.junit.Test; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -95,7 +98,19 @@ public void onFailure(HCaptchaException e) { @Test public void testLoaderJsAssetPresence() throws IOException { - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertNotNull(appContext.getAssets().open("hcaptcha/loader.mjs")); + AssetManager assets = InstrumentationRegistry + .getInstrumentation() + .getTargetContext() + .getAssets(); + assertNotNull(assets.open("hcaptcha/loader.js")); + } + + @Test + public void testPolyfillsJsAssetPresence() throws IOException { + AssetManager assets = InstrumentationRegistry + .getInstrumentation() + .getTargetContext() + .getAssets(); + assertNotNull(assets.open("hcaptcha/polyfills.js")); } } From ff29a22269048755180edc0c446dc4d146e6cca4 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 2 Jul 2024 10:25:02 +0200 Subject: [PATCH 5/6] feat: update hcaptcha-loader to 1.2.4 --- sdk/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 2fab4a89..ce388a61 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -11,7 +11,7 @@ plugins { } ext { - hcaptchaLoaderVersion = "1.2.3" + hcaptchaLoaderVersion = "1.2.4" } android { From 85388507af886493dd860a47daf2932156e4cc65 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Mon, 8 Jul 2024 00:03:47 +0200 Subject: [PATCH 6/6] feat: add integrity check for hcaptcha-loader and polyfill --- sdk/build.gradle | 16 ++++++++++++++++ sdk/src/main/html/hcaptcha.html | 4 ++-- .../com/hcaptcha/sdk/HCaptchaWebViewHelper.java | 3 ++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index ce388a61..92e7a319 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -119,6 +119,16 @@ project.afterEvaluate { long MAX_AAR_SIZE_KB = 250 +String sha256Integrity(String filename) { + def file = new File("${buildDir}/generated/assets/hcaptcha/${filename}") + def sha256Hash = file.withInputStream { stream -> + org.apache.commons.codec.digest.DigestUtils.sha256(stream) + } + def base64Hash = org.apache.commons.codec.binary.Base64.encodeBase64String(sha256Hash) + + return "sha256-${base64Hash}" +} + tasks.register('downloadPolyfillsJs', Download) { src "https://www.unpkg.com/@hcaptcha/loader@${hcaptchaLoaderVersion}/dist/polyfills.js" dest layout.buildDirectory.file("generated/assets/hcaptcha/polyfills.js") @@ -170,10 +180,14 @@ android.libraryVariants.all { variant -> doFirst { def outputJavaClass = file("$outputDir/HCaptchaHtml.java") def template = file("$projectDir/src/main/html/HCaptchaHtml.java.tml").text + def polyfillsIntegrity = sha256Integrity('polyfills.js') + def loaderIntegrity = sha256Integrity('loader.js') def html = file("$projectDir/src/main/html/hcaptcha.html") .readLines() .stream() .map({l -> "${l.replaceAll('@LOADER_VERSION@', hcaptchaLoaderVersion)}"}) + .map({l -> "${l.replaceAll('@POLYFILLS_INTEGRITY@', polyfillsIntegrity)}"}) + .map({l -> "${l.replaceAll('@LOADER_INTEGRITY@', loaderIntegrity)}"}) .map({l -> "\"${l.replaceAll('"', '\\\\"')}\\n\""}) .collect(java.util.stream.Collectors.joining("\n${' ' * 16}+ ")) @@ -189,6 +203,8 @@ android.libraryVariants.all { variant -> } variant.registerJavaGeneratingTask(generateTask, outputDir) + generateTask.dependsOn(downloadHCaptchaLoaderJs) + tasks.named("package${variant.name.capitalize()}Assets") .get() .dependsOn(downloadHCaptchaLoaderJs) diff --git a/sdk/src/main/html/hcaptcha.html b/sdk/src/main/html/hcaptcha.html index a200fc20..051908e4 100644 --- a/sdk/src/main/html/hcaptcha.html +++ b/sdk/src/main/html/hcaptcha.html @@ -36,8 +36,8 @@ window.sysDebug = JSON.parse(window.JSDI.getSysDebug()); } - - + +