Fix: Developer logs feature on older phones.

This also happens to address the security finding in issue #121 by putting logs directly into a file rather than scraping them from the device logs.
This commit is contained in:
Kevin Gorham 2020-10-09 11:54:07 -04:00
parent a0316ede08
commit 0039db8602
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
4 changed files with 45 additions and 31 deletions

View File

@ -10,6 +10,7 @@ import cash.z.ecc.android.lockbox.LockBox
import cash.z.ecc.android.sdk.ext.SilentTwig
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
import cash.z.ecc.android.sdk.ext.Twig
import cash.z.ecc.android.ui.util.DebugFileTwig
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
@ -53,7 +54,7 @@ class AppModule {
): FeedbackCoordinator {
return prefs.getBoolean(Const.Pref.FEEDBACK_ENABLED).let { isEnabled ->
// observe nothing unless feedback is enabled
Twig.plant(if (isEnabled) TroubleshootingTwig() else SilentTwig())
Twig.plant(if (isEnabled) DebugFileTwig() else SilentTwig())
FeedbackCoordinator(feedback, if (isEnabled) defaultObservers else setOf())
}
}

View File

@ -3,6 +3,9 @@ package cash.z.ecc.android.ext
import android.content.Context
import android.os.Build
import androidx.fragment.app.Fragment
import cash.z.ecc.android.sdk.ext.Bush
import cash.z.ecc.android.sdk.ext.CompositeTwig
import cash.z.ecc.android.sdk.ext.Twig
import cash.z.ecc.android.sdk.ext.twig
import java.util.*
@ -26,4 +29,10 @@ inline fun Context.locale(): Locale {
//noinspection deprecation
resources.configuration.locale
}
}
// TODO: add this to the SDK and if the trunk is a CompositeTwig, search through there before returning null
inline fun <reified T> Twig.find(): T? {
return if (Bush.trunk::class.java.isAssignableFrom(T::class.java)) Bush.trunk as T
else null
}

View File

@ -11,6 +11,7 @@ import cash.z.ecc.android.R
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.databinding.FragmentProfileBinding
import cash.z.ecc.android.di.viewmodel.viewModel
import cash.z.ecc.android.ext.find
import cash.z.ecc.android.ext.onClick
import cash.z.ecc.android.ext.onClickNavBack
import cash.z.ecc.android.ext.onClickNavTo
@ -18,14 +19,12 @@ import cash.z.ecc.android.feedback.FeedbackFile
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.UserFeedback
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.sdk.ext.Bush
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.util.DebugFileTwig
import kotlinx.coroutines.launch
import okio.Okio
import java.io.File
import java.io.IOException
import java.lang.IllegalArgumentException
class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
@ -70,7 +69,13 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
}
private fun onViewDevLogs() {
shareFile(writeLogcat())
developerLogFile().let {
if (it == null) {
mainActivity?.showSnackbar("Error: No developer log found!")
} else {
shareFile(it)
}
}
}
private fun shareFiles(vararg files: File?) {
@ -102,29 +107,7 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
return mainActivity?.feedbackCoordinator?.findObserver<FeedbackFile>()?.file
}
private fun loadLogFileAsText(): String? {
val feedbackFile: File = userLogFile() ?: return null
Okio.buffer(Okio.source(feedbackFile)).use {
return it.readUtf8()
}
}
private fun writeLogcat(): File? {
try {
// Note: the /logs directory has been configured as a file provider under @xml/file_paths which allows the temporary sharing of this file
val outputFile = File("${ZcashWalletApp.instance.filesDir}/logs", "developer_log.txt").also { it.parentFile.mkdirs() }
if (!outputFile.parentFile.isDirectory) {
// addresses security finding in issue #121
throw IllegalArgumentException("Invalid path: ${outputFile.parentFile}. Verify" +
" that the default files directory is not being manipulated.")
}
val cmd = arrayOf("/bin/sh", "-c", "logcat -v time -d | grep '@TWIG' > '${outputFile.absolutePath}'")
Runtime.getRuntime().exec(cmd)
return outputFile
} catch (e: IOException) {
e.printStackTrace()
twig("Failed to create log")
}
return null
private fun developerLogFile(): File? {
return Bush.trunk.find<DebugFileTwig>()?.file
}
}

View File

@ -0,0 +1,21 @@
package cash.z.ecc.android.ui.util
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
import okio.Okio
import java.io.File
class DebugFileTwig(fileName: String = "developer_log.txt") : TroubleshootingTwig() {
val file = File("${ZcashWalletApp.instance.filesDir}/logs", fileName)
override fun twig(logMessage: String) {
super.twig(logMessage)
appendToFile(formatter(logMessage))
}
private fun appendToFile(message: String) {
Okio.buffer(Okio.appendingSink(file)).use {
it.writeUtf8("$message\n")
}
}
}