From 35b96786f6d1a4089f9de9b3bb07579c1b6f5e6f Mon Sep 17 00:00:00 2001 From: Diego Fornalha Date: Sat, 4 Apr 2026 04:09:06 -0300 Subject: [PATCH] feat: block WhatsApp group messages with GroupTrigger support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add GroupTrigger field to WhatsAppConfig (was missing — other channels like Telegram already had it). When mention_only is true, group messages are silently dropped before reaching the LLM. Prevents magic links and subscriber data from leaking in group chats. Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/channels/whatsapp_native/whatsapp_native.go | 6 ++++++ pkg/config/config.go | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/channels/whatsapp_native/whatsapp_native.go b/pkg/channels/whatsapp_native/whatsapp_native.go index d0a74a405..c35a39462 100644 --- a/pkg/channels/whatsapp_native/whatsapp_native.go +++ b/pkg/channels/whatsapp_native/whatsapp_native.go @@ -364,6 +364,12 @@ func (c *WhatsAppNativeChannel) handleIncoming(evt *events.Message) { metadata["user_name"] = evt.Info.PushName } if evt.Info.Chat.Server == types.GroupServer { + // Skip group messages when mention_only is enabled — prevents + // data leaks (magic links, subscriber info) in public groups. + if c.cfg.GroupTrigger.MentionOnly { + logger.DebugCF("whatsapp", "ignoring group message (mention_only)", map[string]any{"chat": chatID}) + return + } metadata["peer_kind"] = "group" metadata["peer_id"] = chatID } else { diff --git a/pkg/config/config.go b/pkg/config/config.go index 7165246e5..481feb8e7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -340,6 +340,7 @@ type WhatsAppConfig struct { UseNative bool `json:"use_native" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_USE_NATIVE"` SessionStorePath string `json:"session_store_path" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_SESSION_STORE_PATH"` AllowFrom FlexibleStringSlice `json:"allow_from" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_ALLOW_FROM"` + GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty" yaml:"-"` ReasoningChannelID string `json:"reasoning_channel_id" yaml:"-" env:"PICOCLAW_CHANNELS_WHATSAPP_REASONING_CHANNEL_ID"` } @@ -1027,7 +1028,9 @@ func LoadConfig(path string) (*Config, error) { return nil, fmt.Errorf("failed to load existing security config: %w", secErr) } defer func(cfg *Config) { - _ = SaveConfig(path, cfg) + if saveErr := SaveConfig(path, cfg); saveErr != nil { + logger.ErrorF("failed to save config after v0 migration", map[string]any{"error": saveErr}) + } }(cfg) case 1: // V1→V2 migration: infer Enabled and migrate channel config fields @@ -1061,7 +1064,9 @@ func LoadConfig(path string) (*Config, error) { } defer func(cfg *Config) { - _ = SaveConfig(path, cfg) + if saveErr := SaveConfig(path, cfg); saveErr != nil { + logger.ErrorF("failed to save config after v1 migration", map[string]any{"error": saveErr}) + } }(cfg) logger.InfoF( "config migrate success",