Skip to content

Commit 171a075

Browse files
committed
Merge branch 'release/5.72.0' into main
2 parents 2b51d6f + db09df4 commit 171a075

File tree

55 files changed

+1844
-29
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1844
-29
lines changed

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ dependencies {
149149
implementation "androidx.swiperefreshlayout:swiperefreshlayout:$swipeRefreshLayout"
150150
implementation "androidx.webkit:webkit:$webkit"
151151
implementation "com.squareup.okhttp3:okhttp:$okHttp"
152+
implementation "com.squareup.okhttp3:okhttp-tls:$okHttp"
152153
implementation "com.squareup.retrofit2:retrofit:$retrofit"
153154
implementation "com.squareup.retrofit2:converter-moshi:$retrofit"
154155
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit"
@@ -201,6 +202,7 @@ dependencies {
201202

202203
// Glide
203204
implementation "com.github.bumptech.glide:glide:$glide"
205+
implementation "com.github.bumptech.glide:okhttp3-integration:$glide"
204206
kapt "com.github.bumptech.glide:compiler:$glide"
205207

206208
// Lottie

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,13 +1069,15 @@ class BrowserTabViewModelTest {
10691069
loadUrl("http://example.com")
10701070
testee.onDesktopSiteModeToggled(true)
10711071
verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture())
1072+
verify(mockPixel).fire(Pixel.PixelName.MENU_ACTION_DESKTOP_SITE_ENABLE_PRESSED)
10721073
assertTrue(browserViewState().isDesktopBrowsingMode)
10731074
}
10741075

10751076
@Test
10761077
fun whenUserSelectsMobileSiteThenMobileModeStateUpdated() {
10771078
loadUrl("http://example.com")
10781079
testee.onDesktopSiteModeToggled(false)
1080+
verify(mockPixel).fire(Pixel.PixelName.MENU_ACTION_DESKTOP_SITE_DISABLE_PRESSED)
10791081
assertFalse(browserViewState().isDesktopBrowsingMode)
10801082
}
10811083

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserWebViewClientTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.test.annotation.UiThreadTest
2323
import androidx.test.filters.SdkSuppress
2424
import androidx.test.platform.app.InstrumentationRegistry
2525
import com.duckduckgo.app.CoroutineTestRule
26+
import com.duckduckgo.app.browser.certificates.rootstore.TrustedCertificateStore
2627
import com.duckduckgo.app.browser.logindetection.DOMLoginDetector
2728
import com.duckduckgo.app.browser.logindetection.WebNavigationEvent
2829
import com.duckduckgo.app.browser.model.BasicAuthenticationRequest
@@ -55,12 +56,14 @@ class BrowserWebViewClientTest {
5556
private val uncaughtExceptionRepository: UncaughtExceptionRepository = mock()
5657
private val dosDetector: DosDetector = DosDetector()
5758
private val globalPrivacyControlInjector: GlobalPrivacyControlInjector = mock()
59+
private val trustedCertificateStore: TrustedCertificateStore = mock()
5860

5961
@UiThreadTest
6062
@Before
6163
fun setup() {
6264
webView = TestWebView(InstrumentationRegistry.getInstrumentation().targetContext)
6365
testee = BrowserWebViewClient(
66+
trustedCertificateStore,
6467
requestRewriter,
6568
specialUrlDetector,
6669
requestInterceptor,
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2020 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.certificates
18+
19+
import androidx.test.platform.app.InstrumentationRegistry
20+
import com.duckduckgo.app.browser.certificates.rootstore.*
21+
import org.junit.Assert.assertNull
22+
import org.junit.Assert.assertTrue
23+
import org.junit.Test
24+
25+
class LetsEncryptCertificateProviderTest {
26+
27+
private val certificateProvider =
28+
LetsEncryptCertificateProviderImpl(
29+
setOf(
30+
IsrgRootX1(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext),
31+
IsrgRootX2(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext),
32+
LetsEncryptAuthorityX3(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext),
33+
LetsEncryptR3(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext),
34+
LetsEncryptE1(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
35+
)
36+
)
37+
38+
@Test
39+
fun whenFindByCnameAndCnameHitThenReturnCertificate() {
40+
val certificate = certificateProvider.findByCname("R3")
41+
assertTrue(certificate is LetsEncryptR3)
42+
}
43+
44+
@Test
45+
fun whenFindByCnameAndCnameMissThenReturnCertificate() {
46+
val certificate = certificateProvider.findByCname("no_match")
47+
assertNull(certificate)
48+
}
49+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2020 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.certificates.rootstore
18+
19+
import android.net.http.SslCertificate
20+
import com.duckduckgo.app.browser.certificates.CertificateTypes.Companion.X509
21+
import okio.ByteString
22+
import okio.ByteString.Companion.decodeBase64
23+
import java.security.cert.CertificateFactory
24+
import java.security.cert.X509Certificate
25+
26+
fun String.parsePemCertificate(): ByteString? {
27+
return this
28+
.replace("-----BEGIN CERTIFICATE-----", "")
29+
.replace("-----END CERTIFICATE-----", "")
30+
.decodeBase64()
31+
}
32+
33+
fun ByteString.toX509Certificate(): X509Certificate {
34+
val certificateFactory = CertificateFactory.getInstance(X509)
35+
return certificateFactory.generateCertificate(this.toByteArray().inputStream()) as X509Certificate
36+
}
37+
38+
fun ByteString.toSslCertificate(): SslCertificate {
39+
return SslCertificate(this.toX509Certificate())
40+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2020 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.certificates.rootstore
18+
19+
import androidx.test.platform.app.InstrumentationRegistry
20+
import com.duckduckgo.app.browser.certificates.CertificateType
21+
import okio.ByteString.Companion.toByteString
22+
import org.junit.Assert.assertEquals
23+
import org.junit.Test
24+
25+
class IsrgRootX1Test {
26+
private val expectedCertificate = """
27+
-----BEGIN CERTIFICATE-----
28+
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
29+
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
30+
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
31+
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
32+
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
33+
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
34+
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
35+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
36+
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
37+
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
38+
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
39+
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
40+
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
41+
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
42+
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
43+
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
44+
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
45+
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
46+
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
47+
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
48+
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
49+
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
50+
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
51+
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
52+
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
53+
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
54+
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
55+
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
56+
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
57+
-----END CERTIFICATE-----
58+
""".trimIndent().parsePemCertificate()
59+
60+
private val certificatePlugin = IsrgRootX1(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
61+
62+
@Test
63+
fun whenIsrgRootX1TypeThenReturnRoot() {
64+
assertEquals(certificatePlugin.type(), CertificateType.Root)
65+
}
66+
67+
@Test
68+
fun whenGetCertificateThenCertificateIsCorrect() {
69+
assertEquals(certificatePlugin.certificate().encoded.toByteString(), expectedCertificate)
70+
}
71+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2020 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.certificates.rootstore
18+
19+
import androidx.test.platform.app.InstrumentationRegistry
20+
import com.duckduckgo.app.browser.certificates.CertificateType
21+
import okio.ByteString.Companion.toByteString
22+
import org.junit.Assert.assertEquals
23+
import org.junit.Test
24+
25+
class IsrgRootX2Test {
26+
private val expectedCertificate = """
27+
-----BEGIN CERTIFICATE-----
28+
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
29+
CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
30+
R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
31+
MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
32+
ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
33+
EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
34+
+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
35+
ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
36+
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
37+
zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
38+
tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
39+
/q4AaOeMSQ+2b1tbFfLn
40+
-----END CERTIFICATE-----
41+
""".trimIndent().parsePemCertificate()
42+
43+
private val certificatePlugin = IsrgRootX2(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
44+
45+
@Test
46+
fun whenIsrgRootX1TypeThenReturnRoot() {
47+
assertEquals(certificatePlugin.type(), CertificateType.Root)
48+
}
49+
50+
@Test
51+
fun whenGetCertificateThenCertificateIsCorrect() {
52+
assertEquals(certificatePlugin.certificate().encoded.toByteString(), expectedCertificate)
53+
}
54+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2020 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.certificates.rootstore
18+
19+
import androidx.test.platform.app.InstrumentationRegistry
20+
import com.duckduckgo.app.browser.certificates.CertificateType
21+
import okio.ByteString.Companion.toByteString
22+
import org.junit.Assert.assertEquals
23+
import org.junit.Test
24+
25+
class LetsEncryptAuthorityX3Test {
26+
private val expectedCertificate = """
27+
-----BEGIN CERTIFICATE-----
28+
MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw
29+
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
30+
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1
31+
WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
32+
RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi
33+
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX
34+
NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf
35+
89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl
36+
Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc
37+
Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz
38+
uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB
39+
AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
40+
BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
41+
FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo
42+
SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
43+
LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
44+
BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
45+
AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
46+
VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
47+
ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx
48+
A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM
49+
UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2
50+
DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1
51+
eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu
52+
OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw
53+
p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY
54+
2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0
55+
ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR
56+
PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b
57+
rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt
58+
-----END CERTIFICATE-----
59+
""".trimIndent().parsePemCertificate()
60+
61+
private val certificatePlugin = LetsEncryptAuthorityX3(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
62+
63+
@Test
64+
fun whenGetTypeThenReturnIntermediate() {
65+
assertEquals(certificatePlugin.type(), CertificateType.Intermediate)
66+
}
67+
68+
@Test
69+
fun whenGetCertificateThenCertificateIsCorrect() {
70+
assertEquals(certificatePlugin.certificate().encoded.toByteString(), expectedCertificate)
71+
}
72+
}

0 commit comments

Comments
 (0)