security: enforce channel membership check in handleTopic #75
Reference in New Issue
Block a user
Delete Branch "fix/topic-membership-check"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
handleTopicininternal/handlers/api.godid NOT check that the user was a member of the channel before allowing them to set a topic. Any authenticated user could set the topic on any channel they hadn't joined.Changes
internal/handlers/api.go: AddedIsChannelMembercheck after resolving the channel ID and before callingexecuteTopic, mirroring the existing pattern inhandleChannelMsg. Non-members now receiveERR_NOTONCHANNEL(442).internal/handlers/api_test.go: AddedTestTopicNonMember— creates a channel with one user, then verifies a second user who hasn't joined receives numeric 442 when attempting to set the topic.Testing
TestTopicNonMembertest validates the fixdocker build .passes clean (formatting, linting, tests, build)closes #33
Review: PR #75 — security: enforce channel membership check in handleTopic
Policy Compliance Check
No policy violations found.
internal/handlers/api.goandinternal/handlers/api_test.gomodified — no linter config, Makefile, CI config, or test infrastructure changesreturnstatement (AGENTS.md / Go styleguide)isMember, err := ...)handleChannelMsgpattern exactly:IsChannelMember→ log + 500 on DB error → IRC numeric error on non-memberirc.ErrNotOnChannel(442) which is the semantically correct IRC numeric for TOPIC on an unjoined channel (vsErrCannotSendToChanused for PRIVMSG — both are correct per RFC 1459).golangci.ymluntouchedRequirements Checklist (from issue #33)
IsChannelMembercheck inhandleTopicbeforeSetTopicGetChannelByNameand beforeexecuteTopichandleChannelMsgIsChannelMembercall → DB error handling with log + 500 → non-member IRC error responseERR_NOTONCHANNEL(442), correct per RFC 1459 §4.2.4TestTopicNonMembercreates channel with alice, bob attempts TOPIC without joining, asserts 442Build Result
✅
docker build .passes clean — fmt-check, lint, test, and build all succeed.Additional Review Notes
chID) but beforeexecuteTopicis called. No path can bypass the check.IsChannelMemberDB failure correctly returns 500 with logged error, not an IRC numeric (matches existing pattern).TestTopicNonMemberis well-structured — creates two separate sessions, only one joins, verifies the non-member gets 442. Drains initial messages before the test command to avoid false positives.Verdict: PASS
The fix is correct, minimal, policy-compliant, and well-tested. The membership check mirrors the established pattern from
handleChannelMsgand uses the semantically appropriate IRC error code.