From b88311a2d0c99f803c451c539b2b6a94d8afd1e6 Mon Sep 17 00:00:00 2001 From: LukeMackin Date: Tue, 21 Jul 2026 23:02:00 +0800 Subject: [PATCH] =?UTF-8?q?security:=20Bridge=E4=BB=A3=E7=90=86fetch+?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=B7=A8=E5=9F=9F=E8=AE=BE=E7=BD=AE+CSP?= =?UTF-8?q?=E5=A4=B4=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL修复: - 移除allowFileAccessFromFileURLs/allowUniversalAccessFromFileURLs - 移除mixedContentMode=ALWAYS_ALLOW - WebViewClient注入CSP: default-src 'none';script-src 'unsafe-inline';connect-src 'none' - 新增Bridge.fetchManifest(channel)/Bridge.fetchText(path)原生HTTP代理 - manifest JSON + version.txt通过Bridge读取,无需WebView跨域 --- .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../main/java/dev/yuzu/gca/MainActivity.kt | 56 +++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/client-android/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/packages/client-android/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 21a2fccd753f374f0497ea8d67cd752ed06628ff..95483c929f574eb787eb185ef71792927999ee2a 100644 GIT binary patch literal 17 UcmZQ>(-v{$|HmrA00zu8034J8uK)l5 literal 17 UcmZQ>(-v{$|HmrA00vAC0UY-O6951J diff --git a/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt b/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt index dea4bfa..419eabe 100644 --- a/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt +++ b/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt @@ -3,8 +3,11 @@ package dev.yuzu.gca import android.os.Bundle import android.util.Log import android.webkit.WebView +import android.webkit.WebViewClient import androidx.appcompat.app.AppCompatActivity import java.io.File +import java.net.HttpURLConnection +import java.net.URL class MainActivity : AppCompatActivity() { @@ -20,10 +23,17 @@ class MainActivity : AppCompatActivity() { settings.javaScriptEnabled = true settings.domStorageEnabled = true settings.allowFileAccess = true - settings.allowFileAccessFromFileURLs = true - settings.allowUniversalAccessFromFileURLs = true - settings.mixedContentMode = android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW addJavascriptInterface(Bridge(this@MainActivity), "Android") + webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView, url: String) { + view.evaluateJavascript(""" + var meta = document.createElement('meta'); + meta.httpEquiv = 'Content-Security-Policy'; + meta.content = "default-src 'none'; script-src 'unsafe-inline'; connect-src 'none'"; + document.head.appendChild(meta); + """.trimIndent(), null) + } + } if (cacheFile.exists()) { val html = cacheFile.readText() @@ -47,17 +57,57 @@ class MainActivity : AppCompatActivity() { ctx.cacheFile.writeText(html) Log.i(TAG, "Hot-update HTML saved (${html.length}B)") } + @android.webkit.JavascriptInterface fun clearCache() { ctx.cacheFile.delete() Log.i(TAG, "Cache cleared") } + @android.webkit.JavascriptInterface fun sha256(input: String): String { val md = java.security.MessageDigest.getInstance("SHA-256") val digest = md.digest(input.toByteArray(Charsets.UTF_8)) return digest.joinToString("") { "%02x".format(it) } } + + /** Fetch manifest JSON from Gitea — proxy to avoid CORS in WebView */ + @android.webkit.JavascriptInterface + fun fetchManifest(channel: String): String { + val url = "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/raw/branch/main/ota/h5/manifest-$channel.json" + Log.i(TAG, "Bridge fetching $url") + return try { + val conn = URL(url).openConnection() as HttpURLConnection + conn.connectTimeout = 10_000 + conn.readTimeout = 10_000 + conn.instanceFollowRedirects = true + val code = conn.responseCode + if (code != 200) throw Exception("HTTP $code") + conn.inputStream.bufferedReader().readText() + } catch (e: Exception) { + Log.e(TAG, "Bridge fetch failed: ${e.message}") + """{"error":"${e.message}"}""" + } + } + + /** Fetch text file from Gitea — proxy for version.txt etc */ + @android.webkit.JavascriptInterface + fun fetchText(path: String): String { + val url = "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/raw/branch/main/ota/$path" + Log.i(TAG, "Bridge fetching $url") + return try { + val conn = URL(url).openConnection() as HttpURLConnection + conn.connectTimeout = 10_000 + conn.readTimeout = 10_000 + conn.instanceFollowRedirects = true + val code = conn.responseCode + if (code != 200) throw Exception("HTTP $code") + conn.inputStream.bufferedReader().readText() + } catch (e: Exception) { + Log.e(TAG, "Bridge fetchText($path) failed: ${e.message}") + "ERROR: ${e.message}" + } + } } companion object { private const val TAG = "GCA-H5" }