Android WebView: bridge only on our own https page, external schemes blocked, file access off; payment URL must be http(s)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private var connection: Connection? = null
|
private var connection: Connection? = null
|
||||||
private val webHost: String = Uri.parse(BuildConfig.WEB_URL).host ?: ""
|
private val webHost: String = Uri.parse(BuildConfig.WEB_URL).host ?: ""
|
||||||
|
|
||||||
|
/** فقط خودِ سایت و فقط با https؛ «http://دامنهی ما» قابلِ دستکاریِ شبکه است و نباید پل داشته باشد. */
|
||||||
|
private fun isOurPage(uri: Uri?): Boolean = uri != null && uri.scheme == "https" && uri.host == webHost
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -48,12 +51,19 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
webView.settings.javaScriptEnabled = true
|
webView.settings.javaScriptEnabled = true
|
||||||
webView.settings.domStorageEnabled = true // توکنِ ورود در localStorage است
|
webView.settings.domStorageEnabled = true // توکنِ ورود در localStorage است
|
||||||
|
// WebView فقط آدرسِ وب را میخواند؛ دسترسی به فایل و content:// لازم نیست.
|
||||||
|
webView.settings.allowFileAccess = false
|
||||||
|
webView.settings.allowContentAccess = false
|
||||||
webView.webViewClient = object : WebViewClient() {
|
webView.webViewClient = object : WebViewClient() {
|
||||||
// هر آدرسی بیرونِ دامنهی خودمان در مرورگرِ گوشی باز میشود، نه داخلِ اپ؛ پس پل فقط
|
// هر آدرسی بیرونِ دامنهی خودمان در مرورگرِ گوشی باز میشود، نه داخلِ اپ؛ پس پل فقط
|
||||||
// در صفحههای خودمان در دسترس است.
|
// در صفحههای خودمان در دسترس است.
|
||||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||||
if (request.url.host == webHost) return false
|
if (isOurPage(request.url)) return false
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, request.url))
|
// فقط http(s) به مرورگرِ گوشی میرود؛ intent:، file: و schemeهای سفارشی بیصدا بسته میشوند
|
||||||
|
// (پیشتر هر آدرسی بیرونِ دامنه به ACTION_VIEW میرفت و صفحه میتوانست اپِ دلخواه باز کند).
|
||||||
|
if (request.url.scheme == "https" || request.url.scheme == "http") {
|
||||||
|
startActivity(Intent(Intent.ACTION_VIEW, request.url))
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,14 +155,17 @@ class MainActivity : AppCompatActivity() {
|
|||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun bazaarPurchase(sku: String, orderId: String) {
|
fun bazaarPurchase(sku: String, orderId: String) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (Uri.parse(webView.url ?: "").host != webHost) return@runOnUiThread
|
if (!isOurPage(Uri.parse(webView.url ?: ""))) return@runOnUiThread
|
||||||
purchase(sku, orderId)
|
purchase(sku, orderId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun bazaarConsume(purchaseToken: String) {
|
fun bazaarConsume(purchaseToken: String) {
|
||||||
runOnUiThread { consume(purchaseToken) }
|
runOnUiThread {
|
||||||
|
if (!isOurPage(Uri.parse(webView.url ?: ""))) return@runOnUiThread
|
||||||
|
consume(purchaseToken)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ function CheckoutSheet({ plan, bazaar, onClose, onPaid }: { plan: Plan | null; b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!order.paymentUrl) throw new Error('order without paymentUrl');
|
if (!order.paymentUrl) throw new Error('order without paymentUrl');
|
||||||
|
// فقط http(s): یک «javascript:» یا «data:» از پاسخِ API نباید با assign اجرا شود.
|
||||||
|
if (!/^https?:\/\//i.test(order.paymentUrl)) throw new Error('paymentUrl is not http(s)');
|
||||||
window.location.assign(order.paymentUrl);
|
window.location.assign(order.paymentUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(friendlyError(err, 'اتصال به درگاه انجام نشد، دوباره امتحان کن'));
|
setError(friendlyError(err, 'اتصال به درگاه انجام نشد، دوباره امتحان کن'));
|
||||||
|
|||||||
Reference in New Issue
Block a user