fix: MEDIUM/LOW bugs - validation, unread tracking, TLS bounds, KeyPackage checks

- H17: Add unread count tracking to MarmotGroupChatroom
- M8: Add MAX_OPAQUE_SIZE bounds check to TLS deserialization
- M13: Add version/ciphersuite validation on KeyPackage deserialization
- M24: Add logging before deleting corrupted group state in restoreAll
- L1: Add size limit to sentKeys map in MlsGroup
- Additional UI fixes: leave group cleanup, error handling improvements
- Fix MarmotSubscriptionManagerTest for updated API

https://claude.ai/code/session_018gVkmmYgMFtBH7G31pCk9N
This commit is contained in:
Claude
2026-04-07 23:04:09 +00:00
parent 8c8ab4bb2c
commit 4526beb4be
10 changed files with 204 additions and 98 deletions
@@ -121,6 +121,14 @@ class MarmotManager(
welcomeEvent: WelcomeEvent,
nostrGroupId: HexKey,
): WelcomeResult {
// Validate that the provided nostrGroupId matches the WelcomeEvent's h-tag if present
val eventGroupId = welcomeEvent.nostrGroupId()
if (eventGroupId != null && eventGroupId != nostrGroupId) {
return WelcomeResult.Error(
"nostrGroupId mismatch: expected $nostrGroupId but WelcomeEvent has $eventGroupId",
)
}
val result = inboundProcessor.processWelcome(welcomeEvent, nostrGroupId)
if (result is WelcomeResult.Joined) {
@@ -153,6 +161,20 @@ class MarmotManager(
keyPackageEventId: HexKey,
relays: List<NormalizedRelayUrl>,
): Pair<OutboundGroupEvent, WelcomeDelivery?> {
// Verify that the KeyPackage credential matches the expected member pubkey
val kp =
com.vitorpamplona.quartz.marmot.mls.messages.MlsKeyPackage.decodeTls(
com.vitorpamplona.quartz.marmot.mls.codec
.TlsReader(keyPackageBytes),
)
val credential = kp.leafNode.credential
require(credential is Credential.Basic) {
"KeyPackage must use BasicCredential"
}
require(credential.identity.toHexKey() == memberPubKey) {
"KeyPackage credential identity does not match memberPubKey"
}
val commitResult = groupManager.addMember(nostrGroupId, keyPackageBytes)
val commitEvent = outboundProcessor.buildCommitEvent(nostrGroupId, commitResult.commitBytes)
@@ -47,6 +47,7 @@ class MarmotGroupChatroom(
var relays = MutableStateFlow<List<String>>(emptyList())
var memberCount = MutableStateFlow(0)
var newestMessage: Note? = null
val unreadCount = MutableStateFlow(0)
private var changesFlow: WeakReference<MutableSharedFlow<ListChange<Note>>> = WeakReference(null)
@@ -73,6 +74,7 @@ class MarmotGroupChatroom(
newestMessage = msg
}
unreadCount.value += 1
changesFlow.get()?.tryEmit(ListChange.Addition(msg))
return true
}
@@ -95,6 +97,10 @@ class MarmotGroupChatroom(
return false
}
fun markAsRead() {
unreadCount.value = 0
}
fun pruneMessagesToTheLatestOnly(): Set<Note> {
val sorted = messages.sortedWith(DefaultFeedOrder)
val toKeep =