react-native-security-suite 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -2
  3. package/android/build.gradle +45 -28
  4. package/android/src/main/AndroidManifest.xml +47 -0
  5. package/android/src/main/{AndroidManifestDeprecated.xml → AndroidManifestNew.xml} +1 -2
  6. package/android/src/main/java/com/securitysuite/NetworkLogger.java +25 -0
  7. package/android/src/main/java/com/securitysuite/Sslpinning.java +38 -54
  8. package/android/src/main/java/com/securitysuite/api/BodyDecoder.kt +35 -0
  9. package/android/src/main/java/com/securitysuite/api/Chucker.kt +108 -0
  10. package/android/src/main/java/com/securitysuite/api/ChuckerCollector.kt +129 -0
  11. package/android/src/main/java/com/securitysuite/api/ChuckerInterceptor.kt +280 -0
  12. package/android/src/main/java/com/securitysuite/api/ExportFormat.kt +12 -0
  13. package/android/src/main/java/com/securitysuite/api/RetentionManager.kt +109 -0
  14. package/android/src/main/java/com/securitysuite/internal/data/entity/HttpHeader.kt +8 -0
  15. package/android/src/main/java/com/securitysuite/internal/data/entity/HttpTransaction.kt +344 -0
  16. package/android/src/main/java/com/securitysuite/internal/data/entity/HttpTransactionTuple.kt +62 -0
  17. package/android/src/main/java/com/securitysuite/internal/data/har/Har.kt +13 -0
  18. package/android/src/main/java/com/securitysuite/internal/data/har/Log.kt +24 -0
  19. package/android/src/main/java/com/securitysuite/internal/data/har/log/Browser.kt +11 -0
  20. package/android/src/main/java/com/securitysuite/internal/data/har/log/Creator.kt +11 -0
  21. package/android/src/main/java/com/securitysuite/internal/data/har/log/Entry.kt +49 -0
  22. package/android/src/main/java/com/securitysuite/internal/data/har/log/Page.kt +14 -0
  23. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Cache.kt +12 -0
  24. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Header.kt +17 -0
  25. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Request.kt +35 -0
  26. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Response.kt +33 -0
  27. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Timings.kt +25 -0
  28. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/cache/SecondaryRequest.kt +13 -0
  29. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/request/PostData.kt +20 -0
  30. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/request/QueryString.kt +23 -0
  31. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/request/postdata/Params.kt +13 -0
  32. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/response/Content.kt +30 -0
  33. package/android/src/main/java/com/securitysuite/internal/data/har/log/page/PageTimings.kt +11 -0
  34. package/android/src/main/java/com/securitysuite/internal/data/model/DialogData.kt +8 -0
  35. package/android/src/main/java/com/securitysuite/internal/data/repository/HttpTransactionDatabaseRepository.kt +60 -0
  36. package/android/src/main/java/com/securitysuite/internal/data/repository/HttpTransactionRepository.kt +33 -0
  37. package/android/src/main/java/com/securitysuite/internal/data/repository/RepositoryProvider.kt +38 -0
  38. package/android/src/main/java/com/securitysuite/internal/data/room/ChuckerDatabase.kt +22 -0
  39. package/android/src/main/java/com/securitysuite/internal/data/room/HttpTransactionDao.kt +53 -0
  40. package/android/src/main/java/com/securitysuite/internal/support/BitmapUtils.kt +45 -0
  41. package/android/src/main/java/com/securitysuite/internal/support/CacheDirectoryProvider.kt +11 -0
  42. package/android/src/main/java/com/securitysuite/internal/support/ChessboardDrawable.kt +76 -0
  43. package/android/src/main/java/com/securitysuite/internal/support/ChuckerFileProvider.kt +9 -0
  44. package/android/src/main/java/com/securitysuite/internal/support/ClearDatabaseJobIntentServiceReceiver.kt +14 -0
  45. package/android/src/main/java/com/securitysuite/internal/support/ClearDatabaseService.kt +32 -0
  46. package/android/src/main/java/com/securitysuite/internal/support/ContextExt.kt +22 -0
  47. package/android/src/main/java/com/securitysuite/internal/support/DepletingSource.kt +37 -0
  48. package/android/src/main/java/com/securitysuite/internal/support/FileFactory.kt +30 -0
  49. package/android/src/main/java/com/securitysuite/internal/support/FileSaver.kt +41 -0
  50. package/android/src/main/java/com/securitysuite/internal/support/FormatUtils.kt +117 -0
  51. package/android/src/main/java/com/securitysuite/internal/support/FormattedUrl.kt +76 -0
  52. package/android/src/main/java/com/securitysuite/internal/support/HarUtils.kt +28 -0
  53. package/android/src/main/java/com/securitysuite/internal/support/HttpUrlUtils.kt +11 -0
  54. package/android/src/main/java/com/securitysuite/internal/support/JsonConverter.kt +19 -0
  55. package/android/src/main/java/com/securitysuite/internal/support/LimitingSource.kt +22 -0
  56. package/android/src/main/java/com/securitysuite/internal/support/LiveDataUtils.kt +68 -0
  57. package/android/src/main/java/com/securitysuite/internal/support/Logger.kt +43 -0
  58. package/android/src/main/java/com/securitysuite/internal/support/NotificationHelper.kt +149 -0
  59. package/android/src/main/java/com/securitysuite/internal/support/OkHttpUtils.kt +86 -0
  60. package/android/src/main/java/com/securitysuite/internal/support/OkioUtils.kt +34 -0
  61. package/android/src/main/java/com/securitysuite/internal/support/PlainTextDecoder.kt +30 -0
  62. package/android/src/main/java/com/securitysuite/internal/support/ReportingSink.kt +114 -0
  63. package/android/src/main/java/com/securitysuite/internal/support/RequestProcessor.kt +102 -0
  64. package/android/src/main/java/com/securitysuite/internal/support/ResponseProcessor.kt +170 -0
  65. package/android/src/main/java/com/securitysuite/internal/support/SearchHighlightUtil.kt +80 -0
  66. package/android/src/main/java/com/securitysuite/internal/support/Sharable.kt +86 -0
  67. package/android/src/main/java/com/securitysuite/internal/support/SpanTextUtil.kt +202 -0
  68. package/android/src/main/java/com/securitysuite/internal/support/TeeSource.kt +68 -0
  69. package/android/src/main/java/com/securitysuite/internal/support/TransactionCurlCommandSharable.kt +46 -0
  70. package/android/src/main/java/com/securitysuite/internal/support/TransactionDetailsHarSharable.kt +11 -0
  71. package/android/src/main/java/com/securitysuite/internal/support/TransactionDetailsSharable.kt +73 -0
  72. package/android/src/main/java/com/securitysuite/internal/support/TransactionDiffCallback.kt +26 -0
  73. package/android/src/main/java/com/securitysuite/internal/support/TransactionListDetailsSharable.kt +23 -0
  74. package/android/src/main/java/com/securitysuite/internal/ui/BaseChuckerActivity.kt +35 -0
  75. package/android/src/main/java/com/securitysuite/internal/ui/MainActivity.kt +375 -0
  76. package/android/src/main/java/com/securitysuite/internal/ui/MainViewModel.kt +47 -0
  77. package/android/src/main/java/com/securitysuite/internal/ui/transaction/PayloadType.kt +6 -0
  78. package/android/src/main/java/com/securitysuite/internal/ui/transaction/ProtocolResources.kt +14 -0
  79. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionActivity.kt +186 -0
  80. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionAdapter.kt +139 -0
  81. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionOverviewFragment.kt +100 -0
  82. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionPagerAdapter.kt +29 -0
  83. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionPayloadAdapter.kt +269 -0
  84. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionPayloadFragment.kt +529 -0
  85. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionViewModel.kt +69 -0
  86. package/android/src/main/res/A.java +12 -0
  87. package/android/src/main/res/color/chucker_fab_background_colour.xml +5 -0
  88. package/android/src/main/res/drawable/chucker_empty_payload.xml +10 -0
  89. package/android/src/main/res/drawable/chucker_ic_arrow_down.xml +10 -0
  90. package/android/src/main/res/drawable/chucker_ic_copy.xml +12 -0
  91. package/android/src/main/res/drawable/chucker_ic_decoded_url_white.xml +10 -0
  92. package/android/src/main/res/drawable/chucker_ic_delete_white.xml +9 -0
  93. package/android/src/main/res/drawable/chucker_ic_encoded_url_white.xml +11 -0
  94. package/android/src/main/res/drawable/chucker_ic_graphql.xml +27 -0
  95. package/android/src/main/res/drawable/chucker_ic_http.xml +10 -0
  96. package/android/src/main/res/drawable/chucker_ic_https.xml +9 -0
  97. package/android/src/main/res/drawable/chucker_ic_launcher_foreground.xml +14 -0
  98. package/android/src/main/res/drawable/chucker_ic_save_white.xml +9 -0
  99. package/android/src/main/res/drawable/chucker_ic_search_white.xml +9 -0
  100. package/android/src/main/res/drawable/chucker_ic_share_white.xml +9 -0
  101. package/android/src/main/res/drawable/chucker_ic_transaction_notification.xml +9 -0
  102. package/android/src/main/res/layout/activity_main.xml +83 -0
  103. package/android/src/main/res/layout/activity_transaction.xml +48 -0
  104. package/android/src/main/res/layout/fragment_transaction_overview.xml +365 -0
  105. package/android/src/main/res/layout/fragment_transaction_payload.xml +132 -0
  106. package/android/src/main/res/layout/list_item_transaction.xml +122 -0
  107. package/android/src/main/res/layout/transaction_item_body_line.xml +13 -0
  108. package/android/src/main/res/layout/transaction_item_copy.xml +19 -0
  109. package/android/src/main/res/layout/transaction_item_headers.xml +12 -0
  110. package/android/src/main/res/layout/transaction_item_image.xml +16 -0
  111. package/android/src/main/res/menu/chucker_transaction.xml +46 -0
  112. package/android/src/main/res/menu/chucker_transactions_list.xml +41 -0
  113. package/android/src/main/res/mipmap-anydpi-v26/chucker_ic_launcher.xml +5 -0
  114. package/android/src/main/res/mipmap-hdpi/chucker_ic_launcher.png +0 -0
  115. package/android/src/main/res/mipmap-xhdpi/chucker_ic_launcher.png +0 -0
  116. package/android/src/main/res/mipmap-xxhdpi/chucker_ic_launcher.png +0 -0
  117. package/android/src/main/res/mipmap-xxxhdpi/chucker_ic_launcher.png +0 -0
  118. package/android/src/main/res/values/chucker_ic_launcher_background.xml +4 -0
  119. package/android/src/main/res/values/colors.xml +38 -0
  120. package/android/src/main/res/values/dimens.xml +10 -0
  121. package/android/src/main/res/values/public.xml +5 -0
  122. package/android/src/main/res/values/strings.xml +77 -0
  123. package/android/src/main/res/values/styles.xml +44 -0
  124. package/android/src/main/res/values-es/strings.xml +75 -0
  125. package/android/src/main/res/values-night/colors.xml +32 -0
  126. package/android/src/main/res/xml/chucker_provider_paths.xml +4 -0
  127. package/ios/SecuritySuite.swift +0 -2
  128. package/ios/SslPinning.swift +0 -26
  129. package/lib/commonjs/helpers.js +1 -1
  130. package/lib/commonjs/helpers.js.map +1 -1
  131. package/lib/commonjs/index.js +23 -40
  132. package/lib/commonjs/index.js.map +1 -1
  133. package/lib/module/helpers.js +3 -1
  134. package/lib/module/helpers.js.map +1 -1
  135. package/lib/module/index.js +21 -33
  136. package/lib/module/index.js.map +1 -1
  137. package/lib/typescript/commonjs/package.json +1 -0
  138. package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -0
  139. package/lib/typescript/{index.d.ts → commonjs/src/index.d.ts} +1 -6
  140. package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
  141. package/lib/typescript/module/package.json +1 -0
  142. package/lib/typescript/module/src/helpers.d.ts +3 -0
  143. package/lib/typescript/module/src/helpers.d.ts.map +1 -0
  144. package/lib/typescript/module/src/index.d.ts +72 -0
  145. package/lib/typescript/module/src/index.d.ts.map +1 -0
  146. package/package.json +70 -43
  147. package/react-native-security-suite.podspec +23 -15
  148. package/src/helpers.ts +1 -1
  149. package/src/index.tsx +5 -18
  150. package/android/src/main/java/com/securitysuite/AndroidLogger.kt +0 -19
  151. package/android/src/main/java/com/securitysuite/modifier/Base64Decoder.kt +0 -11
  152. package/android/src/main/java/com/securitysuite/modifier/BasicAuthorizationHeaderModifier.kt +0 -16
  153. package/ios/SecuritySuite.xcodeproj/project.pbxproj +0 -293
  154. package/ios/SecuritySuite.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  155. package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  156. package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcuserdata/m.navabifar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  157. package/ios/SecuritySuite.xcodeproj/xcuserdata/m.navabifar.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  158. package/lib/typescript/helpers.d.ts.map +0 -1
  159. package/lib/typescript/index.d.ts.map +0 -1
  160. /package/lib/typescript/{helpers.d.ts → commonjs/src/helpers.d.ts} +0 -0
@@ -0,0 +1,13 @@
1
+ package com.securitysuite.internal.data.har.log.entry.cache
2
+
3
+ import com.google.gson.annotations.SerializedName
4
+
5
+ // https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#beforerequest--afterrequest
6
+ // http://www.softwareishard.com/blog/har-12-spec/#cache
7
+ internal data class SecondaryRequest(
8
+ @SerializedName("expires") val expires: String? = null,
9
+ @SerializedName("lastAccess") val lastAccess: String,
10
+ @SerializedName("eTag") val eTag: String,
11
+ @SerializedName("hitCount") val hitCount: Int,
12
+ @SerializedName("comment") val comment: String? = null,
13
+ )
@@ -0,0 +1,20 @@
1
+ package com.securitysuite.internal.data.har.log.entry.request
2
+
3
+ import com.securitysuite.internal.data.entity.HttpTransaction
4
+ import com.securitysuite.internal.data.har.log.entry.request.postdata.Params
5
+ import com.google.gson.annotations.SerializedName
6
+
7
+ // https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#postdata
8
+ // http://www.softwareishard.com/blog/har-12-spec/#postData
9
+ // text and params fields are mutually exclusive.
10
+ internal data class PostData(
11
+ @SerializedName("mimeType") val mimeType: String,
12
+ @SerializedName("params") val params: Params? = null,
13
+ @SerializedName("text") val text: String? = null,
14
+ @SerializedName("comment") val comment: String? = null,
15
+ ) {
16
+ constructor(transaction: HttpTransaction) : this(
17
+ mimeType = transaction.requestContentType ?: "application/octet-stream",
18
+ text = transaction.requestBody,
19
+ )
20
+ }
@@ -0,0 +1,23 @@
1
+ package com.securitysuite.internal.data.har.log.entry.request
2
+
3
+ import com.google.gson.annotations.SerializedName
4
+ import okhttp3.HttpUrl
5
+
6
+ // https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#querystring
7
+ // http://www.softwareishard.com/blog/har-12-spec/#queryString
8
+ internal data class QueryString(
9
+ @SerializedName("name") val name: String,
10
+ @SerializedName("value") val value: String,
11
+ @SerializedName("comment") val comment: String? = null,
12
+ ) {
13
+ companion object {
14
+ fun fromUrl(url: HttpUrl): List<QueryString> {
15
+ return List(url.querySize) { index ->
16
+ QueryString(
17
+ name = url.queryParameterName(index),
18
+ value = url.queryParameterValue(index).orEmpty(),
19
+ )
20
+ }
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,13 @@
1
+ package com.securitysuite.internal.data.har.log.entry.request.postdata
2
+
3
+ import com.google.gson.annotations.SerializedName
4
+
5
+ // https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#params
6
+ // http://www.softwareishard.com/blog/har-12-spec/#params
7
+ internal data class Params(
8
+ @SerializedName("name") val name: String,
9
+ @SerializedName("value") val value: String? = null,
10
+ @SerializedName("fileName") val fileName: String? = null,
11
+ @SerializedName("contentType") val contentType: String? = null,
12
+ @SerializedName("comment") val comment: String? = null,
13
+ )
@@ -0,0 +1,30 @@
1
+ package com.securitysuite.internal.data.har.log.entry.response
2
+
3
+ import com.securitysuite.internal.data.entity.HttpTransaction
4
+ import com.google.gson.annotations.SerializedName
5
+
6
+ // https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#content
7
+ // http://www.softwareishard.com/blog/har-12-spec/#content
8
+ internal data class Content(
9
+ @SerializedName("size") val size: Long? = null,
10
+ @SerializedName("compression") val compression: Int? = null,
11
+ @SerializedName("mimeType") val mimeType: String,
12
+ @SerializedName("text") val text: String? = null,
13
+ @SerializedName("encoding") val encoding: String? = null,
14
+ @SerializedName("comment") val comment: String? = null,
15
+ ) {
16
+ companion object {
17
+ internal val EMPTY =
18
+ Content(
19
+ size = 0L,
20
+ compression = 0,
21
+ mimeType = "text/plain",
22
+ text = "",
23
+ )
24
+ }
25
+ constructor(transaction: HttpTransaction) : this(
26
+ size = transaction.responsePayloadSize,
27
+ mimeType = transaction.responseContentType ?: "application/octet-stream",
28
+ text = transaction.responseBody,
29
+ )
30
+ }
@@ -0,0 +1,11 @@
1
+ package com.securitysuite.internal.data.har.log.page
2
+
3
+ import com.google.gson.annotations.SerializedName
4
+
5
+ // https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#pagetimings
6
+ // http://www.softwareishard.com/blog/har-12-spec/#pageTimings
7
+ internal data class PageTimings(
8
+ @SerializedName("onContentLoad") val onContentLoad: Long? = null,
9
+ @SerializedName("onLoad") val onLoad: Long? = null,
10
+ @SerializedName("comment") val comment: String? = null,
11
+ )
@@ -0,0 +1,8 @@
1
+ package com.securitysuite.internal.data.model
2
+
3
+ internal data class DialogData(
4
+ val title: String,
5
+ val message: String,
6
+ val positiveButtonText: String?,
7
+ val negativeButtonText: String?,
8
+ )
@@ -0,0 +1,60 @@
1
+ package com.securitysuite.internal.data.repository
2
+
3
+ import androidx.lifecycle.LiveData
4
+ import com.securitysuite.internal.data.entity.HttpTransaction
5
+ import com.securitysuite.internal.data.entity.HttpTransactionTuple
6
+ import com.securitysuite.internal.data.room.ChuckerDatabase
7
+ import com.securitysuite.internal.support.distinctUntilChanged
8
+
9
+ internal class HttpTransactionDatabaseRepository(private val database: ChuckerDatabase) : HttpTransactionRepository {
10
+ private val transactionDao get() = database.transactionDao()
11
+
12
+ override fun getFilteredTransactionTuples(
13
+ code: String,
14
+ path: String,
15
+ ): LiveData<List<HttpTransactionTuple>> {
16
+ val pathQuery = if (path.isNotEmpty()) "%$path%" else "%"
17
+ return transactionDao.getFilteredTuples(
18
+ "$code%",
19
+ pathQuery = pathQuery,
20
+ /*
21
+ * Refer <a href='https://github.com/ChuckerTeam/chucker/issues/847">Issue #847</a> for
22
+ * more context
23
+ */
24
+ graphQlQuery = pathQuery,
25
+ )
26
+ }
27
+
28
+ override fun getTransaction(transactionId: Long): LiveData<HttpTransaction?> {
29
+ return transactionDao.getById(transactionId)
30
+ .distinctUntilChanged { old, new -> old?.hasTheSameContent(new) != false }
31
+ }
32
+
33
+ override fun getSortedTransactionTuples(): LiveData<List<HttpTransactionTuple>> {
34
+ return transactionDao.getSortedTuples()
35
+ }
36
+
37
+ override suspend fun deleteAllTransactions() {
38
+ transactionDao.deleteAll()
39
+ }
40
+
41
+ override suspend fun insertTransaction(transaction: HttpTransaction) {
42
+ val id = transactionDao.insert(transaction)
43
+ transaction.id = id ?: 0
44
+ }
45
+
46
+ override suspend fun updateTransaction(transaction: HttpTransaction): Int {
47
+ return transactionDao.update(transaction)
48
+ }
49
+
50
+ override suspend fun deleteOldTransactions(threshold: Long) {
51
+ transactionDao.deleteBefore(threshold)
52
+ }
53
+
54
+ override suspend fun getAllTransactions(): List<HttpTransaction> = transactionDao.getAll()
55
+
56
+ override fun getTransactionsInTimeRange(minTimestamp: Long?): List<HttpTransaction> {
57
+ val timestamp = minTimestamp ?: 0L
58
+ return transactionDao.getTransactionsInTimeRange(timestamp)
59
+ }
60
+ }
@@ -0,0 +1,33 @@
1
+ package com.securitysuite.internal.data.repository
2
+
3
+ import androidx.lifecycle.LiveData
4
+ import com.securitysuite.internal.data.entity.HttpTransaction
5
+ import com.securitysuite.internal.data.entity.HttpTransactionTuple
6
+
7
+ /**
8
+ * Repository Interface representing all the operations that are needed to let Chucker work
9
+ * with [HttpTransaction] and [HttpTransactionTuple]. Please use [HttpTransactionDatabaseRepository] that
10
+ * uses Room and SqLite to run those operations.
11
+ */
12
+ internal interface HttpTransactionRepository {
13
+ suspend fun insertTransaction(transaction: HttpTransaction)
14
+
15
+ suspend fun updateTransaction(transaction: HttpTransaction): Int
16
+
17
+ suspend fun deleteOldTransactions(threshold: Long)
18
+
19
+ suspend fun deleteAllTransactions()
20
+
21
+ fun getSortedTransactionTuples(): LiveData<List<HttpTransactionTuple>>
22
+
23
+ fun getFilteredTransactionTuples(
24
+ code: String,
25
+ path: String,
26
+ ): LiveData<List<HttpTransactionTuple>>
27
+
28
+ fun getTransaction(transactionId: Long): LiveData<HttpTransaction?>
29
+
30
+ suspend fun getAllTransactions(): List<HttpTransaction>
31
+
32
+ fun getTransactionsInTimeRange(minTimestamp: Long?): List<HttpTransaction>
33
+ }
@@ -0,0 +1,38 @@
1
+ package com.securitysuite.internal.data.repository
2
+
3
+ import android.content.Context
4
+ import androidx.annotation.VisibleForTesting
5
+ import com.securitysuite.internal.data.repository.RepositoryProvider.initialize
6
+ import com.securitysuite.internal.data.room.ChuckerDatabase
7
+
8
+ /**
9
+ * A singleton to hold the [HttpTransactionRepository] instance.
10
+ * Make sure you call [initialize] before accessing the stored instance.
11
+ */
12
+ internal object RepositoryProvider {
13
+ private var transactionRepository: HttpTransactionRepository? = null
14
+
15
+ fun transaction(): HttpTransactionRepository {
16
+ return checkNotNull(transactionRepository) {
17
+ "You can't access the transaction repository if you don't initialize it!"
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Idempotent method. Must be called before accessing the repositories.
23
+ */
24
+ fun initialize(applicationContext: Context) {
25
+ if (transactionRepository == null) {
26
+ val db = ChuckerDatabase.create(applicationContext)
27
+ transactionRepository = HttpTransactionDatabaseRepository(db)
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Cleanup stored singleton objects
33
+ */
34
+ @VisibleForTesting
35
+ fun close() {
36
+ transactionRepository = null
37
+ }
38
+ }
@@ -0,0 +1,22 @@
1
+ package com.securitysuite.internal.data.room
2
+
3
+ import android.content.Context
4
+ import androidx.room.Database
5
+ import androidx.room.Room
6
+ import androidx.room.RoomDatabase
7
+ import com.securitysuite.internal.data.entity.HttpTransaction
8
+
9
+ @Database(entities = [HttpTransaction::class], version = 9, exportSchema = false)
10
+ internal abstract class ChuckerDatabase : RoomDatabase() {
11
+ abstract fun transactionDao(): HttpTransactionDao
12
+
13
+ companion object {
14
+ private const val DB_NAME = "chucker.db"
15
+
16
+ fun create(applicationContext: Context): ChuckerDatabase {
17
+ return Room.databaseBuilder(applicationContext, ChuckerDatabase::class.java, DB_NAME)
18
+ .fallbackToDestructiveMigration()
19
+ .build()
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,53 @@
1
+ package com.securitysuite.internal.data.room
2
+
3
+ import androidx.lifecycle.LiveData
4
+ import androidx.room.Dao
5
+ import androidx.room.Insert
6
+ import androidx.room.OnConflictStrategy
7
+ import androidx.room.Query
8
+ import androidx.room.Update
9
+ import com.securitysuite.internal.data.entity.HttpTransaction
10
+ import com.securitysuite.internal.data.entity.HttpTransactionTuple
11
+
12
+ @Dao
13
+ internal interface HttpTransactionDao {
14
+ @Query(
15
+ "SELECT id, requestDate, tookMs, protocol, method, host, path, scheme, responseCode, " +
16
+ "requestPayloadSize, responsePayloadSize, error, graphQLDetected, graphQlOperationName FROM " +
17
+ "transactions ORDER BY requestDate DESC",
18
+ )
19
+ fun getSortedTuples(): LiveData<List<HttpTransactionTuple>>
20
+
21
+ @Query(
22
+ "SELECT id, requestDate, tookMs, protocol, method, host, path, scheme, responseCode, " +
23
+ "requestPayloadSize, responsePayloadSize, error, graphQLDetected, graphQlOperationName FROM " +
24
+ "transactions WHERE responseCode LIKE :codeQuery AND (path LIKE :pathQuery OR " +
25
+ "graphQlOperationName LIKE :graphQlQuery) ORDER BY requestDate DESC",
26
+ )
27
+ fun getFilteredTuples(
28
+ codeQuery: String,
29
+ pathQuery: String,
30
+ graphQlQuery: String = "",
31
+ ): LiveData<List<HttpTransactionTuple>>
32
+
33
+ @Insert
34
+ suspend fun insert(transaction: HttpTransaction): Long?
35
+
36
+ @Update(onConflict = OnConflictStrategy.REPLACE)
37
+ suspend fun update(transaction: HttpTransaction): Int
38
+
39
+ @Query("DELETE FROM transactions")
40
+ suspend fun deleteAll(): Int
41
+
42
+ @Query("SELECT * FROM transactions WHERE id = :id")
43
+ fun getById(id: Long): LiveData<HttpTransaction?>
44
+
45
+ @Query("DELETE FROM transactions WHERE requestDate <= :threshold")
46
+ suspend fun deleteBefore(threshold: Long): Int
47
+
48
+ @Query("SELECT * FROM transactions")
49
+ suspend fun getAll(): List<HttpTransaction>
50
+
51
+ @Query("SELECT * FROM transactions WHERE requestDate >= :timestamp")
52
+ fun getTransactionsInTimeRange(timestamp: Long): List<HttpTransaction>
53
+ }
@@ -0,0 +1,45 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.Canvas
5
+ import android.graphics.Color
6
+ import android.graphics.Matrix
7
+ import android.graphics.Paint
8
+ import androidx.annotation.ColorInt
9
+ import androidx.core.graphics.ColorUtils
10
+ import androidx.palette.graphics.Palette
11
+ import kotlinx.coroutines.Dispatchers
12
+ import kotlinx.coroutines.withContext
13
+
14
+ private val BITMAP_PAINT = Paint(Paint.FILTER_BITMAP_FLAG)
15
+
16
+ internal suspend fun Bitmap.calculateLuminance(): Double? {
17
+ val color = Color.MAGENTA
18
+ return withContext(Dispatchers.Default) {
19
+ val alpha = replaceAlphaWithColor(color)
20
+ return@withContext alpha.getLuminance(color)
21
+ }
22
+ }
23
+
24
+ private fun Bitmap.replaceAlphaWithColor(
25
+ @ColorInt color: Int,
26
+ ): Bitmap {
27
+ val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
28
+ result.eraseColor(color)
29
+ Canvas(result).apply {
30
+ drawBitmap(this@replaceAlphaWithColor, Matrix(), BITMAP_PAINT)
31
+ }
32
+ return result
33
+ }
34
+
35
+ private fun Bitmap.getLuminance(
36
+ @ColorInt alphaSubstitute: Int,
37
+ ): Double? {
38
+ val imagePalette =
39
+ Palette.from(this)
40
+ .clearFilters()
41
+ .addFilter { rgb, _ -> (rgb != alphaSubstitute) }
42
+ .generate()
43
+ val dominantSwatch = imagePalette.dominantSwatch
44
+ return dominantSwatch?.rgb?.let(ColorUtils::calculateLuminance)
45
+ }
@@ -0,0 +1,11 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import java.io.File
4
+
5
+ /**
6
+ * An interface that returns a reference to a cache directory where temporary files can be
7
+ * saved.
8
+ */
9
+ internal fun interface CacheDirectoryProvider {
10
+ fun provide(): File?
11
+ }
@@ -0,0 +1,76 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.Bitmap.Config.ARGB_8888
6
+ import android.graphics.BitmapShader
7
+ import android.graphics.Canvas
8
+ import android.graphics.ColorFilter
9
+ import android.graphics.Paint
10
+ import android.graphics.Paint.Style.FILL
11
+ import android.graphics.PixelFormat
12
+ import android.graphics.Rect
13
+ import android.graphics.Shader.TileMode.REPEAT
14
+ import android.graphics.drawable.Drawable
15
+ import androidx.annotation.ColorInt
16
+ import androidx.annotation.ColorRes
17
+ import androidx.annotation.DimenRes
18
+ import androidx.annotation.Px
19
+ import androidx.core.content.ContextCompat
20
+
21
+ internal class ChessboardDrawable(
22
+ @ColorInt evenColor: Int,
23
+ @ColorInt oddColor: Int,
24
+ @Px squareSize: Int,
25
+ ) : Drawable() {
26
+ private val chessboardPaint =
27
+ Paint().apply {
28
+ val patternBitmap = Bitmap.createBitmap(squareSize * 2, squareSize * 2, ARGB_8888)
29
+ patternBitmap.eraseColor(evenColor)
30
+
31
+ color = oddColor
32
+ style = FILL
33
+ val patternCanvas = Canvas(patternBitmap)
34
+ val squareRect = Rect(squareSize, 0, 2 * squareSize, squareSize)
35
+ patternCanvas.drawRect(squareRect, this)
36
+ squareRect.offsetTo(0, squareSize)
37
+ patternCanvas.drawRect(squareRect, this)
38
+
39
+ reset()
40
+ shader = BitmapShader(patternBitmap, REPEAT, REPEAT)
41
+ }
42
+
43
+ override fun draw(canvas: Canvas) {
44
+ canvas.drawPaint(chessboardPaint)
45
+ }
46
+
47
+ override fun setAlpha(alpha: Int) {
48
+ chessboardPaint.alpha = alpha
49
+ }
50
+
51
+ override fun getOpacity(): Int {
52
+ return if (chessboardPaint.colorFilter == null) {
53
+ PixelFormat.OPAQUE
54
+ } else {
55
+ PixelFormat.TRANSLUCENT
56
+ }
57
+ }
58
+
59
+ override fun setColorFilter(colorFilter: ColorFilter?) {
60
+ chessboardPaint.colorFilter = colorFilter
61
+ }
62
+
63
+ companion object {
64
+ fun createPattern(
65
+ context: Context,
66
+ @ColorRes evenColorId: Int,
67
+ @ColorRes oddColorId: Int,
68
+ @DimenRes sizeId: Int,
69
+ ): ChessboardDrawable {
70
+ val evenColor = ContextCompat.getColor(context, evenColorId)
71
+ val oddColor = ContextCompat.getColor(context, oddColorId)
72
+ val size = context.resources.getDimensionPixelSize(sizeId)
73
+ return ChessboardDrawable(evenColor, oddColor, size)
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,9 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import androidx.core.content.FileProvider
4
+
5
+ /**
6
+ * We need our own subclass so we don't conflict with other [FileProvider]s
7
+ * See: https://github.com/ChuckerTeam/chucker/issues/409
8
+ */
9
+ internal class ChuckerFileProvider : FileProvider()
@@ -0,0 +1,14 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+
7
+ internal class ClearDatabaseJobIntentServiceReceiver : BroadcastReceiver() {
8
+ override fun onReceive(
9
+ context: Context,
10
+ intent: Intent,
11
+ ) {
12
+ ClearDatabaseService.enqueueWork(context, intent)
13
+ }
14
+ }
@@ -0,0 +1,32 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import android.content.Intent
5
+ import androidx.core.app.JobIntentService
6
+ import com.securitysuite.internal.data.repository.RepositoryProvider
7
+ import kotlinx.coroutines.MainScope
8
+ import kotlinx.coroutines.launch
9
+
10
+ internal class ClearDatabaseService : JobIntentService() {
11
+ private val scope = MainScope()
12
+
13
+ override fun onHandleWork(intent: Intent) {
14
+ RepositoryProvider.initialize(applicationContext)
15
+ scope.launch {
16
+ RepositoryProvider.transaction().deleteAllTransactions()
17
+ NotificationHelper.clearBuffer()
18
+ NotificationHelper(applicationContext).dismissNotifications()
19
+ }
20
+ }
21
+
22
+ companion object {
23
+ private const val CLEAN_DATABASE_JOB_ID = 123321
24
+
25
+ fun enqueueWork(
26
+ context: Context,
27
+ work: Intent,
28
+ ) {
29
+ enqueueWork(context, ClearDatabaseService::class.java, CLEAN_DATABASE_JOB_ID, work)
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,22 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import com.securitysuite.internal.data.model.DialogData
5
+ import com.google.android.material.dialog.MaterialAlertDialogBuilder
6
+
7
+ internal fun Context.showDialog(
8
+ dialogData: DialogData,
9
+ onPositiveClick: (() -> Unit)?,
10
+ onNegativeClick: (() -> Unit)?,
11
+ ) {
12
+ MaterialAlertDialogBuilder(this)
13
+ .setTitle(dialogData.title)
14
+ .setMessage(dialogData.message)
15
+ .setPositiveButton(dialogData.positiveButtonText) { _, _ ->
16
+ onPositiveClick?.invoke()
17
+ }
18
+ .setNegativeButton(dialogData.negativeButtonText) { _, _ ->
19
+ onNegativeClick?.invoke()
20
+ }
21
+ .show()
22
+ }
@@ -0,0 +1,37 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import okio.Buffer
4
+ import okio.ForwardingSource
5
+ import okio.Source
6
+ import okio.blackholeSink
7
+ import okio.buffer
8
+ import java.io.IOException
9
+
10
+ internal class DepletingSource(delegate: Source) : ForwardingSource(delegate) {
11
+ private var shouldDeplete = true
12
+
13
+ override fun read(
14
+ sink: Buffer,
15
+ byteCount: Long,
16
+ ) = try {
17
+ val bytesRead = super.read(sink, byteCount)
18
+ if (bytesRead == -1L) shouldDeplete = false
19
+ bytesRead
20
+ } catch (e: IOException) {
21
+ shouldDeplete = false
22
+ throw e
23
+ }
24
+
25
+ override fun close() {
26
+ if (shouldDeplete) {
27
+ try {
28
+ delegate.buffer().readAll(blackholeSink())
29
+ } catch (e: IOException) {
30
+ Logger.error("An error occurred while depleting the source", e)
31
+ }
32
+ }
33
+ shouldDeplete = false
34
+
35
+ super.close()
36
+ }
37
+ }
@@ -0,0 +1,30 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import java.io.File
4
+ import java.io.IOException
5
+ import java.util.concurrent.atomic.AtomicLong
6
+
7
+ internal object FileFactory {
8
+ private val uniqueIdGenerator = AtomicLong()
9
+
10
+ fun create(directory: File) = create(directory, fileName = "chucker-${uniqueIdGenerator.getAndIncrement()}")
11
+
12
+ fun create(
13
+ directory: File,
14
+ fileName: String,
15
+ ): File? =
16
+ try {
17
+ File(directory, fileName).apply {
18
+ if (exists() && !delete()) {
19
+ throw IOException("Failed to delete file $this")
20
+ }
21
+ parentFile?.mkdirs()
22
+ if (!createNewFile()) {
23
+ throw IOException("File $this already exists")
24
+ }
25
+ }
26
+ } catch (e: IOException) {
27
+ Logger.error("An error occurred while creating a file", e)
28
+ null
29
+ }
30
+ }
@@ -0,0 +1,41 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.ContentResolver
4
+ import android.net.Uri
5
+ import kotlinx.coroutines.Dispatchers
6
+ import kotlinx.coroutines.withContext
7
+ import okio.Source
8
+ import okio.buffer
9
+ import okio.sink
10
+
11
+ /**
12
+ * Utility class to save a file from a [Source] to a [Uri].
13
+ */
14
+ public object FileSaver {
15
+ /**
16
+ * Saves the data from the [source] to the file at the [uri] using the [contentResolver].
17
+ *
18
+ * @param source The source of the data to save.
19
+ * @param uri The URI of the file to save the data to.
20
+ * @param contentResolver The content resolver to use to save the data.
21
+ * @return `true` if the data was saved successfully, `false` otherwise.
22
+ */
23
+ public suspend fun saveFile(
24
+ source: Source,
25
+ uri: Uri,
26
+ contentResolver: ContentResolver,
27
+ ): Boolean =
28
+ withContext(Dispatchers.IO) {
29
+ runCatching {
30
+ contentResolver.openOutputStream(uri)?.use { outputStream ->
31
+ outputStream.sink().buffer().use { sink ->
32
+ sink.writeAll(source)
33
+ }
34
+ }
35
+ }.onFailure {
36
+ Logger.error("Failed to save data to a file", it)
37
+ return@withContext false
38
+ }
39
+ return@withContext true
40
+ }
41
+ }