Coverage Summary for Class: PiiRedactionKt (com.georgeci.moneysurfer.domain.logging)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| PiiRedactionKt |
100%
(1/1)
|
100%
(2/2)
|
100%
(20/20)
|
100%
(12/12)
|
100%
(106/106)
|
package com.georgeci.moneysurfer.domain.logging
/**
* PII redaction helpers for log messages. Emails and user identifiers (Firebase
* UIDs / `UserId` values) are personal data and must never reach logcat or the
* iOS unified log in cleartext — not even in release, where anyone with brief
* physical access (`adb logcat`, Xcode Console) or a log / bug-report collector
* could harvest the user's and their invitees' identities (issue #154).
*
* The masks keep just enough for a developer to correlate log lines without
* exposing the underlying identity, and are applied at the call sites regardless
* of build type — so a stray [co.touchlab.kermit.LogWriter] or a mistakenly
* lowered min-severity can never leak the raw value. Release muting
* ([configureLogging]) is the second layer.
*/
/** `john.doe@example.com` -> `j***@***.com`; blank / malformed -> a fixed marker. */
fun String?.redactEmail(): String {
val email = this?.trim().orEmpty()
if (email.isEmpty()) return BLANK_EMAIL
val at = email.indexOf('@')
if (at <= 0 || at == email.length - 1) return REDACTED_EMAIL
val maskedLocal = "${email.first()}$MASK"
val domain = email.substring(at + 1)
val lastDot = domain.lastIndexOf('.')
val maskedDomain = if (lastDot > 0) "$MASK${domain.substring(lastDot)}" else MASK
return "$maskedLocal@$maskedDomain"
}
/**
* Firebase UID / `UserId` mask: keeps a short prefix for cross-line correlation
* and drops the rest. `abcd1234efgh` -> `abcd***`; blank or too-short values are
* fully masked so nothing identifying survives.
*/
fun String?.redactUid(): String {
val uid = this?.trim().orEmpty()
if (uid.isEmpty()) return BLANK_UID
return if (uid.length <= PREFIX_LEN) MASK else "${uid.take(PREFIX_LEN)}$MASK"
}
private const val MASK = "***"
private const val BLANK_EMAIL = "<blank-email>"
private const val REDACTED_EMAIL = "<redacted-email>"
private const val BLANK_UID = "<blank-uid>"
private const val PREFIX_LEN = 4