This commit is contained in:
spaced4ndy
2026-07-06 18:26:11 +04:00
parent eba91b5f3e
commit 41621a64a6
2 changed files with 33 additions and 87 deletions
+5 -5
View File
@@ -2280,17 +2280,17 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
(cci,) <$> getGroupChatScopeInfoForItem db cxt user gInfo (cChatItemId cci)
case cci of
CChatItem SMDRcv ci@ChatItem {chatDir = CIGroupRcv m', meta = CIMeta {itemLive, msgSigned = itemSigned}, content = CIRcvMsgContent oldMC}
| isSender m' -> requireVerifiedMutation (CDGroupRcv gInfo scopeInfo m') itemSigned $ updateCI False ci scopeInfo oldMC itemLive (Just $ memberId' m')
| isSender m' -> requireVerifiedEdit (CDGroupRcv gInfo scopeInfo m') itemSigned $ updateCI False ci scopeInfo oldMC itemLive (Just $ memberId' m')
| otherwise -> messageError "x.msg.update: group member attempted to update a message of another member" $> Nothing
CChatItem SMDRcv ci@ChatItem {chatDir = CIChannelRcv, meta = CIMeta {itemLive, msgSigned = itemSigned}, content = CIRcvMsgContent oldMC}
| maybe True (\m -> memberRole' m == GROwner) m_ -> requireVerifiedMutation (CDChannelRcv gInfo scopeInfo) itemSigned $ updateCI True ci scopeInfo oldMC itemLive Nothing
| maybe True (\m -> memberRole' m == GROwner) m_ -> requireVerifiedEdit (CDChannelRcv gInfo scopeInfo) itemSigned $ updateCI True ci scopeInfo oldMC itemLive Nothing
| otherwise -> messageError "x.msg.update: member attempted to update channel message" $> Nothing
_ -> messageError "x.msg.update: invalid message update" $> Nothing
where
isSender m' = maybe False (\m -> sameMemberId (memberId' m) m') m_
-- only a verified mutation may alter a verified item; reject otherwise (fail-closed)
requireVerifiedMutation :: ChatDirection 'CTGroup 'MDRcv -> Maybe MsgSigStatus -> CM (Maybe DeliveryTaskContext) -> CM (Maybe DeliveryTaskContext)
requireVerifiedMutation cd itemSigned action
-- only a verified edit may alter a verified item; reject otherwise (fail-closed)
requireVerifiedEdit :: ChatDirection 'CTGroup 'MDRcv -> Maybe MsgSigStatus -> CM (Maybe DeliveryTaskContext) -> CM (Maybe DeliveryTaskContext)
requireVerifiedEdit cd itemSigned action
| itemSigned == Just MSSVerified && msgSigned /= Just MSSVerified = do
createInternalChatItem user cd (CIRcvGroupEvent RGEMsgBadSignature) (Just brokerTs)
pure Nothing
+28 -82
View File
@@ -10090,17 +10090,6 @@ testChannelRelayCannotForgePrivilegedMember ps =
case rows of
[Only mid] -> pure (MemberId mid)
_ -> fail "expected exactly one owner member on the relay"
relayConnIdToMember :: TestCC -> T.Text -> IO ByteString
relayConnIdToMember cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query
db
"SELECT c.agent_conn_id FROM connections c JOIN group_members m ON m.group_member_id = c.group_member_id WHERE m.local_display_name = ?"
(Only name) ::
IO [Only ByteString]
case rows of
(Only connId : _) -> pure connId
_ -> fail $ "no relay connection to member " <> T.unpack name
forgedMemberRows :: TestCC -> T.Text -> IO [(T.Text, Maybe ByteString)]
forgedMemberRows cc name =
withCCTransaction cc $ \db ->
@@ -12076,6 +12065,34 @@ testChannelMemberMessageDelete ps =
eve <# "#team cath> [marked deleted] hello"
]
memberIdByName :: TestCC -> T.Text -> IO MemberId
memberIdByName cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query db "SELECT member_id FROM group_members WHERE local_display_name = ?" (Only name) :: IO [Only ByteString]
case rows of
(Only mid : _) -> pure (MemberId mid)
_ -> fail $ "no member " <> T.unpack name
relayConnIdToMember :: TestCC -> T.Text -> IO ByteString
relayConnIdToMember cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query
db
"SELECT c.agent_conn_id FROM connections c JOIN group_members m ON m.group_member_id = c.group_member_id WHERE m.local_display_name = ?"
(Only name) ::
IO [Only ByteString]
case rows of
(Only connId : _) -> pure connId
_ -> fail $ "no relay connection to member " <> T.unpack name
itemSharedMsgId :: TestCC -> IO SharedMsgId
itemSharedMsgId cc = do
rows <- withCCTransaction cc $ \db ->
DB.query_ db "SELECT shared_msg_id FROM chat_items WHERE shared_msg_id IS NOT NULL ORDER BY chat_item_id DESC LIMIT 1" :: IO [Only ByteString]
case rows of
(Only smid : _) -> pure (SharedMsgId smid)
_ -> fail "no shared_msg_id"
testChannelMemberMessageSign :: HasCallStack => TestParams -> IO ()
testChannelMemberMessageSign ps =
withNewTestChat ps "alice" aliceProfile $ \alice ->
@@ -12183,32 +12200,6 @@ testChannelMemberUpdateEnforcement ps =
]
dan #$> ("/_get chat #1 count=100 search=secret edited", chat, [(0, "secret edited (signed)")])
dan #$> ("/_get chat #1 count=100 search=bad signature", chat, [(0, "message rejected: bad signature")])
where
memberIdByName :: TestCC -> T.Text -> IO MemberId
memberIdByName cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query db "SELECT member_id FROM group_members WHERE local_display_name = ?" (Only name) :: IO [Only ByteString]
case rows of
(Only mid : _) -> pure (MemberId mid)
_ -> fail $ "no member " <> T.unpack name
relayConnIdToMember :: TestCC -> T.Text -> IO ByteString
relayConnIdToMember cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query
db
"SELECT c.agent_conn_id FROM connections c JOIN group_members m ON m.group_member_id = c.group_member_id WHERE m.local_display_name = ?"
(Only name) ::
IO [Only ByteString]
case rows of
(Only connId : _) -> pure connId
_ -> fail $ "no relay connection to member " <> T.unpack name
itemSharedMsgId :: TestCC -> IO SharedMsgId
itemSharedMsgId cc = do
rows <- withCCTransaction cc $ \db ->
DB.query_ db "SELECT shared_msg_id FROM chat_items WHERE shared_msg_id IS NOT NULL ORDER BY chat_item_id DESC LIMIT 1" :: IO [Only ByteString]
case rows of
(Only smid : _) -> pure (SharedMsgId smid)
_ -> fail "no shared_msg_id"
testChannelAsGroupSign :: HasCallStack => TestParams -> IO ()
testChannelAsGroupSign ps =
@@ -12273,25 +12264,6 @@ testChannelAsGroupSpoof ps =
-- not rendered as the channel: dan still holds only the legitimate member message
threadDelay 1000000
dan #$> ("/_get chat #1 count=100 search=hi from cath", chat, [(0, "hi from cath")])
where
memberIdByName :: TestCC -> T.Text -> IO MemberId
memberIdByName cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query db "SELECT member_id FROM group_members WHERE local_display_name = ?" (Only name) :: IO [Only ByteString]
case rows of
(Only mid : _) -> pure (MemberId mid)
_ -> fail $ "no member " <> T.unpack name
relayConnIdToMember :: TestCC -> T.Text -> IO ByteString
relayConnIdToMember cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query
db
"SELECT c.agent_conn_id FROM connections c JOIN group_members m ON m.group_member_id = c.group_member_id WHERE m.local_display_name = ?"
(Only name) ::
IO [Only ByteString]
case rows of
(Only connId : _) -> pure connId
_ -> fail $ "no relay connection to member " <> T.unpack name
testChannelMemberSelfDeleteSign :: HasCallStack => TestParams -> IO ()
testChannelMemberSelfDeleteSign ps =
@@ -12395,32 +12367,6 @@ testChannelMemberDeleteEnforcement ps =
dan <# "#team cath> [marked deleted] secret (signed)",
eve <# "#team cath> [marked deleted] secret (signed)"
]
where
memberIdByName :: TestCC -> T.Text -> IO MemberId
memberIdByName cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query db "SELECT member_id FROM group_members WHERE local_display_name = ?" (Only name) :: IO [Only ByteString]
case rows of
(Only mid : _) -> pure (MemberId mid)
_ -> fail $ "no member " <> T.unpack name
relayConnIdToMember :: TestCC -> T.Text -> IO ByteString
relayConnIdToMember cc name = do
rows <- withCCTransaction cc $ \db ->
DB.query
db
"SELECT c.agent_conn_id FROM connections c JOIN group_members m ON m.group_member_id = c.group_member_id WHERE m.local_display_name = ?"
(Only name) ::
IO [Only ByteString]
case rows of
(Only connId : _) -> pure connId
_ -> fail $ "no relay connection to member " <> T.unpack name
itemSharedMsgId :: TestCC -> IO SharedMsgId
itemSharedMsgId cc = do
rows <- withCCTransaction cc $ \db ->
DB.query_ db "SELECT shared_msg_id FROM chat_items WHERE shared_msg_id IS NOT NULL ORDER BY chat_item_id DESC LIMIT 1" :: IO [Only ByteString]
case rows of
(Only smid : _) -> pure (SharedMsgId smid)
_ -> fail "no shared_msg_id"
testChannelModerationDeleteSign :: HasCallStack => TestParams -> IO ()
testChannelModerationDeleteSign ps =