fix: corrections de l'audit de sécurité et de robustesse
- VPN retiré : le contrôle reste activé (alerte parent, tunnel remonté après 30 s, autorisation redemandée à l'ouverture) - Horloge de référence : changer date/heure/fuseau ne contourne plus les plages horaires ni le temps d'écran (signalé au parent) - Mot de passe : PBKDF2 + attente croissante après 3 échecs ; conversion automatique des anciens hachages - Écrans protégés : aucun affichage avant chargement des réglages ; effacement du journal protégé - Signature release via keystore.properties (même clé que les versions publiées) - Sauvegarde Google : réglages, appairage et journal exclus - Alerte admin fiable (goAsync), relance refusée signalée, liste d'applis renvoyée à chaque installation, code d'appairage consommé - Journal thread-safe et sans recopie à chaque ligne ; correctifs lint - Tests unitaires (mot de passe, règles, versions, config Firebase) ; README aligné sur le comportement réel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9CBCVP5oDu2sfoYPisNCQ
This commit is contained in:
1 parent
f33776bbb5
commit
e269e9932a
37 files changed
+809
-147
No files matched your search
@@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.tasks.await
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -75,6 +76,10 @@ class RemoteSync @Inject constructor(
|
||||
"pairedAt" to System.currentTimeMillis()
|
||||
)
|
||||
).await()
|
||||
// Code à usage unique : supprimé une fois consommé (sans effet si les règles Firestore déployées
|
||||
// ne l'autorisent pas encore — il expire de toute façon au bout de 10 min).
|
||||
runCatching { db.collection("pairings").document(clean).delete().await() }
|
||||
.onFailure { AppLog.w("FamilyControl", "Code d'appairage non supprimé : ${it.message}") }
|
||||
settingsRepository.setPairing(familyId, deviceId)
|
||||
}
|
||||
|
||||
@@ -168,7 +173,8 @@ class RemoteSync @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun pushApps() {
|
||||
/** Envoie la liste des applications installées (au parent de choisir lesquelles autoriser). */
|
||||
suspend fun pushApps() {
|
||||
val s = settingsRepository.settingsFlow.first()
|
||||
val ref = deviceDoc(s) ?: return
|
||||
val apps = installedApps.listInstalledApps(withIcons = false).map {
|
||||
@@ -206,10 +212,15 @@ class RemoteSync @Inject constructor(
|
||||
suspend fun pushEvent(type: String, detail: String) {
|
||||
val s = settingsRepository.settingsFlow.first()
|
||||
val ref = deviceDoc(s) ?: return
|
||||
if (gateway.ensureSignedIn() == null) return
|
||||
// Attente bornée de l'accusé du serveur : appelé depuis un récepteur (goAsync), le processus
|
||||
// peut être arrêté juste après ; hors ligne, l'écriture reste en file dans le SDK.
|
||||
runCatching {
|
||||
ref.collection("events").add(
|
||||
mapOf("type" to type, "detail" to detail, "at" to System.currentTimeMillis())
|
||||
)
|
||||
withTimeoutOrNull(EVENT_TIMEOUT_MS) {
|
||||
ref.collection("events").add(
|
||||
mapOf("type" to type, "detail" to detail, "at" to System.currentTimeMillis())
|
||||
).await()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,5 +230,8 @@ class RemoteSync @Inject constructor(
|
||||
ref.set(data, SetOptions.merge())
|
||||
}
|
||||
|
||||
companion object { private const val TAG = "RemoteSync" }
|
||||
companion object {
|
||||
private const val TAG = "RemoteSync"
|
||||
private const val EVENT_TIMEOUT_MS = 8_000L
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user