diff --git a/src/androidTest/java/com/owncloud/android/ClientIT.kt b/src/androidTest/java/com/owncloud/android/ClientIT.kt new file mode 100644 index 0000000000..3f95462368 --- /dev/null +++ b/src/androidTest/java/com/owncloud/android/ClientIT.kt @@ -0,0 +1,334 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2021 Tobias Kaminsky + * Copyright (C) 2021 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android + +import android.content.Context +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Test +import javax.net.ssl.SSLHandshakeException + +abstract class ClientIT { + val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + + // 🎫Certificate + @Test(expected = SSLHandshakeException::class) + fun expiredCert() { + testConnection("https://expired.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun wrongHostCert() { + testConnection("https://wrong.host.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun selfSignedCert() { + testConnection("https://self-signed.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun untrustedRootCert() { + testConnection("https://untrusted-root.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun revokedCert() { + testConnection("https://revoked.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun pinningTestCert() { + testConnection("https://pinning-test.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun noCommonName() { + testConnection("https://no-common-name.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun noSubject() { + testConnection("https://no-subject.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun incompleteChain() { + testConnection("https://incomplete-chain.badssl.com") + } + + @Test + fun sha256() { + testConnection("https://sha256.badssl.com") + } + + @Test + fun sha384() { + testConnection("https://sha384.badssl.com") + } + + @Test + fun sha512() { + testConnection("https://sha512.badssl.com") + } + + @Test + fun sans1000() { + testConnection("https://1000-sans.badssl.com") + } + + @Test + fun sans10000() { + testConnection("https://10000-sans.badssl.com") + } + + @Test + fun ecc256() { + testConnection("https://ecc-256-sans.badssl.com") + } + + @Test + fun ecc384() { + testConnection("https://ecc-384-sans.badssl.com") + } + + @Test + fun rsa2048() { + testConnection("https://rsa2048.badssl.com") + } + + @Test + fun rsa4096() { + testConnection("https://rsa4096.badssl.com") + } + + @Test + fun rsa8192() { + testConnection("https://rsa8192.badssl.com") + } + + @Test + fun extendedValidation() { + testConnection("https://extended-validation.badssl.com") + } + + // 🎟Client Certificate + // not tested + + // 🖼Mixed Content + // TODO shall we? + + // ✏️HTTP + // TODO shall we? + + // 🔀Cipher Suite + @Test + fun cbc() { + testConnection("https://cbc.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun rc4md5() { + testConnection("https://rc4-md5.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun rc4() { + testConnection("https://rc4.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun des3() { + testConnection("https://3des.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun testNull() { + testConnection("https://null.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun mozillaOld() { + testConnection("https://mozilla-old.badssl.com") + } + + @Test + fun mozillaIntermediate() { + testConnection("https://mozilla-intermediate.badssl.com") + } + + @Test + fun mozillaModern() { + testConnection("https://mozilla-modern.badssl.com") + } + + // 🔑Key Exchange + @Test(expected = SSLHandshakeException::class) + fun dh480() { + testConnection("https://dh480.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun dh512() { + testConnection("https://dh512.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun dh1024() { + testConnection("https://dh1024.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun dh2048() { + testConnection("https://dh2048.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun dhSmallSubgroup() { + testConnection("https://dh-small-subgroup.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun dhComposite() { + testConnection("https://dh-composite.badssl.com") + } + + @Test + fun staticRSA() { + testConnection("https://static-rsa.badssl.com") + } + + // ↔️Protocol + @Test + fun tls1() { + testConnection("https://tls-v1-0.badssl.com:1010") + } + + @Test + fun tls11() { + testConnection("https://tls-v1-1.badssl.com:1011") + } + + @Test + fun tls12() { + testConnection("https://tls-v1-2.badssl.com:1012") + } + + // 🔍Certificate Transparency + @Test(expected = SSLHandshakeException::class) + fun noSCT() { + testConnection("https://no-sct.badssl.com") + } + + // ⬆️Upgrade + @Test + fun hsts() { + testConnection("https://hsts.badssl.com") + } + + @Test + fun upgrade() { + testConnection("https://upgrade.badssl.com") + } + + @Test + fun preloadedHsts() { + testConnection("https://preloaded-hsts.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun subdomainPreloadedHsts() { + testConnection("https://subdomain.preloaded-hsts.badssl.com") + } + + // 👀UI + @Test + fun longTitle() { + testConnection("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com") + } + + @Test + fun longTitle2() { + testConnection("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com") + } + + // ❌Known Bad + + @Test(expected = SSLHandshakeException::class) + fun superfish() { + testConnection("https://superfish.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun eDellRoot() { + testConnection("https://edellroot.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun dsdTestProvider() { + testConnection("https://dsdtestprovider.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun preactCLI() { + testConnection("https://preact-cli.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun webpackDevServer() { + testConnection("https://webpack-dev-server.badssl.com") + } + + // Chrome Tests + @Test(expected = SSLHandshakeException::class) + fun captivePortal() { + testConnection("https://captive-portal.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun mitmSoftware() { + testConnection("https://mitm-software.badssl.com") + } + + // ☠️Defunct + @Test(expected = SSLHandshakeException::class) + fun sha1_2016() { + testConnection("https://sha1-2016.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun sha1_2017() { + testConnection("https://sha1-2017.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun sha1_intermediate() { + testConnection("https://sha1-intermediate.badssl.com") + } + + @Test(expected = SSLHandshakeException::class) + fun invalidExpectedSCT() { + testConnection("https://invalid-expected-sct.badssl.com") + } + + abstract fun testConnection(url: String) +} diff --git a/src/androidTest/java/com/owncloud/android/NextcloudClientIT.kt b/src/androidTest/java/com/owncloud/android/NextcloudClientIT.kt new file mode 100644 index 0000000000..c9f107183f --- /dev/null +++ b/src/androidTest/java/com/owncloud/android/NextcloudClientIT.kt @@ -0,0 +1,41 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2021 Tobias Kaminsky + * Copyright (C) 2021 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android + +import android.net.Uri +import com.nextcloud.operations.GetMethod +import com.owncloud.android.lib.common.OwnCloudClientFactory + +class NextcloudClientIT : ClientIT() { + private val client = OwnCloudClientFactory.createNextcloudClient( + Uri.EMPTY, + "", + "", + context, + true + ) + + override fun testConnection(url: String) { + client.execute(GetMethod(url, false)) + } +} diff --git a/src/androidTest/java/com/owncloud/android/OwnCloudClientIT.kt b/src/androidTest/java/com/owncloud/android/OwnCloudClientIT.kt new file mode 100644 index 0000000000..791b068a56 --- /dev/null +++ b/src/androidTest/java/com/owncloud/android/OwnCloudClientIT.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2021 Tobias Kaminsky + * Copyright (C) 2021 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android + +import android.net.Uri +import com.owncloud.android.lib.common.OwnCloudClientFactory +import org.apache.commons.httpclient.methods.GetMethod + +class OwnCloudClientIT : ClientIT() { + private val client = OwnCloudClientFactory.createOwnCloudClient(Uri.EMPTY, context, true) + + override fun testConnection(url: String) { + client.executeMethod(GetMethod(url)) + } +} diff --git a/src/main/java/com/nextcloud/common/OkHttpMethodBase.kt b/src/main/java/com/nextcloud/common/OkHttpMethodBase.kt index 9ce6a8ae89..2cf4e62792 100644 --- a/src/main/java/com/nextcloud/common/OkHttpMethodBase.kt +++ b/src/main/java/com/nextcloud/common/OkHttpMethodBase.kt @@ -35,6 +35,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Request import okhttp3.Response import java.io.IOException +import javax.net.ssl.SSLHandshakeException /** * Common base class for all new OkHttpMethods @@ -147,7 +148,11 @@ abstract class OkHttpMethodBase( try { response = nextcloudClient.client.newCall(request).execute() } catch (ex: IOException) { - return UNKNOWN_STATUS_CODE + if (ex is SSLHandshakeException) { + throw ex + } else { + return UNKNOWN_STATUS_CODE + } } return if (nextcloudClient.followRedirects) {