From cbefe1125d30b27997ce062a67990c1de354af9a Mon Sep 17 00:00:00 2001 From: Aria Moradi Date: Mon, 24 Apr 2023 18:26:04 +0330 Subject: [PATCH] migrate webview solution to Jep --- buildSrc/src/main/kotlin/Constants.kt | 2 +- gradle/libs.versions.toml | 5 +- server/build.gradle.kts | 5 +- .../interceptor/CloudflareInterceptor.kt | 176 ++++++++------- .../manga/controller/UpdateController.kt | 16 +- .../suwayomi/tachidesk/server/ServerConfig.kt | 1 + .../server/util/WebInterfaceManager.kt | 3 - .../cloudflare-js/canvas.fingerprinting.js | 28 --- .../resources/cloudflare-js/chrome.global.js | 52 ----- .../resources/cloudflare-js/chrome.plugin.js | 203 ------------------ .../resources/cloudflare-js/chrome.runtime.js | 170 --------------- .../resources/cloudflare-js/emulate.touch.js | 3 - .../cloudflare-js/navigator.permissions.js | 33 --- .../cloudflare-js/navigator.webdriver.js | 5 - .../src/main/resources/server-reference.conf | 1 + 15 files changed, 109 insertions(+), 594 deletions(-) delete mode 100644 server/src/main/resources/cloudflare-js/canvas.fingerprinting.js delete mode 100644 server/src/main/resources/cloudflare-js/chrome.global.js delete mode 100644 server/src/main/resources/cloudflare-js/chrome.plugin.js delete mode 100644 server/src/main/resources/cloudflare-js/chrome.runtime.js delete mode 100644 server/src/main/resources/cloudflare-js/emulate.touch.js delete mode 100644 server/src/main/resources/cloudflare-js/navigator.permissions.js delete mode 100644 server/src/main/resources/cloudflare-js/navigator.webdriver.js diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index 7a56bff90..dfb44fa66 100644 --- a/buildSrc/src/main/kotlin/Constants.kt +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -12,7 +12,7 @@ const val MainClass = "suwayomi.tachidesk.MainKt" // should be bumped with each stable release val tachideskVersion = System.getenv("ProductVersion") ?: "v0.7.0" -val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r983" +val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r1045" // counts commits on the master branch val tachideskRevision = runCatching { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index efb266236..69b28f85a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,6 @@ dex2jar = "v60" rhino = "1.7.14" settings = "1.0.0-RC" twelvemonkeys = "3.9.4" -playwright = "1.28.0" [libraries] # Kotlin @@ -95,8 +94,8 @@ appdirs = "net.harawata:appdirs:1.2.1" zip4j = "net.lingala.zip4j:zip4j:2.11.2" junrar = "com.github.junrar:junrar:7.5.3" -# CloudflareInterceptor -playwright = { module = "com.microsoft.playwright:playwright", version.ref = "playwright" } +# CloudflareInterceptor WebView +jep = "black.ninia:jep:4.1.1" # AES/CBC/PKCS7Padding Cypher provider bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.72" diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 57f3ffbb0..3ab66ed60 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -51,7 +51,7 @@ dependencies { implementation(libs.junrar) // CloudflareInterceptor - implementation(libs.playwright) + implementation(libs.jep) // AES/CBC/PKCS7Padding Cypher provider for zh.copymanga implementation(libs.bouncycastle) @@ -168,7 +168,8 @@ tasks { application.applicationDefaultJvmArgs = listOf( "-Dsuwayomi.tachidesk.config.server.webUIInterface=electron", // Change this to the installed electron application - "-Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron" + "-Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron", +// "-Djava.library.path=/home/armor/programming/github-clones/undetected-chromedriver/venv/lib/python3.10/site-packages/jep" ) } } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt index 2c7222e22..659b62355 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt @@ -1,24 +1,24 @@ package eu.kanade.tachiyomi.network.interceptor -import com.microsoft.playwright.Browser -import com.microsoft.playwright.BrowserType.LaunchOptions -import com.microsoft.playwright.Page -import com.microsoft.playwright.Playwright -import com.microsoft.playwright.PlaywrightException import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.interceptor.CFClearance.resolveWithWebView +import jep.SharedInterpreter +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import mu.KotlinLogging import okhttp3.Cookie import okhttp3.HttpUrl import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response -import suwayomi.tachidesk.server.ServerConfig +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import suwayomi.tachidesk.server.serverConfig import uy.kohesive.injekt.injectLazy import java.io.IOException -import kotlin.time.Duration.Companion.seconds -import kotlin.time.DurationUnit +import java.util.concurrent.TimeUnit class CloudflareInterceptor : Interceptor { private val logger = KotlinLogging.logger {} @@ -38,10 +38,11 @@ class CloudflareInterceptor : Interceptor { return originalResponse } - throw IOException("playwrite is diabled for v0.6.7") - logger.debug { "Cloudflare anti-bot is on, CloudflareInterceptor is kicking in..." } + if (!serverConfig.webviewEnabled) + throw CloudflareBypassException("Webview is disabled, enable it in server config") + return try { originalResponse.close() network.cookies.remove(originalRequest.url.toUri()) @@ -72,9 +73,14 @@ object CFClearance { private val network: NetworkHelper by injectLazy() init { - // Fix the default DriverJar issue by providing our own implementation - // ref: https://github.com/microsoft/playwright-java/issues/1138 - System.setProperty("playwright.driver.impl", "suwayomi.tachidesk.server.util.DriverJar") + SharedInterpreter().use { jep -> + val uc = "/home/armor/programming/github-clones/undetected-chromedriver" + + jep.exec("import sys") + jep.exec("sys.path.insert(0,'$uc')") + + jep.exec("import undetected_chromedriver") // Cache import + } } fun resolveWithWebView(originalRequest: Request): Request { @@ -82,24 +88,31 @@ object CFClearance { logger.debug { "resolveWithWebView($url)" } - val cookies = Playwright.create().use { playwright -> - playwright.chromium().launch( - LaunchOptions() - .setHeadless(false) - .apply { - if (serverConfig.socksProxyEnabled) { - setProxy("socks5://${serverConfig.socksProxyHost}:${serverConfig.socksProxyPort}") - } - } - ).use { browser -> - val userAgent = originalRequest.header("User-Agent") - if (userAgent != null) { - browser.newContext(Browser.NewContextOptions().setUserAgent(userAgent)).use { browserContext -> - browserContext.newPage().use { getCookies(it, url) } - } - } else { - browser.newPage().use { getCookies(it, url) } + val cookies = SharedInterpreter().use { jep -> + try { + jep.exec("import undetected_chromedriver as uc") + + jep.exec("options = uc.ChromeOptions()") + + if (serverConfig.socksProxyEnabled) { + val proxy = "socks5://${serverConfig.socksProxyHost}:${serverConfig.socksProxyPort}" + jep.exec("options.add_argument('--proxy-server=$proxy')") } + jep.exec("driver = uc.Chrome(options=options)") +// val userAgent = originalRequest.header("User-Agent") + +// if (userAgent != null) { +// browser.newContext(Browser.NewContextOptions().setUserAgent(userAgent)).use { browserContext -> +// browserContext.newPage().use { getCookies(it, url) } +// } +// } else { +// browser.newPage().use { getCookies(it, url) } +// } + jep.exec("driver.get('$url')") + + getCookies(jep, url) + } finally { + jep.exec("driver.quit()") } } @@ -139,44 +152,62 @@ object CFClearance { fun getWebViewUserAgent(): String { return try { - throw PlaywrightException("playwrite is diabled for v0.6.7") + if (!serverConfig.webviewEnabled) + throw CloudflareBypassException("Webview is disabled, enable it in server config") - Playwright.create().use { playwright -> - playwright.chromium().launch( - LaunchOptions() - .setHeadless(true) - ).use { browser -> - browser.newPage().use { page -> - val userAgent = page.evaluate("() => {return navigator.userAgent}") as String - logger.debug { "WebView User-Agent is $userAgent" } - return userAgent - } - } + SharedInterpreter().use { jep -> + jep.exec("import undetected_chromedriver as uc") + + jep.exec("options = uc.ChromeOptions()") + jep.exec("options.add_argument('--headless')") + jep.exec("options.add_argument('--disable-gpu')") + jep.exec("driver = uc.Chrome(options=options)") + + jep.exec("userAgent = driver.execute_script('return navigator.userAgent')") + val userAgent = jep.getValue("userAgent", java.lang.String::class.java).toString() + jep.exec("driver.quit()") + + userAgent } - } catch (e: PlaywrightException) { - // Playwright might fail on headless environments like docker - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" + } catch (e: Exception) { + // Webview might fail on headless environments like docker + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" } } - private fun getCookies(page: Page, url: String): List { - applyStealthInitScripts(page) - page.navigate(url) - val challengeResolved = waitForChallengeResolve(page) + @Serializable + data class PythonSeleniumCookie( + val domain: String, + val expiry: Long?, + val httpOnly: Boolean, + val name: String, + val path: String, + val sameSite: String, + val secure: Boolean, + val value: String + ) + + private val json by DI.global.instance() + private fun getCookies(jep: SharedInterpreter, url: String): List { + val challengeResolved = waitForChallengeResolve(jep) return if (challengeResolved) { - val cookies = page.context().cookies() + jep.exec("import json") + jep.exec("cookies = json.dumps(driver.get_cookies())") + val cookiesJson = jep.getValue("cookies", java.lang.String::class.java).toString() + val cookies = json.decodeFromString>(cookiesJson) logger.debug { - val userAgent = page.evaluate("() => {return navigator.userAgent}") - "Playwright User-Agent is $userAgent" + jep.exec("userAgent = driver.execute_script('return navigator.userAgent')") + val userAgent = jep.getValue("userAgent", java.lang.String::class.java).toString() + "Webview User-Agent is $userAgent" } - // Convert PlayWright cookies to OkHttp cookies + // Convert Webview cookies to OkHttp cookies cookies.map { Cookie.Builder() .domain(it.domain.removePrefix(".")) - .expiresAt(it.expires?.times(1000)?.toLong() ?: Long.MAX_VALUE) + .expiresAt(it.expiry?.times(1000) ?: Long.MAX_VALUE) .name(it.name) .path(it.path) .value(it.value).apply { @@ -185,39 +216,18 @@ object CFClearance { }.build() } } else { - logger.debug { "Cloudflare challenge failed to resolve" } - throw CloudflareBypassException() + throw CloudflareBypassException("Cloudflare challenge failed to resolve") } } - // ref: https://github.com/vvanglro/cf-clearance/blob/44124a8f06d8d0ecf2bf558a027082ff88dab435/cf_clearance/stealth.py#L18 - private val stealthInitScripts by lazy { - arrayOf( - ServerConfig::class.java.getResource("/cloudflare-js/canvas.fingerprinting.js")!!.readText(), - ServerConfig::class.java.getResource("/cloudflare-js/chrome.global.js")!!.readText(), - ServerConfig::class.java.getResource("/cloudflare-js/emulate.touch.js")!!.readText(), - ServerConfig::class.java.getResource("/cloudflare-js/navigator.permissions.js")!!.readText(), - ServerConfig::class.java.getResource("/cloudflare-js/navigator.webdriver.js")!!.readText(), - ServerConfig::class.java.getResource("/cloudflare-js/chrome.runtime.js")!!.readText(), - ServerConfig::class.java.getResource("/cloudflare-js/chrome.plugin.js")!!.readText() - ) - } - - // ref: https://github.com/vvanglro/cf-clearance/blob/44124a8f06d8d0ecf2bf558a027082ff88dab435/cf_clearance/stealth.py#L76 - private fun applyStealthInitScripts(page: Page) { - for (script in stealthInitScripts) { - page.addInitScript(script) - } - } - - // ref: https://github.com/vvanglro/cf-clearance/blob/44124a8f06d8d0ecf2bf558a027082ff88dab435/cf_clearance/retry.py#L21 - private fun waitForChallengeResolve(page: Page): Boolean { - // sometimes the user has to solve the captcha challenge manually, potentially wait a long time + private fun waitForChallengeResolve(jep: SharedInterpreter): Boolean { + // sometimes the user has to solve the captcha challenge manually and multiple times, potentially wait a long time val timeoutSeconds = 120 repeat(timeoutSeconds) { - page.waitForTimeout(1.seconds.toDouble(DurationUnit.MILLISECONDS)) + TimeUnit.SECONDS.sleep(1) val success = try { - page.querySelector("#challenge-form") == null + jep.exec("r = driver.execute_script('return document.querySelector(\"#challenge-form\") == null')") + jep.getValue("r", java.lang.Boolean::class.java).toString().toBoolean() } catch (e: Exception) { logger.debug(e) { "query Error" } false @@ -226,6 +236,6 @@ object CFClearance { } return false } - - private class CloudflareBypassException : Exception() } +private class CloudflareBypassException(message: String?) : Exception(message) + diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt index 06f055a68..e12b2dc1f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt @@ -1,5 +1,12 @@ package suwayomi.tachidesk.manga.controller +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + import eu.kanade.tachiyomi.source.model.UpdateStrategy import io.javalin.http.HttpCode import io.javalin.websocket.WsConfig @@ -20,13 +27,6 @@ import suwayomi.tachidesk.server.util.handler import suwayomi.tachidesk.server.util.pathParam import suwayomi.tachidesk.server.util.withOperation -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - object UpdateController { private val logger = KotlinLogging.logger { } @@ -115,7 +115,7 @@ object UpdateController { updater.addMangasToQueue( mangasToUpdate - .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, MangaDataClass::title)), + .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, MangaDataClass::title)) ) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt index 1fac844dd..cad35b211 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt @@ -44,6 +44,7 @@ class ServerConfig(config: Config, moduleName: String = MODULE_NAME) : SystemPro // misc val debugLogsEnabled: Boolean = debugLogsEnabled(GlobalConfigManager.config) val systemTrayEnabled: Boolean by overridableConfig + val webviewEnabled : Boolean by overridableConfig companion object { fun register(config: Config) = ServerConfig(config.getConfig(MODULE_NAME)) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt index 52ba7dbf4..54a2576e4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt @@ -7,7 +7,6 @@ package suwayomi.tachidesk.server.util * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import kotlinx.serialization.json.Json import mu.KotlinLogging import net.lingala.zip4j.ZipFile import org.kodein.di.DI @@ -16,7 +15,6 @@ import org.kodein.di.instance import suwayomi.tachidesk.server.ApplicationDirs import suwayomi.tachidesk.server.BuildConfig import suwayomi.tachidesk.server.serverConfig -import uy.kohesive.injekt.injectLazy import java.io.File import java.io.InputStream import java.net.HttpURLConnection @@ -26,7 +24,6 @@ import java.security.MessageDigest private val logger = KotlinLogging.logger {} private val applicationDirs by DI.global.instance() -private val json: Json by injectLazy() private val tmpDir = System.getProperty("java.io.tmpdir") private fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) } diff --git a/server/src/main/resources/cloudflare-js/canvas.fingerprinting.js b/server/src/main/resources/cloudflare-js/canvas.fingerprinting.js deleted file mode 100644 index 836175676..000000000 --- a/server/src/main/resources/cloudflare-js/canvas.fingerprinting.js +++ /dev/null @@ -1,28 +0,0 @@ -(function () { - const ORIGINAL_CANVAS = HTMLCanvasElement.prototype[name]; - Object.defineProperty(HTMLCanvasElement.prototype, name, { - "value": function () { - var shift = { - 'r': Math.floor(Math.random() * 10) - 5, - 'g': Math.floor(Math.random() * 10) - 5, - 'b': Math.floor(Math.random() * 10) - 5, - 'a': Math.floor(Math.random() * 10) - 5 - }; - var width = this.width, - height = this.height, - context = this.getContext("2d"); - var imageData = context.getImageData(0, 0, width, height); - for (var i = 0; i < height; i++) { - for (var j = 0; j < width; j++) { - var n = ((i * (width * 4)) + (j * 4)); - imageData.data[n + 0] = imageData.data[n + 0] + shift.r; - imageData.data[n + 1] = imageData.data[n + 1] + shift.g; - imageData.data[n + 2] = imageData.data[n + 2] + shift.b; - imageData.data[n + 3] = imageData.data[n + 3] + shift.a; - } - } - context.putImageData(imageData, 0, 0); - return ORIGINAL_CANVAS.apply(this, arguments); - } - }); -})(this); diff --git a/server/src/main/resources/cloudflare-js/chrome.global.js b/server/src/main/resources/cloudflare-js/chrome.global.js deleted file mode 100644 index 243a28991..000000000 --- a/server/src/main/resources/cloudflare-js/chrome.global.js +++ /dev/null @@ -1,52 +0,0 @@ -Object.defineProperty(window, 'chrome', { - value: new Proxy(window.chrome, { - has: (target, key) => true, - get: (target, key) => { - return { - app: { - isInstalled: false, - }, - webstore: { - onInstallStageChanged: {}, - onDownloadProgress: {}, - }, - runtime: { - PlatformOs: { - MAC: 'mac', - WIN: 'win', - ANDROID: 'android', - CROS: 'cros', - LINUX: 'linux', - OPENBSD: 'openbsd', - }, - PlatformArch: { - ARM: 'arm', - X86_32: 'x86-32', - X86_64: 'x86-64', - }, - PlatformNaclArch: { - ARM: 'arm', - X86_32: 'x86-32', - X86_64: 'x86-64', - }, - RequestUpdateCheckStatus: { - THROTTLED: 'throttled', - NO_UPDATE: 'no_update', - UPDATE_AVAILABLE: 'update_available', - }, - OnInstalledReason: { - INSTALL: 'install', - UPDATE: 'update', - CHROME_UPDATE: 'chrome_update', - SHARED_MODULE_UPDATE: 'shared_module_update', - }, - OnRestartRequiredReason: { - APP_UPDATE: 'app_update', - OS_UPDATE: 'os_update', - PERIODIC: 'periodic', - }, - }, - } - } - }) -}); diff --git a/server/src/main/resources/cloudflare-js/chrome.plugin.js b/server/src/main/resources/cloudflare-js/chrome.plugin.js deleted file mode 100644 index e996d62c0..000000000 --- a/server/src/main/resources/cloudflare-js/chrome.plugin.js +++ /dev/null @@ -1,203 +0,0 @@ -(function () { - const plugin0 = Object.create(Plugin.prototype); - - const mimeType0 = Object.create(MimeType.prototype); - const mimeType1 = Object.create(MimeType.prototype); - Object.defineProperties(mimeType0, { - type: { - get: () => 'application/pdf', - }, - suffixes: { - get: () => 'pdf', - }, - }); - - Object.defineProperties(mimeType1, { - type: { - get: () => 'text/pdf', - }, - suffixes: { - get: () => 'pdf', - }, - }); - - Object.defineProperties(plugin0, { - name: { - get: () => 'Chrome PDF Viewer', - }, - description: { - get: () => 'Portable Document Format', - }, - 0: { - get: () => { - return mimeType0; - }, - }, - 1: { - get: () => { - return mimeType1; - }, - }, - length: { - get: () => 2, - }, - filename: { - get: () => 'internal-pdf-viewer', - }, - }); - - const plugin1 = Object.create(Plugin.prototype); - Object.defineProperties(plugin1, { - name: { - get: () => 'Chromium PDF Viewer', - }, - description: { - get: () => 'Portable Document Format', - }, - 0: { - get: () => { - return mimeType0; - }, - }, - 1: { - get: () => { - return mimeType1; - }, - }, - length: { - get: () => 2, - }, - filename: { - get: () => 'internal-pdf-viewer', - }, - }); - - const plugin2 = Object.create(Plugin.prototype); - Object.defineProperties(plugin2, { - name: { - get: () => 'Microsoft Edge PDF Viewer', - }, - description: { - get: () => 'Portable Document Format', - }, - 0: { - get: () => { - return mimeType0; - }, - }, - 1: { - get: () => { - return mimeType1; - }, - }, - length: { - get: () => 2, - }, - filename: { - get: () => 'internal-pdf-viewer', - }, - }); - - const plugin3 = Object.create(Plugin.prototype); - Object.defineProperties(plugin3, { - name: { - get: () => 'PDF Viewer', - }, - description: { - get: () => 'Portable Document Format', - }, - 0: { - get: () => { - return mimeType0; - }, - }, - 1: { - get: () => { - return mimeType1; - }, - }, - length: { - get: () => 2, - }, - filename: { - get: () => 'internal-pdf-viewer', - }, - }); - - const plugin4 = Object.create(Plugin.prototype); - Object.defineProperties(plugin4, { - name: { - get: () => 'WebKit built-in PDF', - }, - description: { - get: () => 'Portable Document Format', - }, - 0: { - get: () => { - return mimeType0; - }, - }, - 1: { - get: () => { - return mimeType1; - }, - }, - length: { - get: () => 2, - }, - filename: { - get: () => 'internal-pdf-viewer', - }, - }); - - const pluginArray = Object.create(PluginArray.prototype); - - pluginArray['0'] = plugin0; - pluginArray['1'] = plugin1; - pluginArray['2'] = plugin2; - pluginArray['3'] = plugin3; - pluginArray['4'] = plugin4; - - let refreshValue; - - Object.defineProperties(pluginArray, { - length: { - get: () => 5, - }, - item: { - value: (index) => { - if (index > 4294967295) { - index = index % 4294967296; - } - switch (index) { - case 0: - return plugin3; - case 1: - return plugin0; - case 2: - return plugin1; - case 3: - return plugin2; - case 4: - return plugin4; - default: - break; - } - }, - }, - refresh: { - get: () => { - return refreshValue; - }, - set: (value) => { - refreshValue = value; - }, - }, - }); - - Object.defineProperty(Object.getPrototypeOf(navigator), 'plugins', { - get: () => { - return pluginArray; - }, - }); -})(); diff --git a/server/src/main/resources/cloudflare-js/chrome.runtime.js b/server/src/main/resources/cloudflare-js/chrome.runtime.js deleted file mode 100644 index b00befadd..000000000 --- a/server/src/main/resources/cloudflare-js/chrome.runtime.js +++ /dev/null @@ -1,170 +0,0 @@ -(function () { - window.chrome = {}; - window.chrome.app = { - InstallState: { - DISABLED: 'disabled', - INSTALLED: 'installed', - NOT_INSTALLED: 'not_installed', - }, - RunningState: { - CANNOT_RUN: 'cannot_run', - READY_TO_RUN: 'ready_to_run', - RUNNING: 'running', - }, - getDetails: () => { - '[native code]'; - }, - getIsInstalled: () => { - '[native code]'; - }, - installState: () => { - '[native code]'; - }, - get isInstalled() { - return false; - }, - runningState: () => { - '[native code]'; - }, - }; - - window.chrome.runtime = { - OnInstalledReason: { - CHROME_UPDATE: 'chrome_update', - INSTALL: 'install', - SHARED_MODULE_UPDATE: 'shared_module_update', - UPDATE: 'update', - }, - OnRestartRequiredReason: { - APP_UPDATE: 'app_update', - OS_UPDATE: 'os_update', - PERIODIC: 'periodic', - }, - PlatformArch: { - ARM: 'arm', - ARM64: 'arm64', - MIPS: 'mips', - MIPS64: 'mips64', - X86_32: 'x86-32', - X86_64: 'x86-64', - }, - PlatformNaclArch: { - ARM: 'arm', - MIPS: 'mips', - MIPS64: 'mips64', - X86_32: 'x86-32', - X86_64: 'x86-64', - }, - PlatformOs: { - ANDROID: 'android', - CROS: 'cros', - FUCHSIA: 'fuchsia', - LINUX: 'linux', - MAC: 'mac', - OPENBSD: 'openbsd', - WIN: 'win', - }, - RequestUpdateCheckStatus: { - NO_UPDATE: 'no_update', - THROTTLED: 'throttled', - UPDATE_AVAILABLE: 'update_available', - }, - connect() { - '[native code]'; - }, - sendMessage() { - '[native code]'; - }, - id: undefined, - }; - - let startE = Date.now(); - window.chrome.csi = function () { - '[native code]'; - return { - startE: startE, - onloadT: startE + 281, - pageT: 3947.235, - tran: 15, - }; - }; - - window.chrome.loadTimes = function () { - '[native code]'; - return { - get requestTime() { - return startE / 1000; - }, - get startLoadTime() { - return startE / 1000; - }, - get commitLoadTime() { - return startE / 1000 + 0.324; - }, - get finishDocumentLoadTime() { - return startE / 1000 + 0.498; - }, - get finishLoadTime() { - return startE / 1000 + 0.534; - }, - get firstPaintTime() { - return startE / 1000 + 0.437; - }, - get firstPaintAfterLoadTime() { - return 0; - }, - get navigationType() { - return 'Other'; - }, - get wasFetchedViaSpdy() { - return true; - }, - get wasNpnNegotiated() { - return true; - }, - get npnNegotiatedProtocol() { - return 'h3'; - }, - get wasAlternateProtocolAvailable() { - return false; - }, - get connectionInfo() { - return 'h3'; - }, - }; - }; -})(); - -// Bypass OOPIF test -(function performance_memory() { - const jsHeapSizeLimitInt = 4294705152; - - const total_js_heap_size = 35244183; - const used_js_heap_size = [ - 17632315, 17632315, 17632315, 17634847, 17636091, 17636751, - ]; - - let counter = 0; - - let MemoryInfoProto = Object.getPrototypeOf(performance.memory); - Object.defineProperties(MemoryInfoProto, { - jsHeapSizeLimit: { - get: () => { - return jsHeapSizeLimitInt; - }, - }, - totalJSHeapSize: { - get: () => { - return total_js_heap_size; - }, - }, - usedJSHeapSize: { - get: () => { - if (counter > 5) { - counter = 0; - } - return used_js_heap_size[counter++]; - }, - }, - }); -})(); diff --git a/server/src/main/resources/cloudflare-js/emulate.touch.js b/server/src/main/resources/cloudflare-js/emulate.touch.js deleted file mode 100644 index 2e063974e..000000000 --- a/server/src/main/resources/cloudflare-js/emulate.touch.js +++ /dev/null @@ -1,3 +0,0 @@ -Object.defineProperty(navigator, 'maxTouchPoints', { - get: () => 1 -}); diff --git a/server/src/main/resources/cloudflare-js/navigator.permissions.js b/server/src/main/resources/cloudflare-js/navigator.permissions.js deleted file mode 100644 index c56a7a82b..000000000 --- a/server/src/main/resources/cloudflare-js/navigator.permissions.js +++ /dev/null @@ -1,33 +0,0 @@ -// https://github.com/microlinkhq/browserless/blob/master/packages/goto/src/evasions/navigator-permissions.js -if (!window.Notification) { - window.Notification = { - permission: 'denied' - } -} -const originalQuery = window.navigator.permissions.query -window.navigator.permissions.__proto__.query = parameters => - parameters.name === 'notifications' - ? Promise.resolve({state: window.Notification.permission}) - : originalQuery(parameters) -const oldCall = Function.prototype.call - -function call() { - return oldCall.apply(this, arguments) -} - -Function.prototype.call = call -const nativeToStringFunctionString = Error.toString().replace(/Error/g, 'toString') -const oldToString = Function.prototype.toString - -function functionToString() { - if (this === window.navigator.permissions.query) { - return 'function query() { [native code] }' - } - if (this === functionToString) { - return nativeToStringFunctionString - } - return oldCall.call(oldToString, this) -} - -// eslint-disable-next-line -Function.prototype.toString = functionToString diff --git a/server/src/main/resources/cloudflare-js/navigator.webdriver.js b/server/src/main/resources/cloudflare-js/navigator.webdriver.js deleted file mode 100644 index 749d4e0c1..000000000 --- a/server/src/main/resources/cloudflare-js/navigator.webdriver.js +++ /dev/null @@ -1,5 +0,0 @@ -Object.defineProperty(Navigator.prototype, 'webdriver', { - get() { - return false; - }, -}); diff --git a/server/src/main/resources/server-reference.conf b/server/src/main/resources/server-reference.conf index d6a1d8898..6b4bc6fbf 100644 --- a/server/src/main/resources/server-reference.conf +++ b/server/src/main/resources/server-reference.conf @@ -29,3 +29,4 @@ server.basicAuthPassword = "" # misc server.debugLogsEnabled = false server.systemTrayEnabled = true +server.webviewEnabled = false