Skip to content

🔒 [security fix] Fix WebView JavascriptInterface vulnerability - #194

Closed
alvin000009238 wants to merge 1 commit into
mainfrom
jules-4970981808506226386-94fffc1b
Closed

🔒 [security fix] Fix WebView JavascriptInterface vulnerability#194
alvin000009238 wants to merge 1 commit into
mainfrom
jules-4970981808506226386-94fffc1b

Conversation

@alvin000009238

Copy link
Copy Markdown
Owner

🎯 What: Replaced insecure addJavascriptInterface with secure WebViewCompat.addWebMessageListener.

⚠️ Risk: Any site loaded in the WebView (via open redirect, ad injection, etc.) could potentially invoke Android code through the JavaScript interface if it has XSS or is malicious, as it is indiscriminately available to all origins loaded in the WebView. addJavascriptInterface does not allow specifying an origin, making it inherently vulnerable.

🛡️ Solution: Used WebViewCompat.addWebMessageListener from androidx.webkit:webkit. This modern API restricts access to the injected JavaScript object only to the specific, trusted origins ("https://shcloud2.k12ea.gov.tw"). The JavaScript payload was updated to use window.AndroidLogin.postMessage to communicate securely.


PR created automatically by Jules for task 4970981808506226386 started by @alvin000009238

Co-authored-by: alvin000009238 <107313913+alvin000009238@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 17, 2026 14:33
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request replaces the insecure addJavascriptInterface with WebViewCompat.addWebMessageListener to restrict WebView communication to trusted origins, enhancing security. The review feedback points out a potential bug where the onLoginSuccess lambda is captured inside the AndroidView's factory block, which could lead to stale callback execution during recompositions, and suggests using rememberUpdatedState to resolve it.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

loginHandled = true
val cookieString = CookieManager.getInstance()
.getCookie("https://$SCHOOL_DOMAIN") ?: ""
post { onLoginSuccess(studentNo, cookieString) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The onLoginSuccess lambda is captured inside the AndroidView's factory block, which only runs once when the WebView is created. If onLoginSuccess changes during recomposition (for example, if the parent composable recomposes with a new callback instance), the WebView will still invoke the stale version of the lambda captured during the initial creation.\n\nTo prevent this stale lambda capture bug, you should use rememberUpdatedState to keep a reference to the latest lambda.\n\nSince the top of WebViewContent is outside the modified diff hunks, you can apply this fix by adding the following at the beginning of WebViewContent (around line 193):\n\nkotlin\nval currentOnLoginSuccess by rememberUpdatedState(onLoginSuccess)\n\n\nAnd then update line 237 to use currentOnLoginSuccess:\n\nkotlin\npost { currentOnLoginSuccess(studentNo, cookieString) }\n

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8382f6b54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

val cookieString = CookieManager.getInstance()
.getCookie("https://$SCHOOL_DOMAIN") ?: ""
post { onLoginSuccess(studentNo, cookieString) }
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add an unsupported-WebView path

On devices whose installed WebView provider does not support WEB_MESSAGE_LISTENER, this branch simply skips registering any native bridge while the injected login hook now only calls window.AndroidLogin.postMessage(...). In that environment the school login can succeed in the WebView, but onLoginSuccess is never invoked, leaving users stuck on the login screen with no error or recovery. Please surface an update/unsupported-WebView flow or provide a safe fallback for this case.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Replace the insecure WebView JavaScript bridge with an origin-restricted messaging API to mitigate addJavascriptInterface abuse.

Changes:

  • Replaced addJavascriptInterface + @JavascriptInterface bridge with WebViewCompat.addWebMessageListener gated by WebViewFeature.WEB_MESSAGE_LISTENER.
  • Updated injected JS payload to use window.AndroidLogin.postMessage(loginId) for communication.
  • Added androidx.webkit:webkit dependency to support WebViewCompat APIs.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
pr_details.txt Adds a textual summary of the PR’s security rationale and approach.
android/app/src/main/java/com/clhs/score/ui/WebViewLoginScreen.kt Switches WebView bridge from addJavascriptInterface to origin-restricted messaging and updates JS hook accordingly.
android/app/build.gradle.kts Adds the AndroidX WebKit dependency required for WebViewCompat / WebViewFeature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +225 to 241
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
WebViewCompat.addWebMessageListener(
this,
"AndroidLogin",
setOf("https://$SCHOOL_DOMAIN"),
) { _, message, _, _, _ ->
val studentNo = message.data
if (studentNo != null) {
if (loginHandled || !isTrustedLoginPage) return@addWebMessageListener
loginHandled = true
val cookieString = CookieManager.getInstance()
.getCookie("https://$SCHOOL_DOMAIN") ?: ""
post { onLoginSuccess(studentNo, cookieString) }
}
}
}
addJavascriptInterface(jsInterface, "AndroidLogin")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants