react-native-security-suite 0.6.7 → 0.7.0

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 (123) hide show
  1. package/android/build.gradle +0 -15
  2. package/android/src/main/AndroidManifest.xml +3 -48
  3. package/package.json +1 -1
  4. package/android/src/main/java/com/securitysuite/NetworkLogger.java +0 -25
  5. package/android/src/main/java/com/securitysuite/api/BodyDecoder.kt +0 -35
  6. package/android/src/main/java/com/securitysuite/api/Chucker.kt +0 -108
  7. package/android/src/main/java/com/securitysuite/api/ChuckerCollector.kt +0 -129
  8. package/android/src/main/java/com/securitysuite/api/ChuckerInterceptor.kt +0 -280
  9. package/android/src/main/java/com/securitysuite/api/ExportFormat.kt +0 -12
  10. package/android/src/main/java/com/securitysuite/api/RetentionManager.kt +0 -109
  11. package/android/src/main/java/com/securitysuite/internal/data/entity/HttpHeader.kt +0 -8
  12. package/android/src/main/java/com/securitysuite/internal/data/entity/HttpTransaction.kt +0 -344
  13. package/android/src/main/java/com/securitysuite/internal/data/entity/HttpTransactionTuple.kt +0 -62
  14. package/android/src/main/java/com/securitysuite/internal/data/har/Har.kt +0 -13
  15. package/android/src/main/java/com/securitysuite/internal/data/har/Log.kt +0 -24
  16. package/android/src/main/java/com/securitysuite/internal/data/har/log/Browser.kt +0 -11
  17. package/android/src/main/java/com/securitysuite/internal/data/har/log/Creator.kt +0 -11
  18. package/android/src/main/java/com/securitysuite/internal/data/har/log/Entry.kt +0 -49
  19. package/android/src/main/java/com/securitysuite/internal/data/har/log/Page.kt +0 -14
  20. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Cache.kt +0 -12
  21. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Header.kt +0 -17
  22. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Request.kt +0 -35
  23. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Response.kt +0 -33
  24. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/Timings.kt +0 -25
  25. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/cache/SecondaryRequest.kt +0 -13
  26. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/request/PostData.kt +0 -20
  27. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/request/QueryString.kt +0 -23
  28. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/request/postdata/Params.kt +0 -13
  29. package/android/src/main/java/com/securitysuite/internal/data/har/log/entry/response/Content.kt +0 -30
  30. package/android/src/main/java/com/securitysuite/internal/data/har/log/page/PageTimings.kt +0 -11
  31. package/android/src/main/java/com/securitysuite/internal/data/model/DialogData.kt +0 -8
  32. package/android/src/main/java/com/securitysuite/internal/data/repository/HttpTransactionDatabaseRepository.kt +0 -60
  33. package/android/src/main/java/com/securitysuite/internal/data/repository/HttpTransactionRepository.kt +0 -33
  34. package/android/src/main/java/com/securitysuite/internal/data/repository/RepositoryProvider.kt +0 -38
  35. package/android/src/main/java/com/securitysuite/internal/data/room/ChuckerDatabase.kt +0 -22
  36. package/android/src/main/java/com/securitysuite/internal/data/room/HttpTransactionDao.kt +0 -53
  37. package/android/src/main/java/com/securitysuite/internal/support/BitmapUtils.kt +0 -45
  38. package/android/src/main/java/com/securitysuite/internal/support/CacheDirectoryProvider.kt +0 -11
  39. package/android/src/main/java/com/securitysuite/internal/support/ChessboardDrawable.kt +0 -76
  40. package/android/src/main/java/com/securitysuite/internal/support/ChuckerFileProvider.kt +0 -9
  41. package/android/src/main/java/com/securitysuite/internal/support/ClearDatabaseJobIntentServiceReceiver.kt +0 -14
  42. package/android/src/main/java/com/securitysuite/internal/support/ClearDatabaseService.kt +0 -32
  43. package/android/src/main/java/com/securitysuite/internal/support/ContextExt.kt +0 -22
  44. package/android/src/main/java/com/securitysuite/internal/support/DepletingSource.kt +0 -37
  45. package/android/src/main/java/com/securitysuite/internal/support/FileFactory.kt +0 -30
  46. package/android/src/main/java/com/securitysuite/internal/support/FileSaver.kt +0 -41
  47. package/android/src/main/java/com/securitysuite/internal/support/FormatUtils.kt +0 -117
  48. package/android/src/main/java/com/securitysuite/internal/support/FormattedUrl.kt +0 -76
  49. package/android/src/main/java/com/securitysuite/internal/support/HarUtils.kt +0 -28
  50. package/android/src/main/java/com/securitysuite/internal/support/HttpUrlUtils.kt +0 -11
  51. package/android/src/main/java/com/securitysuite/internal/support/JsonConverter.kt +0 -19
  52. package/android/src/main/java/com/securitysuite/internal/support/LimitingSource.kt +0 -22
  53. package/android/src/main/java/com/securitysuite/internal/support/LiveDataUtils.kt +0 -68
  54. package/android/src/main/java/com/securitysuite/internal/support/Logger.kt +0 -43
  55. package/android/src/main/java/com/securitysuite/internal/support/NotificationHelper.kt +0 -149
  56. package/android/src/main/java/com/securitysuite/internal/support/OkHttpUtils.kt +0 -86
  57. package/android/src/main/java/com/securitysuite/internal/support/OkioUtils.kt +0 -34
  58. package/android/src/main/java/com/securitysuite/internal/support/PlainTextDecoder.kt +0 -30
  59. package/android/src/main/java/com/securitysuite/internal/support/ReportingSink.kt +0 -114
  60. package/android/src/main/java/com/securitysuite/internal/support/RequestProcessor.kt +0 -102
  61. package/android/src/main/java/com/securitysuite/internal/support/ResponseProcessor.kt +0 -170
  62. package/android/src/main/java/com/securitysuite/internal/support/SearchHighlightUtil.kt +0 -80
  63. package/android/src/main/java/com/securitysuite/internal/support/Sharable.kt +0 -86
  64. package/android/src/main/java/com/securitysuite/internal/support/SpanTextUtil.kt +0 -202
  65. package/android/src/main/java/com/securitysuite/internal/support/TeeSource.kt +0 -68
  66. package/android/src/main/java/com/securitysuite/internal/support/TransactionCurlCommandSharable.kt +0 -46
  67. package/android/src/main/java/com/securitysuite/internal/support/TransactionDetailsHarSharable.kt +0 -11
  68. package/android/src/main/java/com/securitysuite/internal/support/TransactionDetailsSharable.kt +0 -73
  69. package/android/src/main/java/com/securitysuite/internal/support/TransactionDiffCallback.kt +0 -26
  70. package/android/src/main/java/com/securitysuite/internal/support/TransactionListDetailsSharable.kt +0 -23
  71. package/android/src/main/java/com/securitysuite/internal/ui/BaseChuckerActivity.kt +0 -35
  72. package/android/src/main/java/com/securitysuite/internal/ui/MainActivity.kt +0 -375
  73. package/android/src/main/java/com/securitysuite/internal/ui/MainViewModel.kt +0 -47
  74. package/android/src/main/java/com/securitysuite/internal/ui/transaction/PayloadType.kt +0 -6
  75. package/android/src/main/java/com/securitysuite/internal/ui/transaction/ProtocolResources.kt +0 -14
  76. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionActivity.kt +0 -186
  77. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionAdapter.kt +0 -139
  78. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionOverviewFragment.kt +0 -100
  79. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionPagerAdapter.kt +0 -29
  80. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionPayloadAdapter.kt +0 -269
  81. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionPayloadFragment.kt +0 -529
  82. package/android/src/main/java/com/securitysuite/internal/ui/transaction/TransactionViewModel.kt +0 -69
  83. package/android/src/main/res/A.java +0 -12
  84. package/android/src/main/res/color/chucker_fab_background_colour.xml +0 -5
  85. package/android/src/main/res/drawable/chucker_empty_payload.xml +0 -10
  86. package/android/src/main/res/drawable/chucker_ic_arrow_down.xml +0 -10
  87. package/android/src/main/res/drawable/chucker_ic_copy.xml +0 -12
  88. package/android/src/main/res/drawable/chucker_ic_decoded_url_white.xml +0 -10
  89. package/android/src/main/res/drawable/chucker_ic_delete_white.xml +0 -9
  90. package/android/src/main/res/drawable/chucker_ic_encoded_url_white.xml +0 -11
  91. package/android/src/main/res/drawable/chucker_ic_graphql.xml +0 -27
  92. package/android/src/main/res/drawable/chucker_ic_http.xml +0 -10
  93. package/android/src/main/res/drawable/chucker_ic_https.xml +0 -9
  94. package/android/src/main/res/drawable/chucker_ic_launcher_foreground.xml +0 -14
  95. package/android/src/main/res/drawable/chucker_ic_save_white.xml +0 -9
  96. package/android/src/main/res/drawable/chucker_ic_search_white.xml +0 -9
  97. package/android/src/main/res/drawable/chucker_ic_share_white.xml +0 -9
  98. package/android/src/main/res/drawable/chucker_ic_transaction_notification.xml +0 -9
  99. package/android/src/main/res/layout/activity_main.xml +0 -83
  100. package/android/src/main/res/layout/activity_transaction.xml +0 -48
  101. package/android/src/main/res/layout/fragment_transaction_overview.xml +0 -365
  102. package/android/src/main/res/layout/fragment_transaction_payload.xml +0 -132
  103. package/android/src/main/res/layout/list_item_transaction.xml +0 -122
  104. package/android/src/main/res/layout/transaction_item_body_line.xml +0 -13
  105. package/android/src/main/res/layout/transaction_item_copy.xml +0 -19
  106. package/android/src/main/res/layout/transaction_item_headers.xml +0 -12
  107. package/android/src/main/res/layout/transaction_item_image.xml +0 -16
  108. package/android/src/main/res/menu/chucker_transaction.xml +0 -46
  109. package/android/src/main/res/menu/chucker_transactions_list.xml +0 -41
  110. package/android/src/main/res/mipmap-anydpi-v26/chucker_ic_launcher.xml +0 -5
  111. package/android/src/main/res/mipmap-hdpi/chucker_ic_launcher.png +0 -0
  112. package/android/src/main/res/mipmap-xhdpi/chucker_ic_launcher.png +0 -0
  113. package/android/src/main/res/mipmap-xxhdpi/chucker_ic_launcher.png +0 -0
  114. package/android/src/main/res/mipmap-xxxhdpi/chucker_ic_launcher.png +0 -0
  115. package/android/src/main/res/values/chucker_ic_launcher_background.xml +0 -4
  116. package/android/src/main/res/values/colors.xml +0 -38
  117. package/android/src/main/res/values/dimens.xml +0 -10
  118. package/android/src/main/res/values/public.xml +0 -5
  119. package/android/src/main/res/values/strings.xml +0 -77
  120. package/android/src/main/res/values/styles.xml +0 -44
  121. package/android/src/main/res/values-es/strings.xml +0 -75
  122. package/android/src/main/res/values-night/colors.xml +0 -32
  123. package/android/src/main/res/xml/chucker_provider_paths.xml +0 -4
@@ -1,86 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import okhttp3.Headers
4
- import okhttp3.Response
5
- import okio.Source
6
- import okio.buffer
7
- import okio.gzip
8
- import okio.source
9
- import org.brotli.dec.BrotliInputStream
10
- import java.net.HttpURLConnection.HTTP_NOT_MODIFIED
11
- import java.net.HttpURLConnection.HTTP_NO_CONTENT
12
- import java.net.HttpURLConnection.HTTP_OK
13
- import java.util.Locale
14
-
15
- private const val HTTP_CONTINUE = 100
16
-
17
- /** Returns true if the response must have a (possibly 0-length) body. See RFC 7231. */
18
- internal fun Response.hasBody(): Boolean {
19
- // HEAD requests never yield a body regardless of the response headers.
20
- if (request.method == "HEAD") {
21
- return false
22
- }
23
-
24
- val responseCode = code
25
- if ((responseCode < HTTP_CONTINUE || responseCode >= HTTP_OK) &&
26
- (responseCode != HTTP_NO_CONTENT) &&
27
- (responseCode != HTTP_NOT_MODIFIED)
28
- ) {
29
- return true
30
- }
31
-
32
- // If the Content-Length or Transfer-Encoding headers disagree with the response code, the
33
- // response is malformed. For best compatibility, we honor the headers.
34
- return ((contentLength > 0) || isChunked)
35
- }
36
-
37
- private val Response.contentLength: Long
38
- get() {
39
- return this.header("Content-Length")?.toLongOrNull() ?: -1L
40
- }
41
-
42
- internal val Response.isChunked: Boolean
43
- get() {
44
- return this.header("Transfer-Encoding").equals("chunked", ignoreCase = true)
45
- }
46
-
47
- internal val Response.contentType: String?
48
- get() {
49
- return this.header("Content-Type")
50
- }
51
-
52
- private val Headers.containsGzip: Boolean
53
- get() {
54
- return this["Content-Encoding"].equals("gzip", ignoreCase = true)
55
- }
56
-
57
- private val Headers.containsBrotli: Boolean
58
- get() {
59
- return this["Content-Encoding"].equals("br", ignoreCase = true)
60
- }
61
-
62
- private val supportedEncodings = listOf("identity", "gzip", "br")
63
-
64
- internal val Headers.hasSupportedContentEncoding: Boolean
65
- get() =
66
- get("Content-Encoding")
67
- ?.takeIf { it.isNotEmpty() }
68
- ?.let { it.lowercase(Locale.ROOT) in supportedEncodings }
69
- ?: true
70
-
71
- internal fun Source.uncompress(headers: Headers) =
72
- when {
73
- headers.containsGzip -> gzip()
74
- headers.containsBrotli -> BrotliInputStream(this.buffer().inputStream()).source()
75
- else -> this
76
- }
77
-
78
- internal fun Headers.redact(names: Iterable<String>): Headers {
79
- val builder = newBuilder()
80
- for (name in names()) {
81
- if (names.any { userHeader -> userHeader.equals(name, ignoreCase = true) }) {
82
- builder[name] = "**"
83
- }
84
- }
85
- return builder.build()
86
- }
@@ -1,34 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import okio.Buffer
4
- import okio.ByteString
5
- import java.io.EOFException
6
- import kotlin.math.min
7
-
8
- private const val MAX_PREFIX_SIZE = 64L
9
- private const val CODE_POINT_SIZE = 16
10
-
11
- /**
12
- * Returns true if the [Buffer] contains human readable text. Uses a small sample
13
- * of code points to detect unicode control characters commonly used in binary file signatures.
14
- */
15
- internal val Buffer.isProbablyPlainText
16
- get() =
17
- try {
18
- val prefix = Buffer()
19
- val byteCount = min(size, MAX_PREFIX_SIZE)
20
- copyTo(prefix, 0, byteCount)
21
- sequence { while (!prefix.exhausted()) yield(prefix.readUtf8CodePoint()) }
22
- .take(CODE_POINT_SIZE)
23
- .all { codePoint -> codePoint.isPlainTextChar() }
24
- } catch (_: EOFException) {
25
- false // Truncated UTF-8 sequence
26
- }
27
-
28
- internal val ByteString.isProbablyPlainText: Boolean
29
- get() {
30
- val byteCount = min(size, MAX_PREFIX_SIZE.toInt())
31
- return Buffer().write(this, offset = 0, byteCount).isProbablyPlainText
32
- }
33
-
34
- private fun Int.isPlainTextChar() = Character.isWhitespace(this) || !Character.isISOControl(this)
@@ -1,30 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import com.securitysuite.api.BodyDecoder
4
- import okhttp3.Headers
5
- import okhttp3.MediaType
6
- import okhttp3.Request
7
- import okhttp3.Response
8
- import okio.ByteString
9
- import kotlin.text.Charsets.UTF_8
10
-
11
- internal object PlainTextDecoder : BodyDecoder {
12
- override fun decodeRequest(
13
- request: Request,
14
- body: ByteString,
15
- ) = body.tryDecodeAsPlainText(request.headers, request.body?.contentType())
16
-
17
- override fun decodeResponse(
18
- response: Response,
19
- body: ByteString,
20
- ) = body.tryDecodeAsPlainText(response.headers, response.body?.contentType())
21
-
22
- private fun ByteString.tryDecodeAsPlainText(
23
- headers: Headers,
24
- contentType: MediaType?,
25
- ) = if (headers.hasSupportedContentEncoding && isProbablyPlainText) {
26
- string(contentType?.charset() ?: UTF_8)
27
- } else {
28
- null
29
- }
30
- }
@@ -1,114 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import okio.Buffer
4
- import okio.Sink
5
- import okio.Timeout
6
- import okio.sink
7
- import java.io.File
8
- import java.io.IOException
9
-
10
- /**
11
- * A sink that reports result of writing to it via [callback].
12
- *
13
- * Takes an input [downstreamFile] and writes bytes from a source into this input. Amount of bytes
14
- * to copy can be limited with [writeByteLimit]. Results are reported back to a client
15
- * when sink is closed or when an exception occurs while creating a downstream sink or while
16
- * writing bytes.
17
- */
18
- internal class ReportingSink(
19
- private val downstreamFile: File?,
20
- private val callback: Callback,
21
- private val writeByteLimit: Long = Long.MAX_VALUE,
22
- ) : Sink {
23
- private var totalByteCount = 0L
24
- private var isFailure = false
25
- private var isClosed = false
26
- private var downstream =
27
- try {
28
- downstreamFile?.sink()
29
- } catch (e: IOException) {
30
- callDownstreamFailure(IOException("Failed to use file $downstreamFile by Chucker", e))
31
- null
32
- }
33
-
34
- override fun write(
35
- source: Buffer,
36
- byteCount: Long,
37
- ) {
38
- val previousTotalByteCount = totalByteCount
39
- totalByteCount += byteCount
40
- if (isFailure || previousTotalByteCount >= writeByteLimit) return
41
-
42
- val bytesToWrite =
43
- if (previousTotalByteCount + byteCount <= writeByteLimit) {
44
- byteCount
45
- } else {
46
- writeByteLimit - previousTotalByteCount
47
- }
48
-
49
- if (bytesToWrite == 0L) return
50
-
51
- try {
52
- downstream?.write(source, bytesToWrite)
53
- } catch (e: IOException) {
54
- callDownstreamFailure(e)
55
- }
56
- }
57
-
58
- override fun flush() {
59
- if (isFailure) return
60
- try {
61
- downstream?.flush()
62
- } catch (e: IOException) {
63
- callDownstreamFailure(e)
64
- }
65
- }
66
-
67
- override fun close() {
68
- if (isClosed) return
69
- isClosed = true
70
- safeCloseDownstream()
71
- callback.onClosed(downstreamFile, totalByteCount)
72
- }
73
-
74
- override fun timeout(): Timeout = downstream?.timeout() ?: Timeout.NONE
75
-
76
- private fun callDownstreamFailure(exception: IOException) {
77
- if (!isFailure) {
78
- isFailure = true
79
- safeCloseDownstream()
80
- callback.onFailure(downstreamFile, exception)
81
- }
82
- }
83
-
84
- private fun safeCloseDownstream() =
85
- try {
86
- downstream?.close()
87
- } catch (e: IOException) {
88
- callDownstreamFailure(e)
89
- }
90
-
91
- interface Callback {
92
- /**
93
- * Called when the sink is closed. All written bytes are copied to the [file].
94
- * This does not mean that the content of the [file] is valid. Only that the client
95
- * is done with the writing process.
96
- *
97
- * [sourceByteCount] is the exact amount of bytes that the were read from upstream even if
98
- * the [file] is corrupted or does not exist. It is not limited by [writeByteLimit].
99
- */
100
- fun onClosed(
101
- file: File?,
102
- sourceByteCount: Long,
103
- )
104
-
105
- /**
106
- * Called when an [exception] was thrown while processing data.
107
- * Any written bytes are available in a [file].
108
- */
109
- fun onFailure(
110
- file: File?,
111
- exception: IOException,
112
- )
113
- }
114
- }
@@ -1,102 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import android.content.Context
4
- import com.securitysuite.R
5
- import com.securitysuite.api.BodyDecoder
6
- import com.securitysuite.api.ChuckerCollector
7
- import com.securitysuite.internal.data.entity.HttpTransaction
8
- import okhttp3.Request
9
- import okio.Buffer
10
- import okio.ByteString
11
- import okio.IOException
12
-
13
- internal class RequestProcessor(
14
- private val context: Context,
15
- private val collector: ChuckerCollector,
16
- private val maxContentLength: Long,
17
- private val headersToRedact: Set<String>,
18
- private val bodyDecoders: List<BodyDecoder>,
19
- ) {
20
- fun process(
21
- request: Request,
22
- transaction: HttpTransaction,
23
- ) {
24
- processMetadata(request, transaction)
25
- processPayload(request, transaction)
26
- collector.onRequestSent(transaction)
27
- }
28
-
29
- private fun processMetadata(
30
- request: Request,
31
- transaction: HttpTransaction,
32
- ) {
33
- transaction.apply {
34
- requestHeadersSize = request.headers.byteCount()
35
- request.headers.redact(headersToRedact).let {
36
- setRequestHeaders(it)
37
- setGraphQlOperationName(it)
38
- }
39
- populateUrl(request.url)
40
- graphQlDetected = isGraphQLRequest(this.graphQlOperationName, request)
41
-
42
- requestDate = System.currentTimeMillis()
43
- method = request.method
44
- requestContentType = request.body?.contentType()?.toString()
45
- requestPayloadSize = request.body?.contentLength()
46
- }
47
- }
48
-
49
- private fun processPayload(
50
- request: Request,
51
- transaction: HttpTransaction,
52
- ) {
53
- val body = request.body ?: return
54
- if (body.isOneShot()) {
55
- Logger.info("Skipping one shot request body")
56
- return
57
- }
58
- if (body.isDuplex()) {
59
- Logger.info("Skipping duplex request body")
60
- return
61
- }
62
-
63
- val requestSource =
64
- try {
65
- Buffer().apply { body.writeTo(this) }
66
- } catch (e: IOException) {
67
- Logger.error("Failed to read request payload", e)
68
- return
69
- }
70
- val limitingSource = LimitingSource(requestSource.uncompress(request.headers), maxContentLength)
71
-
72
- val contentBuffer = Buffer().apply { limitingSource.use { writeAll(it) } }
73
-
74
- val decodedContent = decodePayload(request, contentBuffer.readByteString())
75
- transaction.requestBody = decodedContent
76
- transaction.isRequestBodyEncoded = decodedContent == null
77
- if (decodedContent != null && limitingSource.isThresholdReached) {
78
- transaction.requestBody += context.getString(R.string.chucker_body_content_truncated)
79
- }
80
- }
81
-
82
- private fun decodePayload(
83
- request: Request,
84
- body: ByteString,
85
- ) = bodyDecoders.asSequence()
86
- .mapNotNull { decoder ->
87
- try {
88
- Logger.info("Decoding with: $decoder")
89
- decoder.decodeRequest(request, body)
90
- } catch (e: IOException) {
91
- Logger.warn("Decoder $decoder failed to process request payload", e)
92
- null
93
- }
94
- }.firstOrNull()
95
-
96
- private fun isGraphQLRequest(
97
- graphQLOperationName: String?,
98
- request: Request,
99
- ) = graphQLOperationName != null ||
100
- request.url.pathSegments.contains("graphql") ||
101
- request.url.host.contains("graphql")
102
- }
@@ -1,170 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import com.securitysuite.api.BodyDecoder
4
- import com.securitysuite.api.ChuckerCollector
5
- import com.securitysuite.internal.data.entity.HttpTransaction
6
- import okhttp3.Response
7
- import okhttp3.ResponseBody.Companion.asResponseBody
8
- import okio.Buffer
9
- import okio.ByteString
10
- import okio.IOException
11
- import okio.Source
12
- import okio.buffer
13
- import okio.source
14
- import java.io.File
15
-
16
- internal class ResponseProcessor(
17
- private val collector: ChuckerCollector,
18
- private val cacheDirectoryProvider: CacheDirectoryProvider,
19
- private val maxContentLength: Long,
20
- private val headersToRedact: Set<String>,
21
- private val alwaysReadResponseBody: Boolean,
22
- private val bodyDecoders: List<BodyDecoder>,
23
- ) {
24
- fun process(
25
- response: Response,
26
- transaction: HttpTransaction,
27
- ): Response {
28
- processResponseMetadata(response, transaction)
29
- return multiCastResponse(response, transaction)
30
- }
31
-
32
- private fun processResponseMetadata(
33
- response: Response,
34
- transaction: HttpTransaction,
35
- ) {
36
- transaction.apply {
37
- // includes headers added later in the chain
38
- requestHeadersSize = response.request.headers.byteCount()
39
- setRequestHeaders(response.request.headers.redact(headersToRedact))
40
- responseHeadersSize = response.headers.byteCount()
41
- setResponseHeaders(response.headers.redact(headersToRedact))
42
-
43
- requestDate = response.sentRequestAtMillis
44
- responseDate = response.receivedResponseAtMillis
45
- protocol = response.protocol.toString()
46
- responseCode = response.code
47
- responseMessage = response.message
48
-
49
- response.handshake?.let { handshake ->
50
- responseTlsVersion = handshake.tlsVersion.javaName
51
- responseCipherSuite = handshake.cipherSuite.javaName
52
- }
53
-
54
- responseContentType = response.contentType
55
-
56
- tookMs = (response.receivedResponseAtMillis - response.sentRequestAtMillis)
57
- }
58
- }
59
-
60
- private fun multiCastResponse(
61
- response: Response,
62
- transaction: HttpTransaction,
63
- ): Response {
64
- val responseBody = response.body
65
- if (!response.hasBody() || responseBody == null) {
66
- collector.onResponseReceived(transaction)
67
- return response
68
- }
69
-
70
- val contentType = responseBody.contentType()
71
- val contentLength = responseBody.contentLength()
72
-
73
- val sideStream =
74
- ReportingSink(
75
- createTempTransactionFile(),
76
- ResponseReportingSinkCallback(response, transaction),
77
- maxContentLength,
78
- )
79
- var upstream: Source = TeeSource(responseBody.source(), sideStream)
80
- if (alwaysReadResponseBody) upstream = DepletingSource(upstream)
81
-
82
- return response.newBuilder()
83
- .body(upstream.buffer().asResponseBody(contentType, contentLength))
84
- .build()
85
- }
86
-
87
- private fun createTempTransactionFile(): File? {
88
- val cache = cacheDirectoryProvider.provide()
89
- return if (cache == null) {
90
- Logger.warn("Failed to obtain a valid cache directory for transaction files")
91
- null
92
- } else {
93
- FileFactory.create(cache)
94
- }
95
- }
96
-
97
- private fun processPayload(
98
- response: Response,
99
- payload: Buffer,
100
- transaction: HttpTransaction,
101
- ) {
102
- val responseBody = response.body ?: return
103
-
104
- val contentType = responseBody.contentType()
105
-
106
- val isImageContentType = contentType?.toString()?.contains(CONTENT_TYPE_IMAGE, ignoreCase = true) == true
107
- if (isImageContentType) {
108
- if (payload.size < MAX_BLOB_SIZE) {
109
- transaction.responseImageData = payload.readByteArray()
110
- }
111
- } else if (payload.size != 0L) {
112
- val decodedContent = decodePayload(response, payload.readByteString())
113
- transaction.responseBody = decodedContent
114
- transaction.isResponseBodyEncoded = decodedContent == null
115
- }
116
- }
117
-
118
- private fun decodePayload(
119
- response: Response,
120
- body: ByteString,
121
- ) = bodyDecoders.asSequence()
122
- .mapNotNull { decoder ->
123
- try {
124
- decoder.decodeResponse(response, body)
125
- } catch (e: IOException) {
126
- Logger.warn("Decoder $decoder failed to process response payload", e)
127
- null
128
- }
129
- }.firstOrNull()
130
-
131
- private inner class ResponseReportingSinkCallback(
132
- private val response: Response,
133
- private val transaction: HttpTransaction,
134
- ) : ReportingSink.Callback {
135
- override fun onClosed(
136
- file: File?,
137
- sourceByteCount: Long,
138
- ) {
139
- file?.readResponsePayload()?.let { payload ->
140
- processPayload(response, payload, transaction)
141
- }
142
- transaction.responsePayloadSize = sourceByteCount
143
- collector.onResponseReceived(transaction)
144
- file?.delete()
145
- }
146
-
147
- override fun onFailure(
148
- file: File?,
149
- exception: java.io.IOException,
150
- ) {
151
- Logger.error("Failed to read response payload", exception)
152
- }
153
-
154
- private fun File.readResponsePayload() =
155
- try {
156
- source().uncompress(response.headers).use { source ->
157
- Buffer().apply { writeAll(source) }
158
- }
159
- } catch (e: java.io.IOException) {
160
- Logger.error("Response payload couldn't be processed", e)
161
- null
162
- }
163
- }
164
-
165
- private companion object {
166
- const val MAX_BLOB_SIZE = 1_000_000L
167
-
168
- const val CONTENT_TYPE_IMAGE = "image"
169
- }
170
- }
@@ -1,80 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import android.text.SpannableStringBuilder
4
- import android.text.Spanned
5
- import android.text.style.BackgroundColorSpan
6
- import android.text.style.ForegroundColorSpan
7
- import android.text.style.UnderlineSpan
8
- import java.util.regex.Pattern
9
-
10
- /**
11
- * Highlight parts of the String when it matches the search.
12
- *
13
- * @param search the text to highlight
14
- */
15
- internal fun SpannableStringBuilder.highlightWithDefinedColors(
16
- search: String,
17
- startIndices: List<Int>,
18
- backgroundColor: Int,
19
- foregroundColor: Int,
20
- ): SpannableStringBuilder {
21
- return applyColoredSpannable(this, startIndices, search.length, backgroundColor, foregroundColor)
22
- }
23
-
24
- internal fun CharSequence.indicesOf(input: String): List<Int> =
25
- Pattern.compile(input, Pattern.CASE_INSENSITIVE).toRegex()
26
- .findAll(this)
27
- .map { it.range.first }
28
- .toCollection(mutableListOf())
29
-
30
- internal fun SpannableStringBuilder.highlightWithDefinedColorsSubstring(
31
- search: String,
32
- startIndex: Int,
33
- backgroundColor: Int,
34
- foregroundColor: Int,
35
- ): SpannableStringBuilder {
36
- return applyColoredSpannableSubstring(this, startIndex, search.length, backgroundColor, foregroundColor)
37
- }
38
-
39
- private fun applyColoredSpannableSubstring(
40
- text: SpannableStringBuilder,
41
- subStringStartPosition: Int,
42
- subStringLength: Int,
43
- backgroundColor: Int,
44
- foregroundColor: Int,
45
- ): SpannableStringBuilder {
46
- return text.apply {
47
- setSpan(
48
- UnderlineSpan(),
49
- subStringStartPosition,
50
- subStringStartPosition + subStringLength,
51
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
52
- )
53
- setSpan(
54
- ForegroundColorSpan(foregroundColor),
55
- subStringStartPosition,
56
- subStringStartPosition + subStringLength,
57
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
58
- )
59
- setSpan(
60
- BackgroundColorSpan(backgroundColor),
61
- subStringStartPosition,
62
- subStringStartPosition + subStringLength,
63
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
64
- )
65
- }
66
- }
67
-
68
- private fun applyColoredSpannable(
69
- text: SpannableStringBuilder,
70
- indexes: List<Int>,
71
- length: Int,
72
- backgroundColor: Int,
73
- foregroundColor: Int,
74
- ): SpannableStringBuilder {
75
- return text.apply {
76
- indexes.forEach {
77
- applyColoredSpannableSubstring(text, it, length, backgroundColor, foregroundColor)
78
- }
79
- }
80
- }
@@ -1,86 +0,0 @@
1
- package com.securitysuite.internal.support
2
-
3
- import android.app.Activity
4
- import android.content.ClipData
5
- import android.content.Context
6
- import android.content.Intent
7
- import android.net.Uri
8
- import androidx.core.app.ShareCompat
9
- import androidx.core.content.FileProvider
10
- import kotlinx.coroutines.Dispatchers
11
- import kotlinx.coroutines.withContext
12
- import okio.BufferedSource
13
- import okio.Source
14
- import okio.buffer
15
- import okio.sink
16
-
17
- internal interface Sharable {
18
- fun toSharableContent(context: Context): Source
19
- }
20
-
21
- internal fun Sharable.toSharableUtf8Content(context: Context) =
22
- toSharableContent(context)
23
- .buffer()
24
- .use(BufferedSource::readUtf8)
25
-
26
- internal suspend fun Sharable.shareAsUtf8Text(
27
- activity: Activity,
28
- intentTitle: String,
29
- intentSubject: String,
30
- ): Intent {
31
- val content = withContext(Dispatchers.Default) { toSharableUtf8Content(activity) }
32
- return ShareCompat.IntentBuilder(activity)
33
- .setType("text/plain")
34
- .setChooserTitle(intentTitle)
35
- .setSubject(intentSubject)
36
- .setText(content)
37
- .createChooserIntent()
38
- }
39
-
40
- internal fun Sharable.writeToFile(
41
- context: Context,
42
- fileName: String,
43
- ): Uri? {
44
- val cache = context.cacheDir
45
- if (cache == null) {
46
- Logger.warn("Failed to obtain a valid cache directory for file export")
47
- return null
48
- }
49
-
50
- val file = FileFactory.create(cache, fileName)
51
- if (file == null) {
52
- Logger.warn("Failed to create an export file")
53
- return null
54
- }
55
-
56
- val fileContent = toSharableContent(context)
57
- file.sink().buffer().use { it.writeAll(fileContent) }
58
-
59
- return FileProvider.getUriForFile(
60
- context,
61
- "${context.packageName}.com.securitysuite.provider",
62
- file,
63
- )
64
- }
65
-
66
- internal fun Sharable.shareAsFile(
67
- activity: Activity,
68
- fileName: String,
69
- intentTitle: String,
70
- intentSubject: String,
71
- clipDataLabel: String,
72
- ): Intent? {
73
- val uri = writeToFile(activity, fileName) ?: return null
74
- val shareIntent =
75
- ShareCompat.IntentBuilder(activity)
76
- .setType(activity.contentResolver.getType(uri))
77
- .setChooserTitle(intentTitle)
78
- .setSubject(intentSubject)
79
- .setStream(uri)
80
- .intent
81
- shareIntent.apply {
82
- clipData = ClipData.newRawUri(clipDataLabel, uri)
83
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
84
- }
85
- return Intent.createChooser(shareIntent, intentTitle)
86
- }