react-native-security-suite 0.6.5 → 0.6.7

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 +46 -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 +39 -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,202 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import android.text.SpannableStringBuilder
5
+ import android.text.Spanned
6
+ import android.text.style.ForegroundColorSpan
7
+ import androidx.core.content.ContextCompat
8
+ import com.securitysuite.R
9
+
10
+ public class SpanTextUtil(context: Context) {
11
+ private val jsonKeyColor: Int
12
+ private val jsonValueColor: Int
13
+ private val jsonDigitsAndNullValueColor: Int
14
+ private val jsonSignElementsColor: Int
15
+ private val jsonBooleanColor: Int
16
+
17
+ private companion object {
18
+ // corresponds to length of word 'true'
19
+ private const val BOOLEAN_TRUE_INDEX_OFFSET = 4
20
+
21
+ // corresponds to length of word 'false'
22
+ private const val BOOLEAN_FALSE_INDEX_OFFSET = 5
23
+ }
24
+
25
+ init {
26
+ jsonKeyColor = ContextCompat.getColor(context, R.color.chucker_json_key_color)
27
+ jsonValueColor = ContextCompat.getColor(context, R.color.chucker_json_value_color)
28
+ jsonDigitsAndNullValueColor =
29
+ ContextCompat.getColor(context, R.color.chucker_json_digit_and_null_value_color)
30
+ jsonSignElementsColor = ContextCompat.getColor(context, R.color.chucker_json_elements_color)
31
+ jsonBooleanColor = ContextCompat.getColor(context, R.color.chucker_json_boolean_color)
32
+ }
33
+
34
+ private enum class TokenType(val delimiters: Set<String>) {
35
+ STRING(setOf("\"")),
36
+ ARRAY(setOf("[", "]")),
37
+ OBJECT(setOf("{", "}")),
38
+ KEY_SEPARATOR(setOf(":")),
39
+ VALUE_SEPARATOR(setOf(",")),
40
+ BOOLEAN(setOf("true", "false")),
41
+ NONE(setOf()),
42
+ ;
43
+
44
+ companion object {
45
+ val allPossibleTokens = values().map { it.delimiters }.flatten().toSet()
46
+ }
47
+ }
48
+
49
+ private fun CharSequence.indexOfNextToken(startIndex: Int = 0): Pair<Int, TokenType> {
50
+ val (index, matched) =
51
+ findAnyOf(
52
+ strings = TokenType.allPossibleTokens,
53
+ startIndex = startIndex,
54
+ ignoreCase = true,
55
+ ) ?: return -1 to TokenType.NONE
56
+ val tokenType =
57
+ when (matched) {
58
+ in TokenType.ARRAY.delimiters -> TokenType.ARRAY
59
+ in TokenType.OBJECT.delimiters -> TokenType.OBJECT
60
+ in TokenType.KEY_SEPARATOR.delimiters -> TokenType.KEY_SEPARATOR
61
+ in TokenType.VALUE_SEPARATOR.delimiters -> TokenType.VALUE_SEPARATOR
62
+ in TokenType.STRING.delimiters -> TokenType.STRING
63
+ in TokenType.BOOLEAN.delimiters -> TokenType.BOOLEAN
64
+ else -> null
65
+ }
66
+ tokenType?.let {
67
+ return index to it
68
+ }
69
+ return -1 to TokenType.NONE
70
+ }
71
+
72
+ private fun CharSequence.indexOfNextUnescapedQuote(startIndex: Int = 0): Int {
73
+ var index = indexOf('"', startIndex)
74
+ while (index < length) {
75
+ if (this[index] == '"' && (index == 0 || this[index - 1] != '\\')) {
76
+ return index
77
+ }
78
+ index = indexOf('"', index + 1)
79
+ }
80
+ return -1
81
+ }
82
+
83
+ public fun spanJson(input: CharSequence): SpannableStringBuilder {
84
+ // First handle the pretty printing step via gson built-in support
85
+ val prettyPrintedInput = FormatUtils.formatJson(input.toString())
86
+
87
+ var lastTokenType: TokenType? = null
88
+ var index = 0
89
+
90
+ val sb = SpannableStringBuilder(prettyPrintedInput)
91
+ // First we set a span for all text to match the digits and null value color since other
92
+ // cases will be overridden below
93
+ sb.setColor(0, prettyPrintedInput.length, jsonDigitsAndNullValueColor)
94
+ while (index < prettyPrintedInput.length) {
95
+ val (tokenIndex, tokenType) = prettyPrintedInput.indexOfNextToken(startIndex = index)
96
+ when (tokenType) {
97
+ TokenType.BOOLEAN ->
98
+ sb.setBooleanColor(tokenIndex).also { endIndex ->
99
+ index = endIndex
100
+ }
101
+ TokenType.ARRAY,
102
+ TokenType.OBJECT,
103
+ TokenType.KEY_SEPARATOR,
104
+ TokenType.VALUE_SEPARATOR,
105
+ -> {
106
+ sb.setColor(
107
+ start = tokenIndex,
108
+ end = tokenIndex + 1,
109
+ color = jsonSignElementsColor,
110
+ )
111
+ index = tokenIndex + 1
112
+ }
113
+ TokenType.STRING ->
114
+ sb.setStringColor(tokenIndex, lastTokenType)?.also { endIndex ->
115
+ index = endIndex + 1
116
+ } ?: return sb
117
+ TokenType.NONE -> return sb
118
+ }
119
+ lastTokenType = tokenType
120
+ }
121
+ return sb
122
+ }
123
+
124
+ private fun SpannableStringBuilder.setColor(
125
+ start: Int,
126
+ end: Int,
127
+ color: Int,
128
+ ): SpannableStringBuilder {
129
+ this.setSpan(
130
+ ChuckerForegroundColorSpan(color),
131
+ start,
132
+ end,
133
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE,
134
+ )
135
+ return this
136
+ }
137
+
138
+ /**
139
+ * Given the tokenIndex, attempt to format the boolean value by first checking to see if the
140
+ * token starts with a 't' for true or not.
141
+ *
142
+ * Returns the index of the end of the spanned boolean value
143
+ */
144
+ private fun SpannableStringBuilder.setBooleanColor(tokenIndex: Int): Int {
145
+ val endIndex =
146
+ if (this[tokenIndex].equals('t', ignoreCase = true)) {
147
+ tokenIndex + BOOLEAN_TRUE_INDEX_OFFSET
148
+ } else {
149
+ tokenIndex + BOOLEAN_FALSE_INDEX_OFFSET
150
+ }
151
+ setColor(
152
+ start = tokenIndex,
153
+ end = endIndex,
154
+ color = jsonBooleanColor,
155
+ )
156
+ return endIndex
157
+ }
158
+
159
+ /**
160
+ * Given the tokenIndex and lastTokenType, attempt to format the color string by searching for
161
+ * the next unescaped quote mark in the string. If none is found, we return null immediately
162
+ * which should signal that the JSON string is incomplete and all further formatting should stop.
163
+ *
164
+ * Otherwise, we will return the index of the end of the spanned string
165
+ */
166
+ private fun SpannableStringBuilder.setStringColor(
167
+ tokenIndex: Int,
168
+ lastTokenType: TokenType? = null,
169
+ ): Int? {
170
+ val color =
171
+ when (lastTokenType) {
172
+ TokenType.ARRAY,
173
+ TokenType.OBJECT,
174
+ TokenType.VALUE_SEPARATOR,
175
+ TokenType.NONE,
176
+ null,
177
+ -> {
178
+ jsonKeyColor
179
+ }
180
+ else -> {
181
+ jsonValueColor
182
+ }
183
+ }
184
+
185
+ @Suppress("TooGenericExceptionCaught", "SwallowedException")
186
+ val endIndex =
187
+ try {
188
+ this.indexOfNextUnescapedQuote(tokenIndex + 1)
189
+ } catch (e: Exception) {
190
+ -1
191
+ }
192
+ // if we somehow get an incomplete string, we lose the ability to parse any other
193
+ // tokens, so just return now
194
+ if (endIndex < tokenIndex) {
195
+ return null
196
+ }
197
+ setColor(start = tokenIndex, end = endIndex + 1, color)
198
+ return endIndex
199
+ }
200
+
201
+ public class ChuckerForegroundColorSpan(color: Int) : ForegroundColorSpan(color)
202
+ }
@@ -0,0 +1,68 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import okio.Buffer
4
+ import okio.Sink
5
+ import okio.Source
6
+ import okio.Timeout
7
+ import java.io.IOException
8
+
9
+ /**
10
+ * A source that acts as a tee operator - https://en.wikipedia.org/wiki/Tee_(command).
11
+ *
12
+ * It takes the input [upstream] and reads from it serving the bytes to the end consumer
13
+ * like a regular [Source]. While bytes are read from the [upstream] the are also copied
14
+ * to a [sideStream].
15
+ */
16
+ internal class TeeSource(
17
+ private val upstream: Source,
18
+ private val sideStream: Sink,
19
+ ) : Source {
20
+ private val tempBuffer = Buffer()
21
+ private var isFailure = false
22
+
23
+ override fun read(
24
+ sink: Buffer,
25
+ byteCount: Long,
26
+ ): Long {
27
+ val bytesRead = upstream.read(sink, byteCount)
28
+
29
+ if (bytesRead == -1L) {
30
+ safeCloseSideStream()
31
+ return -1L
32
+ }
33
+
34
+ if (!isFailure) {
35
+ copyBytesToSideStream(sink, bytesRead)
36
+ }
37
+
38
+ return bytesRead
39
+ }
40
+
41
+ private fun copyBytesToSideStream(
42
+ sink: Buffer,
43
+ bytesRead: Long,
44
+ ) {
45
+ val offset = sink.size - bytesRead
46
+ sink.copyTo(tempBuffer, offset, bytesRead)
47
+ try {
48
+ sideStream.write(tempBuffer, bytesRead)
49
+ } catch (_: IOException) {
50
+ isFailure = true
51
+ safeCloseSideStream()
52
+ }
53
+ }
54
+
55
+ override fun close() {
56
+ safeCloseSideStream()
57
+ upstream.close()
58
+ }
59
+
60
+ private fun safeCloseSideStream() =
61
+ try {
62
+ sideStream.close()
63
+ } catch (_: IOException) {
64
+ isFailure = true
65
+ }
66
+
67
+ override fun timeout(): Timeout = upstream.timeout()
68
+ }
@@ -0,0 +1,46 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import com.securitysuite.internal.data.entity.HttpHeader
5
+ import com.securitysuite.internal.data.entity.HttpTransaction
6
+ import okio.Buffer
7
+ import okio.Source
8
+
9
+ internal class TransactionCurlCommandSharable(
10
+ private val transaction: HttpTransaction,
11
+ ) : Sharable {
12
+ override fun toSharableContent(context: Context): Source =
13
+ Buffer().apply {
14
+ var compressed = false
15
+ writeUtf8("curl -X ${transaction.method}")
16
+ val headers = transaction.getParsedRequestHeaders()
17
+
18
+ headers?.forEach { header ->
19
+ if (isCompressed(header)) {
20
+ compressed = true
21
+ }
22
+ val headerValue = escapeHeaderValue(header.value)
23
+ writeUtf8(" -H \"${header.name}: ${headerValue}\"")
24
+ }
25
+
26
+ val requestBody = transaction.requestBody
27
+ if (!requestBody.isNullOrEmpty()) {
28
+ // try to keep to a single line and use a subshell to preserve any line breaks
29
+ writeUtf8(" --data $'${requestBody.replace("\n", "\\n")}'")
30
+ }
31
+ writeUtf8((if (compressed) " --compressed " else " ") + transaction.getFormattedUrl(encode = false))
32
+ }
33
+
34
+ private fun isCompressed(header: HttpHeader): Boolean {
35
+ return (
36
+ "Accept-Encoding".equals(header.name, ignoreCase = true) &&
37
+ "gzip".contains(header.value, ignoreCase = true) ||
38
+ "br".contains(header.value, ignoreCase = true)
39
+ )
40
+ }
41
+
42
+ private fun escapeHeaderValue(value: String): String {
43
+ // escape double quotes from header value to prevent getting an invalid curl
44
+ return value.replace("\"", "\\\"")
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import okio.Buffer
5
+ import okio.Source
6
+
7
+ internal class TransactionDetailsHarSharable(
8
+ private val content: String,
9
+ ) : Sharable {
10
+ override fun toSharableContent(context: Context): Source = Buffer().writeUtf8("$content\n")
11
+ }
@@ -0,0 +1,73 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import com.securitysuite.R
5
+ import com.securitysuite.internal.data.entity.HttpTransaction
6
+ import okio.Buffer
7
+ import okio.Source
8
+
9
+ internal class TransactionDetailsSharable(
10
+ private val transaction: HttpTransaction,
11
+ private val encodeUrls: Boolean,
12
+ ) : Sharable {
13
+ override fun toSharableContent(context: Context): Source =
14
+ Buffer().apply {
15
+ writeUtf8("${context.getString(R.string.chucker_url)}: ${transaction.getFormattedUrl(encodeUrls)}\n")
16
+ writeUtf8("${context.getString(R.string.chucker_method)}: ${transaction.method}\n")
17
+ writeUtf8("${context.getString(R.string.chucker_protocol)}: ${transaction.protocol}\n")
18
+ writeUtf8("${context.getString(R.string.chucker_status)}: ${transaction.status}\n")
19
+ writeUtf8("${context.getString(R.string.chucker_response)}: ${transaction.responseSummaryText}\n")
20
+ val isSsl = if (transaction.isSsl) R.string.chucker_yes else R.string.chucker_no
21
+ writeUtf8("${context.getString(R.string.chucker_ssl)}: ${context.getString(isSsl)}\n")
22
+ writeUtf8("\n")
23
+ writeUtf8("${context.getString(R.string.chucker_request_time)}: ${transaction.requestDateString}\n")
24
+ writeUtf8("${context.getString(R.string.chucker_response_time)}: ${transaction.responseDateString}\n")
25
+ writeUtf8("${context.getString(R.string.chucker_duration)}: ${transaction.durationString}\n")
26
+ writeUtf8("\n")
27
+ writeUtf8("${context.getString(R.string.chucker_request_size)}: ${transaction.requestSizeString}\n")
28
+ writeUtf8("${context.getString(R.string.chucker_response_size)}: ${transaction.responseSizeString}\n")
29
+ writeUtf8("${context.getString(R.string.chucker_total_size)}: ${transaction.totalSizeString}\n")
30
+ writeUtf8("\n")
31
+ writeUtf8("---------- ${context.getString(R.string.chucker_request)} ----------\n\n")
32
+ var headers = FormatUtils.formatHeaders(transaction.getParsedRequestHeaders(), false)
33
+ if (headers.isNotBlank()) {
34
+ writeUtf8(headers)
35
+ writeUtf8("\n")
36
+ }
37
+ writeUtf8(
38
+ if (transaction.requestBody.isNullOrBlank()) {
39
+ val resId =
40
+ if (transaction.isResponseBodyEncoded) {
41
+ R.string.chucker_body_omitted
42
+ } else {
43
+ R.string.chucker_body_empty
44
+ }
45
+ context.getString(resId)
46
+ } else {
47
+ transaction.getFormattedRequestBody()
48
+ },
49
+ )
50
+ writeUtf8("\n\n")
51
+ writeUtf8("---------- ${context.getString(R.string.chucker_response)} ----------\n\n")
52
+ headers = FormatUtils.formatHeaders(transaction.getParsedResponseHeaders(), false)
53
+
54
+ if (headers.isNotBlank()) {
55
+ writeUtf8(headers)
56
+ writeUtf8("\n")
57
+ }
58
+
59
+ writeUtf8(
60
+ if (transaction.responseBody.isNullOrBlank()) {
61
+ val resId =
62
+ if (transaction.isResponseBodyEncoded) {
63
+ R.string.chucker_body_omitted
64
+ } else {
65
+ R.string.chucker_body_empty
66
+ }
67
+ context.getString(resId)
68
+ } else {
69
+ transaction.getFormattedResponseBody()
70
+ },
71
+ )
72
+ }
73
+ }
@@ -0,0 +1,26 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import androidx.recyclerview.widget.DiffUtil
4
+ import com.securitysuite.internal.data.entity.HttpTransactionTuple
5
+
6
+ internal object TransactionDiffCallback : DiffUtil.ItemCallback<HttpTransactionTuple>() {
7
+ override fun areItemsTheSame(
8
+ oldItem: HttpTransactionTuple,
9
+ newItem: HttpTransactionTuple,
10
+ ): Boolean {
11
+ return oldItem.id == newItem.id
12
+ }
13
+
14
+ override fun areContentsTheSame(
15
+ oldItem: HttpTransactionTuple,
16
+ newItem: HttpTransactionTuple,
17
+ ): Boolean {
18
+ return oldItem == newItem
19
+ }
20
+
21
+ // Overriding function is empty on purpose to avoid flickering by default animator
22
+ override fun getChangePayload(
23
+ oldItem: HttpTransactionTuple,
24
+ newItem: HttpTransactionTuple,
25
+ ) = Unit
26
+ }
@@ -0,0 +1,23 @@
1
+ package com.securitysuite.internal.support
2
+
3
+ import android.content.Context
4
+ import com.securitysuite.R.string
5
+ import com.securitysuite.internal.data.entity.HttpTransaction
6
+ import okio.Buffer
7
+ import okio.Source
8
+
9
+ internal class TransactionListDetailsSharable(
10
+ transactions: List<HttpTransaction>,
11
+ encodeUrls: Boolean,
12
+ ) : Sharable {
13
+ private val transactions = transactions.map { TransactionDetailsSharable(it, encodeUrls) }
14
+
15
+ override fun toSharableContent(context: Context): Source =
16
+ Buffer().writeUtf8(
17
+ transactions.joinToString(
18
+ separator = "\n${context.getString(string.chucker_export_separator)}\n",
19
+ prefix = "${context.getString(string.chucker_export_prefix)}\n",
20
+ postfix = "\n${context.getString(string.chucker_export_postfix)}\n",
21
+ ) { it.toSharableUtf8Content(context) },
22
+ )
23
+ }
@@ -0,0 +1,35 @@
1
+ package com.securitysuite.internal.ui
2
+
3
+ import android.os.Bundle
4
+ import android.widget.Toast
5
+ import androidx.appcompat.app.AppCompatActivity
6
+ import com.securitysuite.internal.data.repository.RepositoryProvider
7
+
8
+ internal abstract class BaseChuckerActivity : AppCompatActivity() {
9
+ override fun onCreate(savedInstanceState: Bundle?) {
10
+ super.onCreate(savedInstanceState)
11
+ RepositoryProvider.initialize(applicationContext)
12
+ }
13
+
14
+ override fun onResume() {
15
+ super.onResume()
16
+ isInForeground = true
17
+ }
18
+
19
+ override fun onPause() {
20
+ super.onPause()
21
+ isInForeground = false
22
+ }
23
+
24
+ companion object {
25
+ var isInForeground: Boolean = false
26
+ private set
27
+ }
28
+
29
+ fun showToast(
30
+ message: String,
31
+ toastDuration: Int = Toast.LENGTH_SHORT,
32
+ ) {
33
+ Toast.makeText(this.applicationContext, message, toastDuration).show()
34
+ }
35
+ }