ScuttleBot

fix: bot review — nil dereference in scroll/scribe, steward mute mode (#76-82) Audit of all 10 system bots found 3 bugs: - scroll: nil dereference on e.Source in PRIVMSG handler (crash on malformed IRC messages) - scribe: same nil dereference bug - steward: used +q (channel owner in Ergo) instead of extended ban m:nick!*@* for muting, same bug warden had All other bots (oracle, warden, snitch, sentinel, herald, systembot, auditbot) passed review — proper nil checks, mutex protection, error handling, +B mode, and INVITE handling all correct.

lmata 2026-04-05 17:28 trunk
Commit 1b7cb63ff2f0285b9c62e0906e5ba2382696302e8e608fb1af39e53bf11b9caa
--- internal/bots/scribe/scribe.go
+++ internal/bots/scribe/scribe.go
@@ -79,11 +79,11 @@
7979
}
8080
})
8181
8282
// Log PRIVMSG — the agent message stream.
8383
c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
84
- if len(e.Params) < 1 {
84
+ if len(e.Params) < 1 || e.Source == nil {
8585
return
8686
}
8787
channel := e.Params[0]
8888
if !strings.HasPrefix(channel, "#") {
8989
return // ignore DMs to scribe itself
9090
--- internal/bots/scribe/scribe.go
+++ internal/bots/scribe/scribe.go
@@ -79,11 +79,11 @@
79 }
80 })
81
82 // Log PRIVMSG — the agent message stream.
83 c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
84 if len(e.Params) < 1 {
85 return
86 }
87 channel := e.Params[0]
88 if !strings.HasPrefix(channel, "#") {
89 return // ignore DMs to scribe itself
90
--- internal/bots/scribe/scribe.go
+++ internal/bots/scribe/scribe.go
@@ -79,11 +79,11 @@
79 }
80 })
81
82 // Log PRIVMSG — the agent message stream.
83 c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
84 if len(e.Params) < 1 || e.Source == nil {
85 return
86 }
87 channel := e.Params[0]
88 if !strings.HasPrefix(channel, "#") {
89 return // ignore DMs to scribe itself
90
--- internal/bots/scroll/scroll.go
+++ internal/bots/scroll/scroll.go
@@ -95,11 +95,11 @@
9595
b.log.Info("scroll connected", "channels", b.channels, "chathistory", hasCH)
9696
})
9797
9898
// Only respond to DMs — ignore anything in a channel.
9999
c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
100
- if len(e.Params) < 1 {
100
+ if len(e.Params) < 1 || e.Source == nil {
101101
return
102102
}
103103
target := e.Params[0]
104104
if strings.HasPrefix(target, "#") {
105105
return // channel message, ignore
106106
--- internal/bots/scroll/scroll.go
+++ internal/bots/scroll/scroll.go
@@ -95,11 +95,11 @@
95 b.log.Info("scroll connected", "channels", b.channels, "chathistory", hasCH)
96 })
97
98 // Only respond to DMs — ignore anything in a channel.
99 c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
100 if len(e.Params) < 1 {
101 return
102 }
103 target := e.Params[0]
104 if strings.HasPrefix(target, "#") {
105 return // channel message, ignore
106
--- internal/bots/scroll/scroll.go
+++ internal/bots/scroll/scroll.go
@@ -95,11 +95,11 @@
95 b.log.Info("scroll connected", "channels", b.channels, "chathistory", hasCH)
96 })
97
98 // Only respond to DMs — ignore anything in a channel.
99 c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
100 if len(e.Params) < 1 || e.Source == nil {
101 return
102 }
103 target := e.Params[0]
104 if strings.HasPrefix(target, "#") {
105 return // channel message, ignore
106
--- internal/bots/steward/steward.go
+++ internal/bots/steward/steward.go
@@ -278,12 +278,12 @@
278278
b.log.Info("steward warn", "nick", nick, "channel", channel, "reason", reason)
279279
}
280280
}
281281
282282
func (b *Bot) mute(c *girc.Client, nick, channel string, d time.Duration) {
283
- // +q (quiet) mode — supported by Ergo.
284
- c.Cmd.Mode(channel, "+q", nick)
283
+ // Extended ban m: to mute — agent stays in channel but cannot speak.
284
+ c.Cmd.Mode(channel, "+b", "m:"+nick+"!*@*")
285285
key := channel + ":" + nick
286286
b.mu.Lock()
287287
b.mutes[key] = time.Now().Add(d)
288288
b.mu.Unlock()
289289
b.announce(c, fmt.Sprintf("muted %s in %s for %s", nick, channel, d.Round(time.Second)))
@@ -291,11 +291,11 @@
291291
b.log.Info("steward mute", "nick", nick, "channel", channel, "duration", d)
292292
}
293293
}
294294
295295
func (b *Bot) unmute(c *girc.Client, nick, channel string) {
296
- c.Cmd.Mode(channel, "-q", nick)
296
+ c.Cmd.Mode(channel, "-b", "m:"+nick+"!*@*")
297297
key := channel + ":" + nick
298298
b.mu.Lock()
299299
delete(b.mutes, key)
300300
b.mu.Unlock()
301301
b.announce(c, fmt.Sprintf("unmuted %s in %s", nick, channel))
302302
--- internal/bots/steward/steward.go
+++ internal/bots/steward/steward.go
@@ -278,12 +278,12 @@
278 b.log.Info("steward warn", "nick", nick, "channel", channel, "reason", reason)
279 }
280 }
281
282 func (b *Bot) mute(c *girc.Client, nick, channel string, d time.Duration) {
283 // +q (quiet) mode — supported by Ergo.
284 c.Cmd.Mode(channel, "+q", nick)
285 key := channel + ":" + nick
286 b.mu.Lock()
287 b.mutes[key] = time.Now().Add(d)
288 b.mu.Unlock()
289 b.announce(c, fmt.Sprintf("muted %s in %s for %s", nick, channel, d.Round(time.Second)))
@@ -291,11 +291,11 @@
291 b.log.Info("steward mute", "nick", nick, "channel", channel, "duration", d)
292 }
293 }
294
295 func (b *Bot) unmute(c *girc.Client, nick, channel string) {
296 c.Cmd.Mode(channel, "-q", nick)
297 key := channel + ":" + nick
298 b.mu.Lock()
299 delete(b.mutes, key)
300 b.mu.Unlock()
301 b.announce(c, fmt.Sprintf("unmuted %s in %s", nick, channel))
302
--- internal/bots/steward/steward.go
+++ internal/bots/steward/steward.go
@@ -278,12 +278,12 @@
278 b.log.Info("steward warn", "nick", nick, "channel", channel, "reason", reason)
279 }
280 }
281
282 func (b *Bot) mute(c *girc.Client, nick, channel string, d time.Duration) {
283 // Extended ban m: to mute — agent stays in channel but cannot speak.
284 c.Cmd.Mode(channel, "+b", "m:"+nick+"!*@*")
285 key := channel + ":" + nick
286 b.mu.Lock()
287 b.mutes[key] = time.Now().Add(d)
288 b.mu.Unlock()
289 b.announce(c, fmt.Sprintf("muted %s in %s for %s", nick, channel, d.Round(time.Second)))
@@ -291,11 +291,11 @@
291 b.log.Info("steward mute", "nick", nick, "channel", channel, "duration", d)
292 }
293 }
294
295 func (b *Bot) unmute(c *girc.Client, nick, channel string) {
296 c.Cmd.Mode(channel, "-b", "m:"+nick+"!*@*")
297 key := channel + ":" + nick
298 b.mu.Lock()
299 delete(b.mutes, key)
300 b.mu.Unlock()
301 b.announce(c, fmt.Sprintf("unmuted %s in %s", nick, channel))
302

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button