core: check forwarding relay at dispatch

This commit is contained in:
shum committed 2026-10-02 11:18:29 +00:00
1 parent fbf075aa36
commit 1bf57d4098
2 files changed
+36 -6

No files matched your search

+10 -5
View File
@@ -1029,8 +1029,9 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
case fwd_ of
Just fwd | SJson <- enc -> do
logInfo $ "group fwd=" <> tshow tag <> " " <> eInfo
xGrpMsgForward (GIK gInfo' gks) scopeInfo m' fwd parsedMsg brokerTs
`catchAllErrors` \e -> eToView e
withGrpFwdRelay gInfo' m' $
xGrpMsgForward (GIK gInfo' gks) scopeInfo m' fwd parsedMsg brokerTs
`catchAllErrors` \e -> eToView e
pure newDeliveryTasks
-- direct JSON and binary messages; binary events don't produce delivery tasks
_ -> do
@@ -1105,7 +1106,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
XGrpRosterRequest reqVer -> Nothing <$ xGrpRosterRequest (GIK gInfo' gks) m'' reqVer
-- TODO [knocking] why don't we forward these messages?
XGrpDirectInv connReq mContent_ msgScope -> memberCanSend (Just m'') msgScope $ Nothing <$ xGrpDirectInv gInfo' m'' conn' connReq mContent_ msg brokerTs
XGrpMsgForward fwd msg' -> Nothing <$ xGrpMsgForward (GIK gInfo' gks) Nothing m'' fwd (ParsedMsg Nothing Nothing msg') brokerTs
XGrpMsgForward fwd msg' -> Nothing <$ withGrpFwdRelay gInfo' m'' (xGrpMsgForward (GIK gInfo' gks) Nothing m'' fwd (ParsedMsg Nothing Nothing msg') brokerTs)
XInfoProbe probe -> Nothing <$ xInfoProbe (COMGroupMember m'') probe
XInfoProbeCheck probeHash -> Nothing <$ xInfoProbeCheck (COMGroupMember m'') probeHash
XInfoProbeOk probe -> Nothing <$ xInfoProbeOk (COMGroupMember m'') probe
@@ -3758,6 +3759,11 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
| useRelays' gInfo = isRelay m
| otherwise = memberRole' m >= GRAdmin
withGrpFwdRelay :: GroupInfo -> GroupMember -> CM () -> CM ()
withGrpFwdRelay gInfo m action
| isMemberGrpFwdRelay gInfo m = action
| otherwise = messageError "x.grp.msg.forward: forwarding member is not a relay"
unknownMemberRole :: GroupInfo -> CM GroupMemberRole
unknownMemberRole gInfo
| useRelays' gInfo = asks $ channelSubscriberRole . config
@@ -3928,8 +3934,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
createInternalChatItem user (CDDirectRcv ct) (CIRcvConnEvent RCEVerificationCodeReset) Nothing
xGrpMsgForward :: GroupInfoKeys -> Maybe GroupChatScopeInfo -> GroupMember -> GrpMsgForward -> ParsedMsg 'Json -> UTCTime -> CM ()
xGrpMsgForward g@(GIK gInfo _) scopeInfo m@GroupMember {localDisplayName} GrpMsgForward {fwdSender, fwdBrokerTs = msgTs} parsedMsg@(ParsedMsg _ _ chatMsg@ChatMessage {chatMsgEvent}) brokerTs = do
unless (isMemberGrpFwdRelay gInfo m) $ throwChatError (CEGroupContactRole localDisplayName)
xGrpMsgForward g@(GIK gInfo _) scopeInfo m GrpMsgForward {fwdSender, fwdBrokerTs = msgTs} parsedMsg@(ParsedMsg _ _ chatMsg@ChatMessage {chatMsgEvent}) brokerTs =
case fwdSender of
FwdMember memberId memberName -> do
unknownRole <- unknownMemberRole gInfo
+26 -1
View File
@@ -38,7 +38,7 @@ import Simplex.Chat.Messages (CIMention (..), CIMentionMember (..), ChatItemId)
import Simplex.Chat.Messages.Batch (encodeBinaryBatch, encodeFwdElement)
import Simplex.Chat.Messages.CIContent (publicGroupNoE2EText)
import Simplex.Chat.Options
import Simplex.Chat.Protocol (ChatMessage (ChatMessage), ChatMsgEvent (XGrpMemNew, XMsgUpdate, XMsgNew, XMsgDel), FwdSender (FwdMember, FwdChannel), GrpMsgForward (GrpMsgForward), MsgContainer (..), MsgMention (..), MsgContent (..), VerifiedMsg (VMUnsigned), mcSimple, msgContentText)
import Simplex.Chat.Protocol (ChatMessage (ChatMessage), ChatMsgEvent (XGrpMemNew, XGrpMsgForward, XMsgUpdate, XMsgNew, XMsgDel), EncodedChatMessage (..), FwdSender (FwdMember, FwdChannel), GrpMsgForward (GrpMsgForward), MsgContainer (..), MsgMention (..), MsgContent (..), VerifiedMsg (VMUnsigned), encodeChatMessage, maxEncodedMsgLength, mcSimple, msgContentText)
import Simplex.Chat.Types
import Simplex.Chat.Types.MemberRelations (MemberRelation (..), getRelation, setRelation)
import Simplex.Chat.Types.Shared (GroupMemberRole (..), GroupAcceptance (..))
@@ -189,6 +189,7 @@ chatGroupTests = do
it "forward member removal (x.grp.mem.del)" testGroupMsgForwardMemberRemoval
it "forward admin removal (x.grp.mem.del, relay forwards it was removed)" testGroupMsgForwardAdminRemoval
it "forward group deletion (x.grp.del)" testGroupMsgForwardGroupDeletion
it "reject forwarded messages from member without forwarding role (x.grp.msg.forward)" testGroupMsgForwardFromMemberRejected
describe "group history" $ do
it "text messages" testGroupHistory
it "history is sent when joining via group link" testGroupHistoryGroupLink
@@ -5867,6 +5868,30 @@ testGroupMsgForwardGroupDeletion =
cath ##> "/groups"
cath <## "#team (group deleted, delete local copy: /d #team)"
testGroupMsgForwardFromMemberRejected :: HasCallStack => TestParams -> IO ()
testGroupMsgForwardFromMemberRejected =
testChat3 aliceProfile bobProfile cathProfile $
\alice bob cath -> do
createGroup3' "team" alice (bob, GRMember) (cath, GRMember)
aliceMemId <- memberIdByName cath "alice"
connId <- relayConnIdToMember cath "bob"
ts <- getCurrentTime
let ChatController {smpAgent = cathAgent} = chatController cath
fwd = GrpMsgForward (FwdMember aliceMemId "alice") ts
forgedMsg text = ChatMessage chatInitialVRange Nothing (XMsgNew $ mcSimple (MCText text))
sendBody body = do
sent <- runExceptT $ sendMessages cathAgent [(connId, PQEncOff, MsgFlags False, vrValue body)]
either (fail . show) (const $ pure ()) sent
sendBody $ encodeBinaryBatch [encodeFwdElement fwd (VMUnsigned $ forgedMsg "forged element")]
bob <## "error: x.grp.msg.forward: forwarding member is not a relay"
case encodeChatMessage maxEncodedMsgLength (ChatMessage chatInitialVRange Nothing (XGrpMsgForward fwd $ forgedMsg "forged event")) of
ECMEncoded body -> sendBody body
ECMLarge -> fail "x.grp.msg.forward exceeds maximum message size"
bob <## "error: x.grp.msg.forward: forwarding member is not a relay"
bob #$> ("/_get chat #1 count=100 search=forged", chat, [])
cath #> "#team hello"
[alice, bob] *<# "#team cath> hello"
testGroupHistory :: HasCallStack => TestParams -> IO ()
testGroupHistory =
testChat3 aliceProfile bobProfile cathProfile $