Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.

Commit c5408ea

Browse files
authored
Update consumer-proguard.pro to fix web views (appmattus#131)
Restores WebView functionality on API 25. Also added a WebView to the sample app to aid with testing.
1 parent 6c11d74 commit c5408ea

File tree

12 files changed

+273
-37
lines changed

12 files changed

+273
-37
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
uses: github/codeql-action/upload-sarif@v3
4949
with:
5050
sarif_file: snyk.sarif
51-
- uses: actions/upload-artifact@v3
51+
- uses: actions/upload-artifact@v4
5252
with:
5353
name: snyk.sarif
5454
path: snyk.sarif
@@ -72,7 +72,7 @@ jobs:
7272

7373
- name: Upload reports
7474
if: failure()
75-
uses: actions/upload-artifact@master
75+
uses: actions/upload-artifact@v4
7676
with:
7777
name: test-results
7878
path: '**/build/reports/**'

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[![CI status](https://github.com/appmattus/certificatetransparency/actions/workflows/main.yml/badge.svg)](https://github.com/appmattus/certificatetransparency/actions)
44
[![codecov](https://codecov.io/gh/appmattus/certificatetransparency/branch/main/graph/badge.svg)](https://codecov.io/gh/appmattus/certificatetransparency)
55
[![Maven Central](https://img.shields.io/maven-central/v/com.appmattus.certificatetransparency/certificatetransparency)](https://central.sonatype.com/search?q=com.appmattus.certificatetransparency)
6+
[![Snyk](https://snyk.io/test/github/appmattus/certificatetransparency/badge.svg)](https://security.snyk.io/package/maven/com.appmattus.certificatetransparency%3Acertificatetransparency)
67

78
To protect our apps from man-in-the-middle attacks one of the first things that
89
usually springs to mind is certificate pinning. However, the issues of
@@ -24,6 +25,9 @@ We are open about the security of our library and provide a threat model in the
2425
[OWASP Threat Dragon](https://threatdragon.org). If you feel there is something
2526
we have missed please reach out so we can keep this up to date.
2627

28+
The source code and dependencies are continuously scanned with
29+
[Snyk](https://snyk.io).
30+
2731
## Getting started
2832

2933
[![Maven Central](https://img.shields.io/maven-central/v/com.appmattus.certificatetransparency/certificatetransparency)](https://central.sonatype.com/search?q=com.appmattus.certificatetransparency)

certificatetransparency-android/consumer-proguard-rules.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@
8888
-keep class com.appmattus.certificatetransparency.chaincleaner.* { *; }
8989

9090
# CertificateTransparencyTrustManager contains methods called through reflection
91-
-keep class com.appmattus.certificatetransparency.internal.verifier.CertificateTransparencyTrustManager { *; }
92-
91+
-keep class com.appmattus.certificatetransparency.internal.verifier.CertificateTransparencyTrustManagerBasic { *; }
92+
-keep class com.appmattus.certificatetransparency.internal.verifier.CertificateTransparencyTrustManagerExtended { *; }
9393

9494
# Specifically for ProGuard (not needed for R8)
9595
-dontwarn module-info

sampleapp/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<application
88
android:name=".SampleApplication"
99
android:allowBackup="false"
10+
android:dataExtractionRules="@xml/data_extraction_rules"
11+
android:fullBackupContent="false"
1012
android:icon="@mipmap/ic_launcher"
1113
android:label="@string/app_name"
1214
android:networkSecurityConfig="@xml/network_security_config"

sampleapp/src/main/java/com/appmattus/certificatetransparency/sampleapp/ExampleScreen.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021-2023 Appmattus Limited
2+
* Copyright 2021-2025 Appmattus Limited
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -216,7 +216,7 @@ private fun LazyListScope.sampleCodeSection(state: State?) {
216216
}
217217

218218
@Composable
219-
fun IncludeHostDialog(showIncludeHostDialog: MutableState<Boolean>, onInclude: (String) -> Unit) {
219+
private fun IncludeHostDialog(showIncludeHostDialog: MutableState<Boolean>, onInclude: (String) -> Unit) {
220220
if (showIncludeHostDialog.value) {
221221
val text = remember { mutableStateOf("") }
222222

@@ -251,7 +251,7 @@ fun IncludeHostDialog(showIncludeHostDialog: MutableState<Boolean>, onInclude: (
251251
}
252252

253253
@Composable
254-
fun ExcludeHostDialog(showExcludeHostDialog: MutableState<Boolean>, onExclude: (String) -> Unit) {
254+
private fun ExcludeHostDialog(showExcludeHostDialog: MutableState<Boolean>, onExclude: (String) -> Unit) {
255255
if (showExcludeHostDialog.value) {
256256
val text = remember { mutableStateOf("") }
257257

@@ -286,7 +286,7 @@ fun ExcludeHostDialog(showExcludeHostDialog: MutableState<Boolean>, onExclude: (
286286
}
287287

288288
@Composable
289-
fun TestConnectionDialog(showConnectionDialog: MutableState<Boolean>, onConnect: (String) -> Unit) {
289+
private fun TestConnectionDialog(showConnectionDialog: MutableState<Boolean>, onConnect: (String) -> Unit) {
290290
if (showConnectionDialog.value) {
291291
val text = remember { mutableStateOf("") }
292292

sampleapp/src/main/java/com/appmattus/certificatetransparency/sampleapp/MainActivity.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Appmattus Limited
2+
* Copyright 2021-2025 Appmattus Limited
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import com.appmattus.certificatetransparency.sampleapp.examples.okhttp.OkHttpJav
2929
import com.appmattus.certificatetransparency.sampleapp.examples.okhttp.OkHttpKotlinExampleViewModel
3030
import com.appmattus.certificatetransparency.sampleapp.examples.trustmanager.TrustManagerJavaExampleViewModel
3131
import com.appmattus.certificatetransparency.sampleapp.examples.trustmanager.TrustManagerKotlinExampleViewModel
32+
import com.appmattus.certificatetransparency.sampleapp.examples.trustmanager.WebViewExampleViewModel
3233
import com.appmattus.certificatetransparency.sampleapp.examples.volley.VolleyJavaExampleViewModel
3334
import com.appmattus.certificatetransparency.sampleapp.examples.volley.VolleyKotlinExampleViewModel
3435

@@ -66,6 +67,9 @@ class MainActivity : AppCompatActivity() {
6667
composable("trustmanager/java") {
6768
ExampleScreen(viewModel = viewModel<TrustManagerJavaExampleViewModel>())
6869
}
70+
composable("webview/kotlin") {
71+
WebViewScreen(viewModel = viewModel<WebViewExampleViewModel>())
72+
}
6973
}
7074
}
7175
}

sampleapp/src/main/java/com/appmattus/certificatetransparency/sampleapp/MainScreen.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021-2023 Appmattus Limited
2+
* Copyright 2021-2025 Appmattus Limited
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,8 +17,11 @@
1717
package com.appmattus.certificatetransparency.sampleapp
1818

1919
import android.net.Uri
20+
import androidx.compose.foundation.layout.WindowInsets
2021
import androidx.compose.foundation.layout.fillMaxHeight
2122
import androidx.compose.foundation.layout.padding
23+
import androidx.compose.foundation.layout.safeContent
24+
import androidx.compose.foundation.layout.windowInsetsPadding
2225
import androidx.compose.foundation.lazy.LazyColumn
2326
import androidx.compose.material.Scaffold
2427
import androidx.compose.material.rememberScaffoldState
@@ -42,6 +45,7 @@ fun MainScreen(navController: NavController) {
4245
LazyColumn(
4346
modifier = Modifier
4447
.padding(padding)
48+
.windowInsetsPadding(WindowInsets.safeContent)
4549
.padding(vertical = 8.dp)
4650
.fillMaxHeight()
4751
) {
@@ -92,6 +96,16 @@ fun MainScreen(navController: NavController) {
9296
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
9397
)
9498
}
99+
item {
100+
ExampleCardItem(
101+
scaffoldState = scaffoldState,
102+
title = stringResource(R.string.webview),
103+
moreInfoUri = null,
104+
onKotlinClick = { navController.navigate("webview/kotlin") },
105+
onJavaClick = null,
106+
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
107+
)
108+
}
95109

96110
item {
97111
AppmattusLogo(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp))
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2025 Appmattus Limited
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.appmattus.certificatetransparency.sampleapp
18+
19+
import android.view.ViewGroup
20+
import android.webkit.WebView
21+
import androidx.compose.foundation.layout.Spacer
22+
import androidx.compose.foundation.layout.WindowInsets
23+
import androidx.compose.foundation.layout.fillMaxHeight
24+
import androidx.compose.foundation.layout.height
25+
import androidx.compose.foundation.layout.padding
26+
import androidx.compose.foundation.layout.safeContent
27+
import androidx.compose.foundation.layout.windowInsetsPadding
28+
import androidx.compose.foundation.lazy.LazyColumn
29+
import androidx.compose.material.Scaffold
30+
import androidx.compose.material.Snackbar
31+
import androidx.compose.material.SnackbarHost
32+
import androidx.compose.material.SnackbarResult
33+
import androidx.compose.material.Text
34+
import androidx.compose.material.rememberScaffoldState
35+
import androidx.compose.runtime.Composable
36+
import androidx.compose.runtime.rememberCoroutineScope
37+
import androidx.compose.ui.Modifier
38+
import androidx.compose.ui.res.colorResource
39+
import androidx.compose.ui.unit.dp
40+
import androidx.compose.ui.viewinterop.AndroidView
41+
import com.appmattus.certificatetransparency.sampleapp.examples.State
42+
import com.appmattus.certificatetransparency.sampleapp.examples.trustmanager.WebViewExampleViewModel
43+
import com.appmattus.certificatetransparency.sampleapp.item.text.HeaderTextItem
44+
import kotlinx.coroutines.launch
45+
import org.orbitmvi.orbit.compose.collectAsState
46+
47+
@Composable
48+
@Suppress("LongMethod")
49+
fun WebViewScreen(viewModel: WebViewExampleViewModel) {
50+
val state = viewModel.collectAsState().value
51+
52+
val scaffoldState = rememberScaffoldState()
53+
val scope = rememberCoroutineScope()
54+
55+
state.message?.let {
56+
scope.launch {
57+
val color = if (it is State.Message.Success) R.color.colorSuccess else R.color.colorFailure
58+
59+
val result = scaffoldState.snackbarHostState.showSnackbar(it.text, color.toString())
60+
if (result == SnackbarResult.Dismissed) {
61+
viewModel.dismissMessage()
62+
}
63+
}
64+
}
65+
66+
Scaffold(
67+
scaffoldState = scaffoldState,
68+
snackbarHost = { snackbarHostState ->
69+
SnackbarHost(snackbarHostState) {
70+
Snackbar(backgroundColor = colorResource(it.actionLabel!!.toInt()), modifier = Modifier.padding(8.dp)) {
71+
Text(
72+
it.message
73+
)
74+
}
75+
}
76+
}
77+
) { padding ->
78+
LazyColumn(
79+
modifier = Modifier
80+
.padding(padding)
81+
.windowInsetsPadding(WindowInsets.safeContent)
82+
.fillMaxHeight()
83+
) {
84+
item { Spacer(modifier = Modifier.height(8.dp)) }
85+
86+
item {
87+
HeaderTextItem(
88+
title = viewModel.title,
89+
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
90+
)
91+
}
92+
93+
item {
94+
AndroidView(factory = {
95+
WebView(it).apply {
96+
layoutParams = ViewGroup.LayoutParams(
97+
ViewGroup.LayoutParams.MATCH_PARENT,
98+
ViewGroup.LayoutParams.MATCH_PARENT
99+
)
100+
}
101+
}, update = {
102+
it.loadUrl("https://example.com/")
103+
})
104+
}
105+
}
106+
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 Appmattus Limited
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.appmattus.certificatetransparency.sampleapp.examples.trustmanager
18+
19+
import android.app.Application
20+
import android.util.Log
21+
import androidx.lifecycle.AndroidViewModel
22+
import androidx.lifecycle.viewModelScope
23+
import com.appmattus.certificatetransparency.CTLogger
24+
import com.appmattus.certificatetransparency.VerificationResult
25+
import com.appmattus.certificatetransparency.cache.AndroidDiskCache
26+
import com.appmattus.certificatetransparency.installCertificateTransparencyProvider
27+
import com.appmattus.certificatetransparency.removeCertificateTransparencyProvider
28+
import com.appmattus.certificatetransparency.sampleapp.R
29+
import com.appmattus.certificatetransparency.sampleapp.examples.State
30+
import org.orbitmvi.orbit.Container
31+
import org.orbitmvi.orbit.ContainerHost
32+
import org.orbitmvi.orbit.container
33+
34+
class WebViewExampleViewModel(application: Application) : AndroidViewModel(application), ContainerHost<State, Unit> {
35+
36+
val title: String
37+
get() = getApplication<Application>().getString(R.string.webview_example)
38+
39+
override val container: Container<State, Unit> = viewModelScope.container(State()) {
40+
intent {
41+
installCertificateTransparencyProvider {
42+
failOnError = true
43+
logger = defaultLogger
44+
diskCache = AndroidDiskCache(getApplication())
45+
}
46+
}
47+
}
48+
49+
override fun onCleared() {
50+
super.onCleared()
51+
removeCertificateTransparencyProvider()
52+
}
53+
54+
fun dismissMessage() = intent {
55+
reduce {
56+
state.copy(message = null)
57+
}
58+
}
59+
60+
fun setFailOnError(failOnError: Boolean) = intent {
61+
reduce {
62+
state.copy(failOnError = failOnError)
63+
}
64+
}
65+
66+
private val defaultLogger = object : CTLogger {
67+
override fun log(host: String, result: VerificationResult) {
68+
intent {
69+
val message = when (result) {
70+
is VerificationResult.Success -> State.Message.Success(result.toString())
71+
is VerificationResult.Failure -> State.Message.Failure(result.toString())
72+
}
73+
74+
Log.d("CT", "$host -> $result")
75+
76+
reduce {
77+
state.copy(message = message)
78+
}
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)