From 2248974114a59854e1f949d2d8c4d632329fdcd1 Mon Sep 17 00:00:00 2001 From: "Scion Agent (mi-dev-length)" Date: Sun, 28 Jun 2026 09:26:40 +0000 Subject: [PATCH 1/2] feat(messages): enforce 2000 character message length limit Add MaxMessageLength constant (2000 chars) and validate message content before dispatch to any integration, returning a clear error suggesting the sender split into multiple messages. --- pkg/hub/handlers_agent_messaging.go | 4 ++++ pkg/messages/types.go | 6 ++++++ pkg/messages/types_test.go | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/pkg/hub/handlers_agent_messaging.go b/pkg/hub/handlers_agent_messaging.go index 67edec9a8..30c1a572a 100644 --- a/pkg/hub/handlers_agent_messaging.go +++ b/pkg/hub/handlers_agent_messaging.go @@ -68,6 +68,10 @@ func (s *Server) handleAgentOutboundMessage(w http.ResponseWriter, r *http.Reque ValidationError(w, "msg is required", nil) return } + if msgLen := len([]rune(req.Msg)); msgLen > messages.MaxMessageLength { + ValidationError(w, fmt.Sprintf("message exceeds %d character limit (current: %d chars). Consider splitting into multiple messages using multiple scion message invocations", messages.MaxMessageLength, msgLen), nil) + return + } if req.Type == "" { req.Type = "input-needed" } diff --git a/pkg/messages/types.go b/pkg/messages/types.go index 962593325..e4508dfde 100644 --- a/pkg/messages/types.go +++ b/pkg/messages/types.go @@ -24,6 +24,9 @@ import ( // Schema version for the structured message format. const Version = 1 +// Maximum length of the Msg field in characters for outbound messages. +const MaxMessageLength = 2000 + // Maximum size of the Msg field in bytes. const MaxMsgSize = 64 * 1024 // 64KB @@ -127,6 +130,9 @@ func (m *StructuredMessage) Validate() error { if m.Msg == "" { return fmt.Errorf("msg field is required") } + if len([]rune(m.Msg)) > MaxMessageLength { + return fmt.Errorf("message exceeds %d character limit (current: %d chars). Consider splitting into multiple messages using multiple scion message invocations", MaxMessageLength, len([]rune(m.Msg))) + } if len(m.Msg) > MaxMsgSize { return fmt.Errorf("msg exceeds maximum size of %d bytes", MaxMsgSize) } diff --git a/pkg/messages/types_test.go b/pkg/messages/types_test.go index 0c19ce0cb..b14aa098b 100644 --- a/pkg/messages/types_test.go +++ b/pkg/messages/types_test.go @@ -76,6 +76,25 @@ func TestStructuredMessage_Validate(t *testing.T) { } }) + t.Run("msg exceeds character limit", func(t *testing.T) { + m := validMsg() + m.Msg = strings.Repeat("x", MaxMessageLength+1) + err := m.Validate() + if err == nil { + t.Error("expected error for msg exceeding character limit") + } else if !strings.Contains(err.Error(), "character limit") { + t.Errorf("error should mention character limit, got: %v", err) + } + }) + + t.Run("msg at character limit", func(t *testing.T) { + m := validMsg() + m.Msg = strings.Repeat("x", MaxMessageLength) + if err := m.Validate(); err != nil { + t.Errorf("unexpected error for msg at character limit: %v", err) + } + }) + t.Run("msg too large", func(t *testing.T) { m := validMsg() m.Msg = strings.Repeat("x", MaxMsgSize+1) From 9531dad11ec1684ac3693f8715a94d93f95d5824 Mon Sep 17 00:00:00 2001 From: "Scion Agent (mi-length-fix)" Date: Sun, 28 Jun 2026 09:50:01 +0000 Subject: [PATCH 2/2] fix: use utf8.RuneCountInString instead of len([]rune()) for efficiency Replace len([]rune(req.Msg)) with utf8.RuneCountInString(req.Msg) to avoid allocating a temporary []rune slice. Addresses Gemini PR #524 MEDIUM finding. --- pkg/hub/handlers_agent_messaging.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/hub/handlers_agent_messaging.go b/pkg/hub/handlers_agent_messaging.go index 30c1a572a..af860d8b9 100644 --- a/pkg/hub/handlers_agent_messaging.go +++ b/pkg/hub/handlers_agent_messaging.go @@ -22,6 +22,7 @@ import ( "net/http" "strings" "time" + "unicode/utf8" "github.com/GoogleCloudPlatform/scion/pkg/agent/state" "github.com/GoogleCloudPlatform/scion/pkg/api" @@ -68,7 +69,7 @@ func (s *Server) handleAgentOutboundMessage(w http.ResponseWriter, r *http.Reque ValidationError(w, "msg is required", nil) return } - if msgLen := len([]rune(req.Msg)); msgLen > messages.MaxMessageLength { + if msgLen := utf8.RuneCountInString(req.Msg); msgLen > messages.MaxMessageLength { ValidationError(w, fmt.Sprintf("message exceeds %d character limit (current: %d chars). Consider splitting into multiple messages using multiple scion message invocations", messages.MaxMessageLength, msgLen), nil) return }