fix: harden fake channel — production guard, token validation, typing events, URL validation, capability narrowing

- Guard fake channel with GOCHAT_ENV check: skip init() registration,
  bootstrap wiring, and inbox creation in production
- Reject empty-token webhooks in production (was silently skipped)
- Use PostgreSQL jsonb @> query for inbox lookup, keep SQLite fallback
- Replace isValidURL string-prefix hack with net/url.Parse
- Handle typing.start/typing.stop by returning nil (no garbage messages)
- Narrow Capabilities to only implemented features (Attachments, Replies)
- Hide fake channel from frontend channel list in production builds
This commit is contained in:
2026-07-10 10:55:54 +08:00
parent 9c852cd99b
commit 05af5ebcbc
6 changed files with 151 additions and 27 deletions
+26 -4
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/gin-gonic/gin"
@@ -496,9 +497,15 @@ func Bootstrap(env string) (*App, error) {
// FakeProvider is self-contained (no service/repo/pipeline deps), so we
// only need to create the webhook handler here. The provider itself is
// registered via init() in the provider package.
fakeProvider := channelprovider.NewFakeProvider()
fakeWebhookHandler := webhook.NewFakeWebhookHandler(fakeProvider, db, channelDispatcher)
fakeWebhookHandler.WithWorkerPool(workerPool)
// Skip in production — the fake channel is a test-only tool.
var fakeWebhookHandler *webhook.FakeWebhookHandler
if !isFakeChannelEnabled(env) {
applogger.L().Info("Fake channel provider skipped (production environment)")
} else {
fakeProvider := channelprovider.NewFakeProvider()
fakeWebhookHandler = webhook.NewFakeWebhookHandler(fakeProvider, db, channelDispatcher)
fakeWebhookHandler.WithWorkerPool(workerPool)
}
// Create Email webhook handler (Gin HTTP handler for Email webhook endpoints)
emailWebhook := emailchannel.NewWebhookHandler()
@@ -723,7 +730,9 @@ func Bootstrap(env string) (*App, error) {
tiktokWebhookHandler.WithSearchIndexer(searchIndexer)
lineWebhookHandler.WithSearchIndexer(searchIndexer)
twilioWebhookHandler.WithSearchIndexer(searchIndexer)
fakeWebhookHandler.WithSearchIndexer(searchIndexer)
if fakeWebhookHandler != nil {
fakeWebhookHandler.WithSearchIndexer(searchIndexer)
}
// Event-triggered automation/AgentBot listeners are registered after the durable
// search indexer exists so action side effects keep Meilisearch current.
channelDispatcher.Register(automation.NewAgentBotRuleListenerWithSearchIndexer(&dbProvider{db: db}, searchIndexer))
@@ -1017,3 +1026,16 @@ func (a *hubTypingAdapter) SetTypingOff(ctx context.Context, accountID, conversa
a.hub.SendToAccount(accountID, data)
return nil
}
// isFakeChannelEnabled reports whether the fake test channel should be wired.
// It is disabled in production environments to prevent test endpoints from
// being exposed in deployed images.
func isFakeChannelEnabled(env string) bool {
switch strings.ToLower(strings.TrimSpace(env)) {
case "production", "prod":
return false
default:
return true
}
}