Skip to content

Commit dfde271

Browse files
AwkwardPeak7jobobby04
authored andcommitted
Spoof or remove X-Requested-With header from webview (#1812)
(cherry picked from commit 793d7fb) # Conflicts: # CHANGELOG.md # app/src/main/java/eu/kanade/tachiyomi/App.kt # core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt
1 parent 5346eac commit dfde271

File tree

4 files changed

+73
-17
lines changed

4 files changed

+73
-17
lines changed

app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package eu.kanade.presentation.webview
33
import android.content.pm.ApplicationInfo
44
import android.graphics.Bitmap
55
import android.webkit.WebResourceRequest
6+
import android.webkit.WebResourceResponse
67
import android.webkit.WebView
78
import androidx.compose.foundation.clickable
89
import androidx.compose.foundation.layout.Box
@@ -26,6 +27,7 @@ import androidx.compose.runtime.setValue
2627
import androidx.compose.ui.Alignment
2728
import androidx.compose.ui.Modifier
2829
import androidx.compose.ui.draw.clip
30+
import androidx.compose.ui.platform.LocalContext
2931
import androidx.compose.ui.platform.LocalUriHandler
3032
import androidx.compose.ui.unit.dp
3133
import com.kevinnzou.web.AccompanistWebViewClient
@@ -37,13 +39,18 @@ import eu.kanade.presentation.components.AppBar
3739
import eu.kanade.presentation.components.AppBarActions
3840
import eu.kanade.presentation.components.WarningBanner
3941
import eu.kanade.tachiyomi.BuildConfig
42+
import eu.kanade.tachiyomi.network.NetworkHelper
43+
import eu.kanade.tachiyomi.util.system.WebViewUtil
4044
import eu.kanade.tachiyomi.util.system.getHtml
4145
import eu.kanade.tachiyomi.util.system.setDefaultSettings
4246
import kotlinx.collections.immutable.persistentListOf
4347
import kotlinx.coroutines.launch
48+
import okhttp3.Request
4449
import tachiyomi.i18n.MR
4550
import tachiyomi.presentation.core.components.material.Scaffold
4651
import tachiyomi.presentation.core.i18n.stringResource
52+
import uy.kohesive.injekt.Injekt
53+
import uy.kohesive.injekt.api.get
4754

4855
@Composable
4956
fun WebViewScreenContent(
@@ -58,8 +65,11 @@ fun WebViewScreenContent(
5865
) {
5966
val state = rememberWebViewState(url = url, additionalHttpHeaders = headers)
6067
val navigator = rememberWebViewNavigator()
68+
val context = LocalContext.current
6169
val uriHandler = LocalUriHandler.current
6270
val scope = rememberCoroutineScope()
71+
val network = remember { Injekt.get<NetworkHelper>() }
72+
val spoofedPackageName = remember { WebViewUtil.spoofedPackageName(context) }
6373

6474
var currentUrl by remember { mutableStateOf(url) }
6575
var showCloudflareHelp by remember { mutableStateOf(false) }
@@ -114,6 +124,40 @@ fun WebViewScreenContent(
114124
}
115125
return super.shouldOverrideUrlLoading(view, request)
116126
}
127+
128+
override fun shouldInterceptRequest(
129+
view: WebView?,
130+
request: WebResourceRequest?,
131+
): WebResourceResponse? {
132+
return try {
133+
val internalRequest = Request.Builder().apply {
134+
url(request!!.url.toString())
135+
request.requestHeaders.forEach { (key, value) ->
136+
if (key == "X-Requested-With" && value in setOf(context.packageName, spoofedPackageName)) {
137+
return@forEach
138+
}
139+
addHeader(key, value)
140+
}
141+
method(request.method, null)
142+
}.build()
143+
144+
val response = network.nonCloudflareClient.newCall(internalRequest).execute()
145+
146+
val contentType = response.body.contentType()?.let { "${it.type}/${it.subtype}" } ?: "text/html"
147+
val contentEncoding = response.body.contentType()?.charset()?.name() ?: "utf-8"
148+
149+
WebResourceResponse(
150+
contentType,
151+
contentEncoding,
152+
response.code,
153+
response.message,
154+
response.headers.associate { it.first to it.second },
155+
response.body.byteStream(),
156+
)
157+
} catch (e: Throwable) {
158+
super.shouldInterceptRequest(view, request)
159+
}
160+
}
117161
}
118162
}
119163

app/src/main/java/eu/kanade/tachiyomi/App.kt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -277,18 +277,16 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
277277
try {
278278
// Override the value passed as X-Requested-With in WebView requests
279279
val stackTrace = Looper.getMainLooper().thread.stackTrace
280-
val chromiumElement = stackTrace.find {
281-
it.className.equals(
282-
"org.chromium.base.BuildInfo",
283-
ignoreCase = true,
284-
)
285-
}
286-
if (chromiumElement?.methodName.equals("getAll", ignoreCase = true)) {
287-
return WebViewUtil.SPOOF_PACKAGE_NAME
280+
val isChromiumCall = stackTrace.any {
281+
it.className.startsWith("org.chromium.") &&
282+
it.methodName in setOf("getAll", "getPackageName", "<init>")
288283
}
284+
285+
if (isChromiumCall) return WebViewUtil.spoofedPackageName(applicationContext)
289286
} catch (_: Exception) {
290287
}
291288
}
289+
292290
return super.getPackageName()
293291
}
294292

core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ open /* SY <-- */ class NetworkHelper(
2424
/* SY --> */
2525
open /* SY <-- */val cookieJar = AndroidCookieJar()
2626

27-
/* SY --> */
28-
open /* SY <-- */val client: OkHttpClient = run {
27+
private val clientBuilder: OkHttpClient.Builder = run {
2928
val builder = OkHttpClient.Builder()
3029
.cookieJar(cookieJar)
3130
.connectTimeout(30, TimeUnit.SECONDS)
@@ -49,10 +48,6 @@ open /* SY <-- */ class NetworkHelper(
4948
builder.addNetworkInterceptor(httpLoggingInterceptor)
5049
}
5150

52-
builder.addInterceptor(
53-
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider),
54-
)
55-
5651
when (preferences.dohProvider().get()) {
5752
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
5853
PREF_DOH_GOOGLE -> builder.dohGoogle()
@@ -66,11 +61,19 @@ open /* SY <-- */ class NetworkHelper(
6661
PREF_DOH_CONTROLD -> builder.dohControlD()
6762
PREF_DOH_NJALLA -> builder.dohNajalla()
6863
PREF_DOH_SHECAN -> builder.dohShecan()
64+
else -> builder
6965
}
70-
71-
builder.build()
7266
}
7367

68+
val nonCloudflareClient = clientBuilder.build()
69+
70+
/* SY --> */
71+
open /* SY <-- */ val client = clientBuilder
72+
.addInterceptor(
73+
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider),
74+
)
75+
.build()
76+
7477
/**
7578
* @deprecated Since extension-lib 1.5
7679
*/

core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/WebViewUtil.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import tachiyomi.core.common.util.system.logcat
1313
import kotlin.coroutines.resume
1414

1515
object WebViewUtil {
16-
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
16+
private const val CHROME_PACKAGE = "com.android.chrome"
17+
private const val SYSTEM_SETTINGS_PACKAGE = "com.android.settings"
1718

1819
const val MINIMUM_WEBVIEW_VERSION = 118
1920

@@ -58,6 +59,16 @@ object WebViewUtil {
5859

5960
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)
6061
}
62+
63+
fun spoofedPackageName(context: Context): String {
64+
return try {
65+
context.packageManager.getPackageInfo(CHROME_PACKAGE, PackageManager.GET_META_DATA)
66+
67+
CHROME_PACKAGE
68+
} catch (_: PackageManager.NameNotFoundException) {
69+
SYSTEM_SETTINGS_PACKAGE
70+
}
71+
}
6172
}
6273

6374
fun WebView.isOutdated(): Boolean {

0 commit comments

Comments
 (0)