diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt index a090b59fadb6860df156d971cc1056565ec12163..638bc4082866a671d4fa8b1222f3da2111faf573 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt @@ -5,12 +5,17 @@ import dagger.Provides import dagger.Reusable import dagger.multibindings.IntoSet import de.rki.coronawarnapp.bugreporting.censors.BugCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryEncounterCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryLocationCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryPersonCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryVisitCensor -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor -import de.rki.coronawarnapp.bugreporting.censors.RegistrationTokenCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryEncounterCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryLocationCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryPersonCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryVisitCensor +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.CheckInsCensor +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.TraceLocationCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.CoronaTestCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.PcrQrCodeCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.RACoronaTestCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.RatProfileCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.RatQrCodeCensor import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebugLoggerScope import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope import de.rki.coronawarnapp.bugreporting.debuglog.upload.server.LogUploadApiV1 @@ -71,11 +76,19 @@ class BugReportingSharedModule { @Provides @IntoSet - fun registrationTokenCensor(censor: RegistrationTokenCensor): BugCensor = censor + fun registrationTokenCensor(censor: CoronaTestCensor): BugCensor = censor @Provides @IntoSet - fun qrCodeCensor(censor: QRCodeCensor): BugCensor = censor + fun pcrQrCodeCensor(censor: PcrQrCodeCensor): BugCensor = censor + + @Provides + @IntoSet + fun ratQrCodeCensor(censor: RatQrCodeCensor): BugCensor = censor + + @Provides + @IntoSet + fun raCoronaTestCensor(censor: RACoronaTestCensor): BugCensor = censor @Provides @IntoSet @@ -92,4 +105,16 @@ class BugReportingSharedModule { @Provides @IntoSet fun diaryVisitCensor(censor: DiaryVisitCensor): BugCensor = censor + + @Provides + @IntoSet + fun checkInsCensor(censor: CheckInsCensor): BugCensor = censor + + @Provides + @IntoSet + fun traceLocationsCensor(censor: TraceLocationCensor): BugCensor = censor + + @Provides + @IntoSet + fun ratProfileCensor(censor: RatProfileCensor): BugCensor = censor } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt index 8be45d08b70e1fcf0dec7a11becd81b00a5cafaa..48c13a4a73e9de887be0621f210e447c91dd8759 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt @@ -38,6 +38,34 @@ interface BugCensor { return true } + fun withValidDescription(description: String?, action: (String) -> Unit): Boolean { + if (description.isNullOrBlank()) return false + if (description.length < 5) return false + action(description) + return true + } + + fun withValidAddress(address: String?, action: (String) -> Unit): Boolean { + if (address.isNullOrBlank()) return false + if (address.length < 4) return false + action(address) + return true + } + + fun withValidCity(city: String?, action: (String) -> Unit): Boolean { + if (city.isNullOrBlank()) return false + if (city.length < 3) return false + action(city) + return true + } + + fun withValidZipCode(zipCode: String?, action: (String) -> Unit): Boolean { + if (zipCode.isNullOrBlank()) return false + if (zipCode.length < 5) return false + action(zipCode) + return true + } + fun LogLine.toNewLogLineIfDifferent(newMessage: String): LogLine? { return if (newMessage != message) copy(message = newMessage) else null } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryEncounterCensor.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryEncounterCensor.kt index b8d3934117675dc62e9e8d29f607b42fcb172f36..9c75a723f8df9d5ff4cc8caa8c6efd13e75ecb3b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryEncounterCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidComment import de.rki.coronawarnapp.bugreporting.debuglog.LogLine diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryLocationCensor.kt similarity index 94% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryLocationCensor.kt index 24bf031cf63dff35797f0a0049b3fd133ce2ca8a..70628444cbaa4ed279be2e03adc54bb37bbc6c1e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryLocationCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidEmail import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryPersonCensor.kt similarity index 94% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryPersonCensor.kt index 42341a8b5fd687da53a5e63ed7e93ab99477f28c..a8a0056d95bc5027c1f79e3d4d3e0451cd0b41a7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryPersonCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidEmail import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryVisitCensor.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryVisitCensor.kt index ff0c63cf1100805d6a36e6ce74efff917445e4cc..f060c0fd2d49e2e415664e3dac90ad56937bbef5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryVisitCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/CheckInsCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/CheckInsCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..a5f10d81cb24ca19dbf32068e6e878b557ac84e0 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/CheckInsCensor.kt @@ -0,0 +1,55 @@ +package de.rki.coronawarnapp.bugreporting.censors.presencetracing + +import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidAddress +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidDescription +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope +import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +@Reusable +class CheckInsCensor @Inject constructor( + @DebuggerScope debugScope: CoroutineScope, + private val checkInRepository: CheckInRepository +) : BugCensor { + + private val checkInsFlow by lazy { + checkInRepository.allCheckIns.stateIn( + scope = debugScope, + started = SharingStarted.Lazily, + initialValue = null + ).filterNotNull() + } + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val checkIns = checkInsFlow.first() + + if (checkIns.isEmpty()) return null + + val newLogMsg = checkIns.fold(entry.message) { initial, checkIn -> + + var acc = initial + + withValidDescription(checkIn.description) { description -> + acc = acc.replace(description, "CheckIn#${checkIn.id}/Description") + } + + withValidAddress(checkIn.address) { address -> + acc = acc.replace(address, "CheckIn#${checkIn.id}/Address") + } + + acc + } + + return entry.toNewLogLineIfDifferent(newLogMsg) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/TraceLocationCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/TraceLocationCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..6d3f60ffb8bf2bc8dc61612b9722a9fc67fac64b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/TraceLocationCensor.kt @@ -0,0 +1,80 @@ +package de.rki.coronawarnapp.bugreporting.censors.presencetracing + +import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidAddress +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidDescription +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope +import de.rki.coronawarnapp.presencetracing.locations.TraceLocationUserInput +import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +/** + * Censors Trace Location Data + * + * The information about which data to censor comes from two places + * - traceLocationRepository, for traceLocations that are already stored + * - dataToCensor, which is set before a traceLocation is created; this is needed in cases when the app crashes between + * data input and storing + */ +@Reusable +class TraceLocationCensor @Inject constructor( + @DebuggerScope debugScope: CoroutineScope, + private val traceLocationRepository: TraceLocationRepository +) : BugCensor { + + private val traceLocationsFlow by lazy { + traceLocationRepository.allTraceLocations.stateIn( + scope = debugScope, + started = SharingStarted.Lazily, + initialValue = null + ).filterNotNull() + } + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val traceLocations = traceLocationsFlow.first() + + var newLogMsg = traceLocations.fold(entry.message) { initial, traceLocation -> + var acc = initial + + acc = acc.replace(traceLocation.type.name, "TraceLocation#${traceLocation.id}/Type") + + withValidDescription(traceLocation.description) { description -> + acc = acc.replace(description, "TraceLocation#${traceLocation.id}/Description") + } + + withValidAddress(traceLocation.address) { address -> + acc = acc.replace(address, "TraceLocation#${traceLocation.id}/Address") + } + + acc + } + + val inputDataToCensor = dataToCensor + if (inputDataToCensor != null) { + newLogMsg = newLogMsg.replace(inputDataToCensor.type.name, "TraceLocationUserInput#Type") + + withValidDescription(inputDataToCensor.description) { + newLogMsg = newLogMsg.replace(inputDataToCensor.description, "TraceLocationUserInput#Description") + } + + withValidAddress(inputDataToCensor.address) { + newLogMsg = newLogMsg.replace(inputDataToCensor.address, "TraceLocationUserInput#Address") + } + } + + return entry.toNewLogLineIfDifferent(newLogMsg) + } + + companion object { + var dataToCensor: TraceLocationUserInput? = null + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/CoronaTestCensor.kt similarity index 56% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/CoronaTestCensor.kt index c8769d23087ff67a5d92401421161273d206e739..df934b7e678642cc9e1eff255b67473bfe70af25 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/CoronaTestCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.submission import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.coronatest.CoronaTestRepository @@ -9,17 +10,25 @@ import kotlinx.coroutines.flow.first import javax.inject.Inject @Reusable -class RegistrationTokenCensor @Inject constructor( +class CoronaTestCensor @Inject constructor( private val coronaTestRepository: CoronaTestRepository, ) : BugCensor { + + // Keep a history to have references even after the user deletes a test + private val tokenHistory = mutableSetOf<String>() + private val identifierHistory = mutableSetOf<String>() + override suspend fun checkLog(entry: LogLine): LogLine? { + + // The Registration Token is received after registration of PCR and RAT tests. It is required to poll the test result. val tokens = coronaTestRepository.coronaTests.first().map { it.registrationToken } + tokenHistory.addAll(tokens) - if (tokens.isEmpty()) return null + val identifiers = coronaTestRepository.coronaTests.first().map { it.identifier } + identifierHistory.addAll(identifiers) var newMessage = entry.message - - for (token in tokens) { + for (token in tokenHistory) { if (!entry.message.contains(token)) continue newMessage = if (CWADebug.isDeviceForTestersBuild) { @@ -29,6 +38,12 @@ class RegistrationTokenCensor @Inject constructor( } } + identifierHistory + .filter { entry.message.contains(it) } + .forEach { + newMessage = newMessage.replace(it, "${it.take(11)}CoronaTest/Identifier") + } + return entry.toNewLogLineIfDifferent(newMessage) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/PcrQrCodeCensor.kt similarity index 83% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/PcrQrCodeCensor.kt index b85888d1d02ba6fb39e379e7740c1fb68aefe39a..3880817e6806f408469f061cf7b2a2f682aab1c0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/PcrQrCodeCensor.kt @@ -1,13 +1,14 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.submission import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.util.CWADebug import javax.inject.Inject @Reusable -class QRCodeCensor @Inject constructor() : BugCensor { +class PcrQrCodeCensor @Inject constructor() : BugCensor { override suspend fun checkLog(entry: LogLine): LogLine? { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RACoronaTestCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RACoronaTestCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..38c06dca4ae6ed269f293e30b47948baeae28467 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RACoronaTestCensor.kt @@ -0,0 +1,57 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import org.joda.time.format.DateTimeFormat +import javax.inject.Inject + +class RACoronaTestCensor @Inject constructor( + @DebuggerScope debugScope: CoroutineScope, + private val coronaTestRepository: CoronaTestRepository +) : BugCensor { + + private val dayOfBirthFormatter = DateTimeFormat.forPattern("yyyy-MM-dd") + + private val coronaTestFlow by lazy { + coronaTestRepository.coronaTests.stateIn( + scope = debugScope, + started = SharingStarted.Lazily, + initialValue = null + ).filterNotNull() + } + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val raCoronaTestFlow = coronaTestFlow.map { tests -> tests.filterIsInstance<RACoronaTest>() }.first() + val raCoronaTest = raCoronaTestFlow.firstOrNull() ?: return null + + var newMessage = entry.message + + with(raCoronaTest) { + withValidName(firstName) { firstName -> + newMessage = newMessage.replace(firstName, "RATest/FirstName") + } + + withValidName(lastName) { lastName -> + newMessage = newMessage.replace(lastName, "RATest/LastName") + } + + val dateOfBirthString = dateOfBirth?.toString(dayOfBirthFormatter) ?: return@with + + newMessage = newMessage.replace(dateOfBirthString, "RATest/DateOfBirth") + } + + return entry.toNewLogLineIfDifferent(newMessage) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..fa1e919bbeeccadfded6038b795001cee462cf34 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensor.kt @@ -0,0 +1,74 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidAddress +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidCity +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidPhoneNumber +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidZipCode +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import kotlinx.coroutines.flow.first +import org.joda.time.format.DateTimeFormat +import javax.inject.Inject + +class RatProfileCensor @Inject constructor( + private val ratProfileSettings: RATProfileSettings +) : BugCensor { + + private val dayOfBirthFormatter = DateTimeFormat.forPattern("yyyy-MM-dd") + private val ratProfileHistory = mutableSetOf<RATProfile>() + + override suspend fun checkLog(entry: LogLine): LogLine? { + val ratProfile = ratProfileSettings.profile.flow.first() + + // store the profile in a property so we still have a reference after it was deleted by the user + if (ratProfile != null) { + ratProfileHistory.add(ratProfile) + } + + var newMessage = entry.message + + ratProfileHistory.forEach { profile -> + with(profile) { + withValidName(firstName) { firstName -> + newMessage = newMessage.replace(firstName, "RAT-Profile/FirstName") + } + + withValidName(lastName) { lastName -> + newMessage = newMessage.replace(lastName, "RAT-Profile/LastName") + } + + val dateOfBirthString = birthDate?.toString(dayOfBirthFormatter) + + if (dateOfBirthString != null) { + newMessage = newMessage.replace(dateOfBirthString, "RAT-Profile/DateOfBirth") + } + + withValidAddress(street) { street -> + newMessage = newMessage.replace(street, "RAT-Profile/Street") + } + + withValidCity(city) { city -> + newMessage = newMessage.replace(city, "RAT-Profile/City") + } + + withValidZipCode(zipCode) { zipCode -> + newMessage = newMessage.replace(zipCode, "RAT-Profile/Zip-Code") + } + + withValidPhoneNumber(phone) { phone -> + newMessage = newMessage.replace(phone, "RAT-Profile/Phone") + } + + withValidPhoneNumber(email) { phone -> + newMessage = newMessage.replace(phone, "RAT-Profile/eMail") + } + } + } + + return entry.toNewLogLineIfDifferent(newMessage) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatQrCodeCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatQrCodeCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..8fe3c62f3d18c6b0c0600fe71f000c0f8090b831 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatQrCodeCensor.kt @@ -0,0 +1,63 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.qrcode.RapidAntigenHash +import de.rki.coronawarnapp.util.CWADebug +import org.joda.time.LocalDate +import org.joda.time.format.DateTimeFormat +import javax.inject.Inject + +@Reusable +class RatQrCodeCensor @Inject constructor() : BugCensor { + + private val dayOfBirthFormatter = DateTimeFormat.forPattern("yyyy-MM-dd") + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val dataToCensor = dataToCensor ?: return null + + var newMessage = entry.message + + with(dataToCensor) { + newMessage = newMessage.replace(rawString, "RatQrCode/ScannedRawString") + + newMessage = if (CWADebug.isDeviceForTestersBuild) { + newMessage.replace(hash, PLACEHOLDER + hash.takeLast(28)) + } else { + newMessage.replace(hash, PLACEHOLDER + hash.takeLast(4)) + } + + withValidName(firstName) { firstName -> + newMessage = newMessage.replace(firstName, "RATest/FirstName") + } + + withValidName(lastName) { lastName -> + newMessage = newMessage.replace(lastName, "RATest/LastName") + } + + val dateOfBirthString = dateOfBirth?.toString(dayOfBirthFormatter) ?: return@with + + newMessage = newMessage.replace(dateOfBirthString, "RATest/DateOfBirth") + } + + return entry.toNewLogLineIfDifferent(newMessage) + } + + companion object { + var dataToCensor: CensorData? = null + + private const val PLACEHOLDER = "SHA256HASH-ENDING-WITH-" + } + + data class CensorData( + val rawString: String, + val hash: RapidAntigenHash, + val firstName: String?, + val lastName: String?, + val dateOfBirth: LocalDate? + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt index 53d57593af693c5fa6cde7db77d4266f26edda1a..49633e3decd06846753a897897b235a16409cbdd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.coronatest.qrcode +import de.rki.coronawarnapp.bugreporting.censors.submission.PcrQrCodeCensor import java.util.regex.Pattern import javax.inject.Inject @@ -8,9 +9,9 @@ class PcrQrCodeExtractor @Inject constructor() : QrCodeExtractor<CoronaTestQRCod override fun canHandle(rawString: String): Boolean = rawString.startsWith(prefix, ignoreCase = true) override fun extract(rawString: String): CoronaTestQRCode.PCR { - return CoronaTestQRCode.PCR( - extractGUID(rawString) - ) + val guid = extractGUID(rawString) + PcrQrCodeCensor.lastGUID = guid + return CoronaTestQRCode.PCR(guid) } private fun extractGUID(rawString: String): CoronaTestGUID { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt index c1a95544ce0b759c88f7dc2f792d35c17cbcd194..c5881f97364a97bd6c5f8c795b3b1df7c1c72df0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.coronatest.qrcode import com.google.common.io.BaseEncoding import com.google.gson.Gson import com.google.gson.annotations.SerializedName +import de.rki.coronawarnapp.bugreporting.censors.submission.RatQrCodeCensor import de.rki.coronawarnapp.util.HashExtensions.toSHA256 import de.rki.coronawarnapp.util.hashing.isSha256Hash import de.rki.coronawarnapp.util.serialization.fromJson @@ -22,6 +23,14 @@ class RapidAntigenQrCodeExtractor @Inject constructor() : QrCodeExtractor<Corona Timber.v("extract(rawString=%s)", rawString) val payload = CleanPayload(extractData(rawString)) + RatQrCodeCensor.dataToCensor = RatQrCodeCensor.CensorData( + rawString = rawString, + hash = payload.hash, + firstName = payload.firstName, + lastName = payload.lastName, + dateOfBirth = payload.dateOfBirth + ) + payload.requireValidData() return CoronaTestQRCode.RapidAntigen( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt index 29976a36cb93c1afb1b495e72df06335aca4aab1..a753ca9424cfd7cc3e55a74b383adc8f5776627c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt @@ -8,6 +8,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.TraceLocationCensor import de.rki.coronawarnapp.contactdiary.util.CWADateTimeFormatPatternFactory.shortDatePattern import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.presencetracing.locations.TraceLocationCreator @@ -68,6 +69,8 @@ class TraceLocationCreateViewModel @AssistedInject constructor( defaultCheckInLengthInMinutes = checkInLength.standardMinutes.toInt() ) + TraceLocationCensor.dataToCensor = userInput + launch { try { val traceLocation = traceLocationCreator.createTraceLocation(userInput) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt index 69b7c6e662c9b7a071f9c2735f2ab6809f753455..f6876b62aeebd0f5662569e1a26073a0fd7e8e66 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.asLiveData import com.google.android.gms.common.api.ApiException import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQrCodeValidator import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException import de.rki.coronawarnapp.nearby.modules.tekhistory.TEKHistoryProvider @@ -79,8 +78,6 @@ class SubmissionConsentViewModel @AssistedInject constructor( private suspend fun validateAndRegister(qrCodeString: String) { try { val coronaTestQRCode = qrCodeValidator.validate(qrCodeString) - // TODO this needs to be adapted to work for different types - QRCodeCensor.lastGUID = coronaTestQRCode.registrationIdentifier qrCodeValidationState.postValue(QrCodeRegistrationStateProcessor.ValidationState.SUCCESS) val coronaTest = submissionRepository.testForType(coronaTestQRCode.type).first() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt index b49dad70291cd84b7433f48fac0d66a8bcc99720..4222a676fd91f1b08fe2e5dd4987fd02629c090f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt @@ -3,7 +3,6 @@ package de.rki.coronawarnapp.ui.submission.qrcode.scan import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQrCodeValidator import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException import de.rki.coronawarnapp.coronatest.type.CoronaTest @@ -43,8 +42,6 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor( suspend fun startQrCodeRegistration(rawResult: String, isConsentGiven: Boolean) { try { val coronaTestQRCode = qrCodeValidator.validate(rawResult) - // TODO this needs to be adapted to work for different types - QRCodeCensor.lastGUID = coronaTestQRCode.registrationIdentifier qrCodeValidationState.postValue(QrCodeRegistrationStateProcessor.ValidationState.SUCCESS) val coronaTest = submissionRepository.testForType(coronaTestQRCode.type).first() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt index c0680a991b0dae9908b46274bac5b5bbd4988276..988042877fb6032ffa037c2ee5957c30ec905798 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt @@ -54,6 +54,51 @@ class BugCensorTest : BaseTest() { BugCensor.withValidComment("abc") {} shouldBe true } + @Test + fun `description censoring validity`() { + BugCensor.withValidDescription(null) {} shouldBe false + BugCensor.withValidDescription(" ") {} shouldBe false + BugCensor.withValidDescription(" ") {} shouldBe false + BugCensor.withValidDescription("a") {} shouldBe false + BugCensor.withValidDescription("ab") {} shouldBe false + BugCensor.withValidDescription("abc") {} shouldBe false + BugCensor.withValidDescription("abcd") {} shouldBe false + BugCensor.withValidDescription("abcde") {} shouldBe true + } + + @Test + fun `address censoring validity`() { + BugCensor.withValidAddress(null) {} shouldBe false + BugCensor.withValidAddress(" ") {} shouldBe false + BugCensor.withValidAddress(" ") {} shouldBe false + BugCensor.withValidAddress("a") {} shouldBe false + BugCensor.withValidAddress("ab") {} shouldBe false + BugCensor.withValidAddress("abc") {} shouldBe false + BugCensor.withValidAddress("abcd") {} shouldBe true + } + + @Test + fun `city censoring validity`() { + BugCensor.withValidCity(null) {} shouldBe false + BugCensor.withValidCity(" ") {} shouldBe false + BugCensor.withValidCity(" ") {} shouldBe false + BugCensor.withValidCity("a") {} shouldBe false + BugCensor.withValidCity("ab") {} shouldBe false + BugCensor.withValidCity("abc") {} shouldBe true + } + + @Test + fun `zip-code censoring validity`() { + BugCensor.withValidZipCode(null) {} shouldBe false + BugCensor.withValidZipCode(" ") {} shouldBe false + BugCensor.withValidZipCode(" ") {} shouldBe false + BugCensor.withValidZipCode("1") {} shouldBe false + BugCensor.withValidZipCode("12") {} shouldBe false + BugCensor.withValidZipCode("123") {} shouldBe false + BugCensor.withValidZipCode("1234") {} shouldBe false + BugCensor.withValidZipCode("12345") {} shouldBe true + } + @Test fun `loglines are only copied if the message is different`() { val logLine = LogLine( diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt index e6c3aa4a1df8bf4db40f25f4eb488cf3ce9150df..cf4989e79c22668f3f03905bd2d0845dd589b5f8 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt @@ -6,6 +6,9 @@ import dagger.Provides import de.rki.coronawarnapp.bugreporting.BugReportingSharedModule import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository import de.rki.coronawarnapp.submission.SubmissionSettings import io.github.classgraph.ClassGraph import io.kotest.matchers.collections.shouldContainAll @@ -73,4 +76,16 @@ class MockProvider { @Singleton @Provides fun coronaTestRepository(): CoronaTestRepository = mockk() + + @Singleton + @Provides + fun checkInRepository(): CheckInRepository = mockk() + + @Singleton + @Provides + fun traceLocationRepository(): TraceLocationRepository = mockk() + + @Singleton + @Provides + fun ratProfileSettings(): RATProfileSettings = mockk() } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CheckInsCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CheckInsCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..818cdbb8e7e03ed2b86cb1cfce7ea7c8e59a0245 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CheckInsCensorTest.kt @@ -0,0 +1,139 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.CheckInsCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.presencetracing.checkins.CheckIn +import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class CheckInsCensorTest : BaseTest() { + + @MockK lateinit var checkInsRepo: CheckInRepository + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + private fun createInstance(scope: CoroutineScope) = CheckInsCensor( + debugScope = scope, + checkInRepository = checkInsRepo + ) + + private fun mockCheckIn( + checkInId: Long, + checkInDescription: String, + checkInAddress: String + ) = mockk<CheckIn>().apply { + every { id } returns checkInId + every { description } returns checkInDescription + every { address } returns checkInAddress + } + + @Test + fun `checkLog() should return LogLine with censored check-in information`() = runBlocking { + every { checkInsRepo.allCheckIns } returns flowOf( + listOf( + mockCheckIn( + checkInId = 1, + checkInDescription = "Moe's Tavern", + checkInAddress = "Near 742 Evergreen Terrace, 12345 Springfield" + ), + mockCheckIn( + checkInId = 2, + checkInDescription = "Kwik-E-Mart", + checkInAddress = "Some Street, 12345 Springfield" + ) + ) + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + Let's go to Moe's Tavern in Near 742 Evergreen Terrace, 12345 Springfield. + Who needs the Kwik-E-Mart in Some Street, 12345 Springfield? I doooo! + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Let's go to CheckIn#1/Description in CheckIn#1/Address. + Who needs the CheckIn#2/Description in CheckIn#2/Address? I doooo! + """.trimIndent() + ) + + // censoring should still work after user deletes his check-ins + every { checkInsRepo.allCheckIns } returns flowOf(emptyList()) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Let's go to CheckIn#1/Description in CheckIn#1/Address. + Who needs the CheckIn#2/Description in CheckIn#2/Address? I doooo! + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return null if no check-ins are stored`() = runBlocking { + every { checkInsRepo.allCheckIns } returns flowOf(emptyList()) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Some log message that shouldn't be censored.", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlocking { + + every { checkInsRepo.allCheckIns } returns flowOf( + listOf( + mockCheckIn( + checkInId = 1, + checkInDescription = "Description 1", + checkInAddress = "Address 1" + ), + mockCheckIn( + checkInId = 2, + checkInDescription = "Description 2", + checkInAddress = "Address 2" + ) + ) + ) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Some log message that shouldn't be censored.", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CoronaTestCensorTest.kt similarity index 54% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CoronaTestCensorTest.kt index b3fde2e4884541ee4687eaa2afd73854c3e0d2bb..0b88f1988898763b4688396469c4dccad04c8b47 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CoronaTestCensorTest.kt @@ -1,8 +1,11 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.submission.CoronaTestCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest import de.rki.coronawarnapp.util.CWADebug import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations @@ -17,15 +20,22 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest -class RegistrationTokenCensorTest : BaseTest() { +class CoronaTestCensorTest : BaseTest() { @MockK lateinit var coronaTestRepository: CoronaTestRepository private val testToken = "63b4d3ff-e0de-4bd4-90c1-17c2bb683a2f" + private val pcrIdentifier = "qrcode-pcr-someIdentifier" + private val ratIdentifier = "qrcode-rat-someIdentifier" private val coronaTests: MutableStateFlow<Set<CoronaTest>> = MutableStateFlow( setOf( - mockk<CoronaTest>().apply { + mockk<PCRCoronaTest>().apply { every { registrationToken } returns testToken + every { identifier } returns pcrIdentifier + }, + mockk<RACoronaTest>().apply { + every { registrationToken } returns testToken + every { identifier } returns ratIdentifier } ) ) @@ -40,7 +50,7 @@ class RegistrationTokenCensorTest : BaseTest() { every { coronaTestRepository.coronaTests } returns coronaTests } - private fun createInstance() = RegistrationTokenCensor( + private fun createInstance() = CoronaTestCensor( coronaTestRepository = coronaTestRepository ) @@ -50,31 +60,31 @@ class RegistrationTokenCensorTest : BaseTest() { val filterMe = LogLine( timestamp = 1, priority = 3, - message = "I'm a shy registration token: $testToken", + message = "I'm a shy registration token: $testToken and we are extrovert $pcrIdentifier and $ratIdentifier", tag = "I'm a tag", throwable = null ) instance.checkLog(filterMe) shouldBe filterMe.copy( - message = "I'm a shy registration token: ########-####-####-####-########3a2f" + message = "I'm a shy registration token: ########-####-####-####-########3a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" ) every { CWADebug.isDeviceForTestersBuild } returns true instance.checkLog(filterMe) shouldBe filterMe.copy( - message = "I'm a shy registration token: ########-e0de-4bd4-90c1-17c2bb683a2f" + message = "I'm a shy registration token: ########-e0de-4bd4-90c1-17c2bb683a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" ) verify { coronaTestRepository.coronaTests } } @Test - fun `censoring returns null if there is no token`() = runBlockingTest { + fun `censoring returns null if there is no corona test stored`() = runBlockingTest { coronaTests.value = emptySet() val instance = createInstance() val filterMeNot = LogLine( timestamp = 1, priority = 3, - message = "I'm a shy registration token: $testToken", + message = "I'm a shy registration token: $testToken and we are extrovert $pcrIdentifier and $ratIdentifier", tag = "I'm a tag", throwable = null ) @@ -93,4 +103,29 @@ class RegistrationTokenCensorTest : BaseTest() { ) instance.checkLog(filterMeNot) shouldBe null } + + @Test + fun `censoring still works after test was deleted`() = runBlockingTest { + + val censor = createInstance() + + val filterMe = LogLine( + timestamp = 1, + priority = 3, + message = "I'm a shy registration token: $testToken and we are extrovert $pcrIdentifier and $ratIdentifier", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(filterMe) shouldBe filterMe.copy( + message = "I'm a shy registration token: ########-####-####-####-########3a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" + ) + + // delete all tests + coronaTests.value = emptySet() + + censor.checkLog(filterMe) shouldBe filterMe.copy( + message = "I'm a shy registration token: ########-####-####-####-########3a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" + ) + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt index a00968c428c93e723309761c38e08af1634ce905..fead49fe948f4b9fc1d76262ba6fe22651ba2830 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryEncounterCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -71,6 +72,17 @@ class DiaryEncounterCensorTest : BaseTest() { everyone disliked that. """.trimIndent() ) + + // censoring should still work after encounters are deleted + every { diaryRepo.personEncounters } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + On Encounter#2/Circumstances, + two persons Encounter#3/Circumstances, + everyone disliked that. + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt index 0c80fee73ed0eb5e43ee044c80a43214c73047d2..191afcd5dcace16a5dce810c8aeeb8b6a9d2cbf4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryLocationCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -73,6 +74,17 @@ class DiaryLocationCensorTest : BaseTest() { and that Location#2/Name doesn't exist as it has neither phonenumber (null) nor email (null). """.trimIndent() ) + + // censoring should still work after locations are deleted + every { diaryRepo.locations } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + Bürgermeister of Location#1/Name (Location#1/PhoneNumber) and Karl of Location#3/Name [Location#3/PhoneNumber] called each other. + Both agreed that their emails (Location#1/EMail|Location#3/EMail) are awesome, + and that Location#2/Name doesn't exist as it has neither phonenumber (null) nor email (null). + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt index e4e5e38c294d6f3b384f6a8e7191f725a29ed441..1025df09c8876fd49a7e11f73c47603a77cb265f 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryPersonCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -74,6 +75,17 @@ class DiaryPersonCensorTest : BaseTest() { A quick mail to Person#1/EMail confirmed this. """.trimIndent() ) + + // censoring should still work after people are deleted + every { diaryRepo.people } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + Person#2/Name requested more coffee from Person#1/PhoneNumber, + but Person#3/Name thought he had enough has had enough for today. + A quick mail to Person#1/EMail confirmed this. + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt index cb9405722fde29918cb4d4a03fff8413fd518bea..5c8a0ed5155bd74d96a1f685b60fbc362ab68ccb 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryVisitCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -69,6 +70,17 @@ class DiaryVisitCensorTest : BaseTest() { only to find out the supermarket was Visit#3/Circumstances. """.trimIndent() ) + + // censoring should still work even after visits are deleted + every { diaryRepo.locationVisits } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + After having a Visit#1/Circumstances, + I got my Visit#2/Circumstances, + only to find out the supermarket was Visit#3/Circumstances. + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/PcrQrCodeCensorTest.kt similarity index 86% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/PcrQrCodeCensorTest.kt index 62bf33f114bc5d0051b5d9fa0fafa92264c01417..d89f76791836507ba93d111cbf1d10fb776b7743 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/PcrQrCodeCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.submission.PcrQrCodeCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.util.CWADebug import io.kotest.matchers.shouldBe @@ -12,7 +13,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest -class QRCodeCensorTest : BaseTest() { +class PcrQrCodeCensorTest : BaseTest() { private val testGUID = "63b4d3ff-e0de-4bd4-90c1-17c2bb683a2f" @@ -26,14 +27,14 @@ class QRCodeCensorTest : BaseTest() { @AfterEach fun teardown() { - QRCodeCensor.lastGUID = null + PcrQrCodeCensor.lastGUID = null } - private fun createInstance() = QRCodeCensor() + private fun createInstance() = PcrQrCodeCensor() @Test fun `censoring replaces the logline message`() = runBlockingTest { - QRCodeCensor.lastGUID = testGUID + PcrQrCodeCensor.lastGUID = testGUID val instance = createInstance() val censored = LogLine( timestamp = 1, @@ -54,7 +55,7 @@ class QRCodeCensorTest : BaseTest() { @Test fun `censoring returns null if there is no match`() = runBlockingTest { - QRCodeCensor.lastGUID = testGUID.replace("f", "a") + PcrQrCodeCensor.lastGUID = testGUID.replace("f", "a") val instance = createInstance() val notCensored = LogLine( timestamp = 1, @@ -68,7 +69,7 @@ class QRCodeCensorTest : BaseTest() { @Test fun `censoring aborts if no qrcode was set`() = runBlockingTest { - QRCodeCensor.lastGUID = null + PcrQrCodeCensor.lastGUID = null val instance = createInstance() val notCensored = LogLine( timestamp = 1, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RACoronaTestCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RACoronaTestCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..74c66acfadeafe2a4252b0a94df2457ebd7806bc --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RACoronaTestCensorTest.kt @@ -0,0 +1,121 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.submission.RACoronaTestCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.joda.time.LocalDate +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class RACoronaTestCensorTest : BaseTest() { + + @MockK lateinit var coronaTestRepository: CoronaTestRepository + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + private fun createInstance(scope: CoroutineScope) = RACoronaTestCensor( + debugScope = scope, + coronaTestRepository = coronaTestRepository + ) + + @Test + fun `checkLog() should return censored LogLine`() = runBlocking { + every { coronaTestRepository.coronaTests } returns flowOf( + setOf( + mockk<RACoronaTest>().apply { + every { firstName } returns "John" + every { lastName } returns "Doe" + every { dateOfBirth } returns LocalDate.parse("2020-01-01") + } + ) + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + Hello! My name is John. My friends call me Mister Doe and I was born on 2020-01-01. + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Hello! My name is RATest/FirstName. My friends call me Mister RATest/LastName and I was born on RATest/DateOfBirth. + """.trimIndent() + ) + + // censoring should still work when test gets deleted + every { coronaTestRepository.coronaTests } returns flowOf(emptySet()) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Hello! My name is RATest/FirstName. My friends call me Mister RATest/LastName and I was born on RATest/DateOfBirth. + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return return null if no corona tests are stored`() = runBlocking { + every { coronaTestRepository.coronaTests } returns flowOf(emptySet()) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlocking { + every { coronaTestRepository.coronaTests } returns flowOf( + setOf( + mockk<RACoronaTest>().apply { + every { firstName } returns "John" + every { lastName } returns "Doe" + every { dateOfBirth } returns LocalDate.parse("2020-01-01") + } + ) + ) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `censoring should still work when test gets deleted`() { + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RatQrCodeCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RatQrCodeCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..83b890cd184c083563c5b1c517362e2cd3768d68 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RatQrCodeCensorTest.kt @@ -0,0 +1,103 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.submission.RatQrCodeCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.util.CWADebug +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.mockkObject +import kotlinx.coroutines.test.runBlockingTest +import org.joda.time.LocalDate +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class RatQrCodeCensorTest { + + private val testHash = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + private val testRawString = "testRawString" + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + + mockkObject(CWADebug) + every { CWADebug.isDeviceForTestersBuild } returns false + } + + @AfterEach + fun teardown() { + RatQrCodeCensor.dataToCensor = null + } + + private fun createInstance() = RatQrCodeCensor() + + @Test + fun `checkLog() should return censored LogLine`() = runBlockingTest { + RatQrCodeCensor.dataToCensor = RatQrCodeCensor.CensorData( + rawString = testRawString, + hash = testHash, + firstName = "Milhouse", + lastName = "Van Houten", + dateOfBirth = LocalDate.parse("1980-07-01") + ) + + val censor = createInstance() + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = "Here comes the hash: $testHash of the rat test of Milhouse Van Houten. He was born on 1980-07-01", + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = "Here comes the hash: SHA256HASH-ENDING-WITH-15ad of the rat test of RATest/FirstName RATest/LastName. He was born on RATest/DateOfBirth" + ) + + every { CWADebug.isDeviceForTestersBuild } returns true + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = "Here comes the hash: SHA256HASH-ENDING-WITH-61a396177a9cb410ff61f20015ad of the rat test of RATest/FirstName RATest/LastName. He was born on RATest/DateOfBirth" + ) + } + + @Test + fun `checkLog() should return null if no data to censor was set`() = runBlockingTest { + val censor = createInstance() + + val logLineNotToCensor = LogLine( + timestamp = 1, + priority = 3, + message = "Here comes the hash: $testHash", + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineNotToCensor) shouldBe null + } + + @Test + fun `checkLog() should return null if nothing should be censored`() = runBlockingTest { + RatQrCodeCensor.dataToCensor = RatQrCodeCensor.CensorData( + rawString = testRawString, + hash = testHash.replace("8", "9"), + firstName = null, + lastName = null, + dateOfBirth = null + ) + + val censor = createInstance() + + val logLineNotToCensor = LogLine( + timestamp = 1, + priority = 3, + message = "Here comes the hash: $testHash", + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineNotToCensor) shouldBe null + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/TraceLocationCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/TraceLocationCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..c7f6d6b7f41e7d877a2a0842a8051153fc765a8c --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/TraceLocationCensorTest.kt @@ -0,0 +1,189 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.TraceLocationCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.presencetracing.locations.TraceLocationUserInput +import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class TraceLocationCensorTest : BaseTest() { + + @MockK lateinit var traceLocationRepo: TraceLocationRepository + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + } + + @AfterEach + fun teardown() { + TraceLocationCensor.dataToCensor = null + } + + private fun createInstance(scope: CoroutineScope) = TraceLocationCensor( + debugScope = scope, + traceLocationRepository = traceLocationRepo + ) + + private fun mockTraceLocation( + traceLocationId: Long, + traceLocationType: TraceLocationOuterClass.TraceLocationType, + traceLocationDescription: String, + traceLocationAddress: String, + ) = mockk<TraceLocation>().apply { + every { id } returns traceLocationId + every { type } returns traceLocationType + every { description } returns traceLocationDescription + every { address } returns traceLocationAddress + } + + @Test + fun `checkLog() should return LogLine with censored trace location information from repository`() = runBlocking { + every { traceLocationRepo.allTraceLocations } returns flowOf( + listOf( + mockTraceLocation( + traceLocationId = 1, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_FOOD_SERVICE, + traceLocationDescription = "Sushi Place", + traceLocationAddress = "Sushi Street 123, 12345 Fish Town" + ), + mockTraceLocation( + traceLocationId = 2, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, + traceLocationDescription = "Rick Astley Concert", + traceLocationAddress = "Never gonna give you up street 1, 12345 RickRoll City" + ) + ) + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + The type is LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT. Yesterday we went to the Rick Astley Concert. The spectacle took place in Never gonna give you up street 1, 12345 RickRoll City. + Afterwards we had some food in Sushi Place in Sushi Street 123, 12345 Fish Town. It a nice LOCATION_TYPE_PERMANENT_FOOD_SERVICE. + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + The type is TraceLocation#2/Type. Yesterday we went to the TraceLocation#2/Description. The spectacle took place in TraceLocation#2/Address. + Afterwards we had some food in TraceLocation#1/Description in TraceLocation#1/Address. It a nice TraceLocation#1/Type. + """.trimIndent() + ) + + // censoring should still work after the user deletes his trace locations + every { traceLocationRepo.allTraceLocations } returns flowOf(emptyList()) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + The type is TraceLocation#2/Type. Yesterday we went to the TraceLocation#2/Description. The spectacle took place in TraceLocation#2/Address. + Afterwards we had some food in TraceLocation#1/Description in TraceLocation#1/Address. It a nice TraceLocation#1/Type. + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return LogLine with censored trace location information from companion object`() = + runBlocking { + every { traceLocationRepo.allTraceLocations } returns flowOf(emptyList()) + TraceLocationCensor.dataToCensor = TraceLocationUserInput( + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT, + description = "Top Secret Private Event", + address = "top secret address", + startDate = null, + endDate = null, + defaultCheckInLengthInMinutes = 180 + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + The user just created a new traceLocation with Top Secret Private Event as the description and + top secret address as the address. The type is LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT. + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + The user just created a new traceLocation with TraceLocationUserInput#Description as the description and + TraceLocationUserInput#Address as the address. The type is TraceLocationUserInput#Type. + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return null if no trace locations are stored`() = runBlockingTest { + every { traceLocationRepo.allTraceLocations } returns flowOf(emptyList()) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlockingTest { + + every { traceLocationRepo.allTraceLocations } returns flowOf( + listOf( + mockTraceLocation( + traceLocationId = 1, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, + traceLocationDescription = "Description 1", + traceLocationAddress = "Address 1" + ), + mockTraceLocation( + traceLocationId = 2, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, + traceLocationDescription = "Description 2", + traceLocationAddress = "Address 2" + ) + ) + ) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..f3c887739c3fc236ec281c5a69b8db69bf5f06a8 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensorTest.kt @@ -0,0 +1,106 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.joda.time.format.DateTimeFormat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class RatProfileCensorTest : BaseTest() { + + @MockK lateinit var ratProfileSettings: RATProfileSettings + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + private fun createInstance(scope: CoroutineScope) = RatProfileCensor( + ratProfileSettings = ratProfileSettings + ) + + @Test + fun `checkLog() should return null if no RAT profile is stored`() = runBlocking { + every { ratProfileSettings.profile.flow } returns flowOf(null) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlocking { + every { ratProfileSettings.profile.flow } returns flowOf(profile) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return censored LogLine`() = runBlocking { + every { ratProfileSettings.profile.flow } returns flowOf(profile) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Mister First name who is also known as Last name and is born on 1950-08-01 lives in Main street, " + + "12132 in the beautiful city of London. You can reach him by phone: 111111111 or email: email@example.com", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe logLine.copy( + message = "Mister RAT-Profile/FirstName who is also known as RAT-Profile/LastName and is born on RAT-Profile/DateOfBirth lives in RAT-Profile/Street, " + + "RAT-Profile/Zip-Code in the beautiful city of RAT-Profile/City. You can reach him by phone: RAT-Profile/Phone or email: RAT-Profile/eMail" + ) + + // censoring should still work after the user deletes his profile + every { ratProfileSettings.profile.flow } returns flowOf(null) + + censor.checkLog(logLine) shouldBe logLine.copy( + message = "Mister RAT-Profile/FirstName who is also known as RAT-Profile/LastName and is born on RAT-Profile/DateOfBirth lives in RAT-Profile/Street, " + + "RAT-Profile/Zip-Code in the beautiful city of RAT-Profile/City. You can reach him by phone: RAT-Profile/Phone or email: RAT-Profile/eMail" + ) + } + + private val formatter = DateTimeFormat.forPattern("yyyy-MM-dd") + + private val profile = RATProfile( + firstName = "First name", + lastName = "Last name", + birthDate = formatter.parseLocalDate("1950-08-01"), + street = "Main street", + zipCode = "12132", + city = "London", + phone = "111111111", + email = "email@example.com" + ) +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt index ace9d7c0a8bf6eacb9e3f12d265f0f0a157d593a..029f51ee528a097dd1be66fd1158be739dfd4588 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt @@ -2,7 +2,7 @@ package de.rki.coronawarnapp.bugreporting.debuglog import android.app.Application import dagger.Lazy -import de.rki.coronawarnapp.bugreporting.censors.RegistrationTokenCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.CoronaTestCensor import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebugLogTree import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.di.ApplicationComponent @@ -29,7 +29,7 @@ class DebugLoggerTest : BaseIOTest() { @MockK lateinit var application: Application @MockK lateinit var component: ApplicationComponent - @MockK lateinit var registrationTokenCensor: RegistrationTokenCensor + @MockK lateinit var coronaTestCensor: CoronaTestCensor private val testDir = File(IO_TEST_BASEDIR, this::class.simpleName!!) private val cacheDir = File(testDir, "cache") @@ -49,10 +49,10 @@ class DebugLoggerTest : BaseIOTest() { every { application.cacheDir } returns cacheDir every { component.inject(any<DebugLogger>()) } answers { val logger = arg<DebugLogger>(0) - logger.bugCensors = Lazy { setOf(registrationTokenCensor) } + logger.bugCensors = Lazy { setOf(coronaTestCensor) } } - coEvery { registrationTokenCensor.checkLog(any()) } returns null + coEvery { coronaTestCensor.checkLog(any()) } returns null } @AfterEach diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt index bd8f052d20fbc544f19b3ef378a2567a6c378c9e..846984526b98bb72db9be53e8dbd89d777a543b7 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt @@ -1,7 +1,6 @@ package de.rki.coronawarnapp.ui.submission.qrcode.scan import androidx.lifecycle.MutableLiveData -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQrCodeValidator import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException @@ -87,12 +86,9 @@ class SubmissionQRCodeScanViewModelTest : BaseTest() { viewModel.qrCodeValidationState.value shouldBe ValidationState.STARTED - QRCodeCensor.lastGUID = null - viewModel.onQrCodeAvailable(validQrCode) viewModel.qrCodeValidationState.observeForever {} viewModel.qrCodeValidationState.value shouldBe ValidationState.SUCCESS - QRCodeCensor.lastGUID = guid // invalid guid viewModel.onQrCodeAvailable(invalidQrCode)