Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions 3DSView/src/main/java/eu/livotov/labs/android/d3s/D3SView.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package eu.livotov.labs.android.d3s;

import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

Expand Down Expand Up @@ -75,6 +79,9 @@ public D3SView(final Context context) {
private void initUI() {
getSettings().setJavaScriptEnabled(true);
getSettings().setBuiltInZoomControls(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
Comment on lines +82 to +84

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling MIXED_CONTENT_COMPATIBILITY_MODE globally weakens WebView security (HTTPS pages can load HTTP subresources), increasing the risk of content injection during payment flows. Consider making this behavior opt-in (setter/attribute), or scoping it to only the specific authorization session where it’s needed, with a clear default that preserves the prior stricter behavior.

Copilot uses AI. Check for mistakes.
addJavascriptInterface(new D3SJSInterface(), JavaScriptNS);

setWebViewClient(new WebViewClient() {
Expand Down Expand Up @@ -117,6 +124,20 @@ public void onReceivedError(WebView view, int errorCode, String description, Str
}
}

@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
// Forward HTTP errors from the gateway (for example Stripe) so callers
// can react to a non 2xx response instead of seeing a blank screen.
Comment on lines +127 to +130

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onReceivedHttpError(...) references WebResourceRequest (API 21+) in the method signature, but this library’s minSdkVersion is 10. Because this anonymous WebViewClient class is instantiated unconditionally, it can fail class verification / loading on pre-Lollipop devices where android.webkit.WebResourceRequest doesn’t exist. Consider using two different WebViewClient implementations and only instantiating the one that overrides onReceivedHttpError on API >= 23 (or >= 21), or use reflection/AndroidX WebKit compat wrappers so the class can load on older SDKs.

Copilot uses AI. Check for mistakes.
String failingUrl = request.getUrl().toString();
if (isPostbackUrl(failingUrl)) return;
int status = errorResponse.getStatusCode();
String reason = errorResponse.getReasonPhrase();
Log.w("D3SView", "HTTP " + status + " " + reason + " for " + failingUrl);

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log.w("D3SView", ...) logs the full failing URL, which may include sensitive payment/session tokens (common in 3DS redirects). Library code should avoid emitting potentially sensitive URLs to logcat in production; either remove this log, redact query parameters, or guard it behind a debug-only flag.

Suggested change
Log.w("D3SView", "HTTP " + status + " " + reason + " for " + failingUrl);
Log.w("D3SView", "HTTP " + status + " " + reason);

Copilot uses AI. Check for mistakes.
if (authorizationListener != null) {
authorizationListener.onAuthorizationWebPageLoadingError(status, reason, failingUrl);
}
Comment on lines +128 to +138

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onReceivedHttpError is invoked for any resource (subframes, images, scripts) that returns an HTTP error. Forwarding every such failure to onAuthorizationWebPageLoadingError can produce false negatives (e.g., a 404 analytics pixel) and abort an otherwise successful 3DS flow. Please gate the callback to the main frame only (e.g., request.isForMainFrame()), and consider only forwarding when the failing URL matches the current top-level page being loaded.

Copilot uses AI. Check for mistakes.
}

private boolean isPostbackUrl(String url) {
return url.toLowerCase().startsWith(postbackUrl.toLowerCase());

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isPostbackUrl uses toLowerCase() without an explicit locale, which can behave incorrectly on some locales (e.g., Turkish) and potentially break postback detection. Use a locale-stable comparison (e.g., toLowerCase(Locale.ROOT) or regionMatches(true, ...)) to keep URL matching consistent.

Suggested change
return url.toLowerCase().startsWith(postbackUrl.toLowerCase());
return url != null
&& postbackUrl != null
&& url.length() >= postbackUrl.length()
&& url.regionMatches(true, 0, postbackUrl, 0, postbackUrl.length());

Copilot uses AI. Check for mistakes.
}
Expand Down
Loading