Skip to content

fix(botsfw)!: scope bot settings lookup by platform#81

Merged
trakhimenok merged 1 commit into
mainfrom
fix/platform-scoped-bot-settings
Jul 16, 2026
Merged

fix(botsfw)!: scope bot settings lookup by platform#81
trakhimenok merged 1 commit into
mainfrom
fix/platform-scoped-bot-settings

Conversation

@trakhimenok

Copy link
Copy Markdown
Contributor

The bug

GetBotContext accepted a platformID parameter and never read it. It resolved purely by bot ID or code, so the platform a webhook arrived on had no bearing on which bot's settings came back.

// before — platformID declared, never used
func (v botContextProvider) GetBotContext(ctx context.Context, platformID botsfwconst.Platform, botID string) (*BotContext, error) {
	botSettingsBy := v.botSettingProvider(ctx)
	botSettings, ok := botSettingsBy.ByID[botID]
	if !ok {
		if botSettings, ok = botSettingsBy.ByCode[botID]; !ok {
			return nil, ErrUnknownBot
		}
	}
	// ...
}

With Telegram as the only framework-side platform, this was invisible. bots-fw-whatsapp now exists, which is precisely what makes it reachable: a WhatsApp webhook could resolve a Telegram bot's settings — including its Token and WebhookSecretToken — whenever the two shared an ID or code.

Worth noting bots-fw-telegram's handler passes PlatformID and then verifies its secret against whatever settings came back.

Two bugs, one root cause

Bot codes were treated as globally unique when they are only unique per platform.

1. The leak. GetBotContext is now platform-scoped via BotSettingsBy.findByPlatform. A bot on another platform returns ErrUnknownBot rather than being silently substituted. A blank platformID is rejected.

2. The blocker. NewBotSettingsBy panicked on duplicate codes regardless of platform — so the same product could not be registered on two platforms at all. debtus on Telegram and debtus on WhatsApp are different bots with different tokens. It now panics only on a duplicate within a platform.

The second one isn't cosmetic: without it, Debtus-on-WhatsApp cannot be registered alongside Debtus-on-Telegram.

Compatibility

Added ByPlatformAndCode / ByPlatformAndID. ByCode and ByID stay populated (first-wins) but are deprecated — they're ambiguous across platforms by construction. The legacy fallback in findByPlatform still checks Platform, so it cannot reintroduce the leak it exists to be compatible with.

Verified: bots-fw-telegram builds and its tests pass against this branch (temporary local replace, reverted — that repo is untouched).

The test suite had encoded the bug

Three separate fixtures hand-built a BotSettings with no Platform and passed only because platformID was ignored:

  • botsfw/bot_context_test.go&BotSettings{Code: "mybot"}, then asked for it as "telegram"
  • botswebhook/settings_test.go{Profile, Code, ID}, no platform
  • (a third surfaced via the same panic)

All three are fixed rather than worked around. NewBotSettings has always panicked on an empty platform, so those fixtures were malformed — they bypassed the constructor.

⚠️ Breaking

  • A BotSettings with an empty Platform now panics in NewBotSettingsBy. Previously accepted — but such a bot can never be resolved by the platform-scoped GetBotContext, so it was silently unreachable. Failing at registration beats failing at 3am.
  • GetBotContext returns ErrUnknownBot for a bot that exists on a different platform. That's the fix, not a regression.

Tests

New: TestGetBotContext_isPlatformScoped (the security regression test — pins that a WhatsApp lookup gets WhatsApp's token, never Telegram's), TestGetBotContext_legacyFlatMapsStillCheckPlatform, TestNewBotSettingsBy_duplicateCodeAcrossPlatforms.

Full suite green; gofmt, go vet, golangci-lint clean.

Background: platform-neutrality gap analysis, "Seam bug" section.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SE2shvgkXuR8fAzzMui4zN

GetBotContext accepted a platformID parameter and never read it. It resolved purely
by bot ID or code, so the platform a webhook arrived on had no bearing on which
bot's settings came back.

With Telegram as the only framework-side platform this was invisible. bots-fw-whatsapp
now exists, which is exactly what makes it reachable: a WhatsApp webhook could
resolve a Telegram bot's settings - including its Token and WebhookSecretToken -
whenever the two shared an ID or code. Note bots-fw-telegram's handler passes
PlatformID and then verifies its secret against whatever settings came back.

Two bugs, one root cause: bot codes were treated as globally unique when they are
only unique per platform.

1. GetBotContext is now platform-scoped, via BotSettingsBy.findByPlatform. A bot on
   another platform returns ErrUnknownBot rather than being silently substituted. A
   blank platformID is rejected.

2. NewBotSettingsBy panicked on duplicate codes regardless of platform, so the same
   product could not be registered on two platforms - "debtus" on Telegram and
   "debtus" on WhatsApp are different bots with different tokens. It now panics only
   on a duplicate WITHIN a platform. This is what made a second platform registrable
   at all.

Added BotSettingsBy.ByPlatformAndCode / ByPlatformAndID. ByCode and ByID are kept
populated (first-wins) for compatibility but are now deprecated: they are ambiguous
across platforms by construction. The legacy fallback in findByPlatform still checks
Platform, so it cannot reintroduce the leak it exists to be compatible with.

The test suite had ENCODED the bug: three separate fixtures hand-built a BotSettings
with no Platform and passed only because platformID was ignored. All three are fixed
rather than worked around. NewBotSettings has always panicked on an empty platform,
so those fixtures were malformed - they bypassed the constructor.

BREAKING:
- A BotSettings with an empty Platform now panics in NewBotSettingsBy. Previously
  accepted, but such a bot can never be resolved by the platform-scoped
  GetBotContext, so it was silently unreachable. Failing at registration beats
  failing at 3am.
- GetBotContext returns ErrUnknownBot for a bot that exists on a different platform.
  That is the fix, not a regression.

Verified: bots-fw-telegram builds and its tests pass against this branch (temporary
local replace, reverted; that repo is untouched). Full suite green, gofmt + vet +
golangci-lint clean.

Background: spec/research/bots-fw-platform-neutrality-gap-analysis.md "Seam bug" in
bots-go-framework/backstage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@trakhimenok
trakhimenok merged commit 4cd1070 into main Jul 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant