H-116: close visitor payload trust boundaries (#19)
* H-116: close visitor payload trust boundaries * H-129: unblock SQLite backend tests * H-129: remove stale last-seen response assertions --------- Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/gochat/gochat/internal/webhookutil"
|
||||
wspkg "github.com/gochat/gochat/internal/ws"
|
||||
"github.com/gochat/gochat/pkg/logger"
|
||||
)
|
||||
@@ -282,7 +283,7 @@ func (h *Hub) SendToAccount(accountID uint, data []byte) {
|
||||
if clientIDs, ok := h.rooms[roomName]; ok {
|
||||
for clientID := range clientIDs {
|
||||
if client, ok := h.clients[clientID]; ok {
|
||||
msg := wrapActionCableMessage(client.Identifier, data)
|
||||
msg := wrapActionCableMessage(client.Identifier, eventDataForClient(client, data))
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
@@ -306,7 +307,7 @@ func (h *Hub) SendToAccountConversation(accountID uint, conversationID uint, dat
|
||||
if clientIDs, ok := h.rooms[roomName]; ok {
|
||||
for clientID := range clientIDs {
|
||||
if client, ok := h.clients[clientID]; ok {
|
||||
msg := wrapActionCableMessage(client.Identifier, data)
|
||||
msg := wrapActionCableMessage(client.Identifier, eventDataForClient(client, data))
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
@@ -320,6 +321,17 @@ func (h *Hub) SendToAccountConversation(accountID uint, conversationID uint, dat
|
||||
}
|
||||
}
|
||||
|
||||
func eventDataForClient(client *Client, data []byte) []byte {
|
||||
if !client.IsContact {
|
||||
return data
|
||||
}
|
||||
payload, err := json.Marshal(webhookutil.SanitizeOutboundJSON(data))
|
||||
if err != nil {
|
||||
return []byte(`{}`)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
// SendToRoom sends a message to all clients in a named room.
|
||||
func (h *Hub) SendToRoom(room string, data []byte) {
|
||||
h.mu.RLock()
|
||||
@@ -328,7 +340,7 @@ func (h *Hub) SendToRoom(room string, data []byte) {
|
||||
if clientIDs, ok := h.rooms[room]; ok {
|
||||
for clientID := range clientIDs {
|
||||
if client, ok := h.clients[clientID]; ok {
|
||||
msg := wrapActionCableMessage(client.Identifier, data)
|
||||
msg := wrapActionCableMessage(client.Identifier, eventDataForClient(client, data))
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
@@ -348,7 +360,7 @@ func (h *Hub) SendToClient(clientID string, data []byte) {
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
if client, ok := h.clients[clientID]; ok {
|
||||
msg := wrapActionCableMessage(client.Identifier, data)
|
||||
msg := wrapActionCableMessage(client.Identifier, eventDataForClient(client, data))
|
||||
if msg == nil {
|
||||
return
|
||||
}
|
||||
@@ -624,4 +636,4 @@ func conversationRoomName(accountID uint, conversationID uint) string {
|
||||
|
||||
func pubsubTokenRoomName(token string) string {
|
||||
return fmt.Sprintf("pubsub_token_%s", token)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +255,61 @@ func TestHub_SendToAccount(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHub_SendToAccountSanitizesVisitorIdentity(t *testing.T) {
|
||||
hub := NewHubSimple()
|
||||
agent := NewClient(1, 10, nil, hub)
|
||||
agent.Identifier = `{"channel":"AccountChannel","account_id":10}`
|
||||
hub.Register(agent)
|
||||
visitor := NewClient(2, 10, nil, hub)
|
||||
visitor.IsContact = true
|
||||
visitor.Identifier = `{"channel":"AccountChannel","account_id":10}`
|
||||
hub.Register(visitor)
|
||||
|
||||
hub.SendToAccount(10, []byte(`{"event":"message.created","data":{"content":"same reply","sender_type":"AgentBot","sender_id":7,"ai_takeover_active":true,"additional_attributes":{"agent_name":"Captain"}}}`))
|
||||
|
||||
var agentMessage, visitorMessage []byte
|
||||
select {
|
||||
case agentMessage = <-agent.Send:
|
||||
default:
|
||||
t.Fatal("expected agent event")
|
||||
}
|
||||
select {
|
||||
case visitorMessage = <-visitor.Send:
|
||||
default:
|
||||
t.Fatal("expected visitor event")
|
||||
}
|
||||
assert.Contains(t, string(agentMessage), "AgentBot")
|
||||
assert.NotContains(t, string(visitorMessage), "AgentBot")
|
||||
assert.NotContains(t, string(visitorMessage), "sender_id")
|
||||
assert.NotContains(t, string(visitorMessage), "sender_type")
|
||||
assert.NotContains(t, string(visitorMessage), "ai_takeover_active")
|
||||
assert.NotContains(t, string(visitorMessage), "agent_name")
|
||||
assert.NotContains(t, string(visitorMessage), "Captain")
|
||||
}
|
||||
|
||||
func TestHub_VisitorRoomAndClientDeliverySanitizeIdentity(t *testing.T) {
|
||||
hub := NewHubSimple()
|
||||
visitor := NewClient(2, 10, nil, hub)
|
||||
visitor.IsContact = true
|
||||
visitor.Identifier = `{"channel":"RoomChannel"}`
|
||||
hub.Register(visitor)
|
||||
hub.subscribeClient(visitor.ID, "visitor-room")
|
||||
visitor.SubscribedRooms["visitor-room"] = true
|
||||
data := []byte(`{"event":"message.created","data":{"sender_type":"Captain::Assistant","sender_name":"Captain"}}`)
|
||||
|
||||
hub.SendToRoom("visitor-room", data)
|
||||
hub.SendToClient(visitor.ID, data)
|
||||
for range 2 {
|
||||
select {
|
||||
case message := <-visitor.Send:
|
||||
assert.NotContains(t, string(message), "Captain")
|
||||
assert.NotContains(t, string(message), "sender_type")
|
||||
default:
|
||||
t.Fatal("expected sanitized visitor event")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHub_SendToAccount_NoClients(t *testing.T) {
|
||||
hub := NewHubSimple()
|
||||
hub.SendToAccount(10, []byte(`{"event":"test"}`)) // should not panic
|
||||
|
||||
Reference in New Issue
Block a user