NoTrigger: make list of prefixes/suffixes and bell (\x07) blocking configurable, docs+test case updates

This commit is contained in:
James Lu 2015-10-11 22:20:59 -07:00
parent 7f5cea73ad
commit 8e8a3a30a5
4 changed files with 59 additions and 25 deletions

View File

@ -1,12 +1,16 @@
NoTrigger is an anti-abuse script that modifies outFilter to prevent triggering other bots.
NoTrigger is an anti-abuse plugin that modifies outFilter to prevent triggering other bots.
## Short description
In short, NoTrigger works by:
- Prepending messages that start with a symbol (```!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~```) with a [zero width space](https://en.wikipedia.org/wiki/Zero-width_space) (ZWSP), since these are often used as prefixes for bots. This has the effect of being completely invisible, and tricks most bots into ignoring yours!
- Prepending messages that start with a symbol (```!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~```) or any configured prefixes with a [zero width space](https://en.wikipedia.org/wiki/Zero-width_space) (ZWSP), since these are often used as prefixes for bots. This has the effect of being completely invisible, and tricks most bots into ignoring yours!
- Prepending messages with a ZWSP if the channel is set to block colors and a message begins with a formatting code (sneaky attackers can otherwise do something like `\x02!echo hello` to bypass filters).
- Optionally, prepending messages with a ZWSP if they match `<something>: ` or `<something>, `, since some bots are taught to respond to their nicks.
- Optionally, blocking all channel-wide CTCPs (except for ACTION).
- Optionally, stripping the bell character from any outgoing messages.
- Optionally, appending messages that end with any configured suffixes with a ZWSP.
To enable NoTrigger, set the `plugins.NoTrigger.enable` config option `True` for the channels in question. You can find a list of NoTrigger's options (toggling the things mentioned above) by running `config list plugins.NoTrigger`.
## Longer description/Backstory on why I wrote this
Sometimes when you have a public development channel with many bots residing in it, someone will come along and do something really evil: that is, create a huge message loop by chaining all your innocent bots together!

View File

@ -28,6 +28,8 @@
###
import string
import supybot.conf as conf
import supybot.registry as registry
try:
@ -38,7 +40,6 @@ except:
# without the i18n module
_ = lambda x: x
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
@ -52,22 +53,32 @@ NoTrigger = conf.registerPlugin('NoTrigger')
conf.registerChannelValue(NoTrigger, 'enable',
registry.Boolean(True, _("""Enable protection against triggering other bots.""")))
conf.registerChannelValue(NoTrigger, 'spaceBeforeNicks',
registry.Boolean(False, _("""Add a space before messages beginning with
registry.Boolean(False, _("""Add a space before messages beginning with
"blah: " or "blah, ", preventing the bot from triggering other bots that
respond to nick. This can cause some weird spacings with error messages and
other command replies, so I wouldn't recommend enabling it unless absolutely
necessary.""")))
respond to nick.""")))
conf.registerChannelValue(NoTrigger, 'colorAware',
registry.Boolean(True, _("""Toggles whether the bot should be aware of colour-stripping
modes. (+c or +S on most IRCds)""")))
conf.registerGroup(NoTrigger, 'colorAware')
registry.Boolean(True, _("""Toggles whether the bot should be aware of color-stripping
channel modes (+c or +S on most IRCds).""")))
conf.registerChannelValue(NoTrigger.colorAware, 'modes',
registry.SpaceSeparatedListOfStrings("c S", _("""Determines a list of modes that should
be treated as colour-blocking modes. This is usually +c (block) and +S (stripcolour) on
UnrealIRCd/InspIRCd, and just +c (stripcolour) on charybdis-based daemons.""")))
registry.SpaceSeparatedListOfStrings("c S", _("""Defines a space-separated list of modes that should
be treated as color-blocking modes. This is usually +c (block) and +S (stripcolour) on
UnrealIRCd/InspIRCd, and just +c (stripcolor) on charybdis-based daemons.""")))
conf.registerChannelValue(NoTrigger, 'blockCtcp',
registry.Boolean(False, _("""Determines whether the bot should block all CTCPs (\001 codes)
registry.Boolean(False, _("""Determines whether the bot should block all outbound channel CTCPs
except CTCP actions. If you are using the Ctcp plugin, you will want to turn this off.""")))
conf.registerChannelValue(NoTrigger, 'prefixes',
registry.SpaceSeparatedListOfStrings(' '.join(string.punctuation),
_("""Defines a space-separated list of prefix triggers the bot should ignore.""")))
conf.registerChannelValue(NoTrigger, 'suffixes',
registry.SpaceSeparatedListOfStrings('',
_("""Defines a space-separated list of suffix triggers the bot should ignore.""")))
conf.registerChannelValue(NoTrigger, 'blockBell',
registry.Boolean(True,
_("""Determines whether the bot should strip bell characters (\x07) from any messages it sends.""")))
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -46,13 +46,13 @@ except ImportError:
class NoTrigger(callbacks.Plugin):
"""Mods outFilter to prevent the bot from triggering other bots."""
"""Modifies outFilter to prevent the bot from triggering other bots."""
def __init__(self, irc):
self.__parent = super(NoTrigger, self)
self.__parent.__init__(irc)
# This uses Unicode Character 'ZERO WIDTH SPACE' (U+200B) for
# padding, which looks nicer (it's invisible) and does the trick.
# This appends the Unicode character 'ZERO WIDTH SPACE' (U+200B) for
# which is absolutely invisible and stops bots from being triggered by us.
if version_info[0] >= 3:
self.padchar = "\u200B"
else:
@ -60,6 +60,8 @@ class NoTrigger(callbacks.Plugin):
self.padchar = u('\u200B')[0]
def isChanStripColor(self, irc, channel):
"""Returns whether the given channel has a color-stripping mode (usually
+c or +S, but configurable) set."""
try:
c = irc.state.channels[channel]
for item in self.registryValue('colorAware.modes'):
@ -74,9 +76,8 @@ class NoTrigger(callbacks.Plugin):
ircutils.isChannel(msg.args[0]) and \
self.registryValue('enable', msg.args[0]):
s = msg.args[1]
prefixes = string.punctuation
rpairs = {"\007": ""}
suffixes = ("moo")
prefixes = self.registryValue('prefixes', msg.args[0])
suffixes = self.registryValue('suffixes', msg.args[0])
if self.registryValue('colorAware') and \
self.isChanStripColor(irc, msg.args[0]) and \
s.startswith(("\003", "\002", "\017", "\037", "\026")):
@ -105,11 +106,13 @@ class NoTrigger(callbacks.Plugin):
"CTCP due to config "
"plugins.notrigger.blockCtcp.", msg.args[0],
irc.network)
for k, v in rpairs.items():
s = s.replace(k, v)
if self.registryValue('blockBell', msg.args[0]):
self.log.debug("NoTrigger (%s/%s): removing bell character"
"from outgoing message.", msg.args[0], irc.network)
s = s.replace('\x07', '')
if s.startswith(tuple(prefixes)):
s = self.padchar + s
if s.endswith(suffixes):
if s.endswith(tuple(suffixes)):
s += self.padchar
msg = ircmsgs.privmsg(msg.args[0], s, msg=msg)
return msg

View File

@ -30,7 +30,6 @@
from supybot.test import *
class NoTriggerTestCase(ChannelPluginTestCase):
plugins = ('NoTrigger', 'Utilities', 'Reply')
config = {'supybot.plugins.notrigger.enable': True,
@ -39,13 +38,30 @@ class NoTriggerTestCase(ChannelPluginTestCase):
def testSpaceBeforePrefixes(self):
self.assertNotRegexp('echo !test', '^!test$')
self.assertNotRegexp('echo $test', '^\$test$')
def testSpaceBeforeNicks(self):
self.assertNotRegexp('echo example: hello', '^example: hello$')
self.assertNotRegexp('echo user1, hello', '^user1, hello$')
def testCTCPBlocking(self):
def testBlockCTCP(self):
self.assertResponse('echo \x01PING abcd\x01', 'PING abcd')
self.assertAction('reply action jumps around', 'jumps around')
def testBlockBell(self):
self.assertResponse('echo \x07', '')
self.assertResponse('echo evil bell char\x07', 'evil bell char')
def testConfigurablePrefixes(self):
with conf.supybot.plugins.notrigger.prefixes.context("moo !"):
self.assertNotRegexp('echo moo test', '^moo test$')
self.assertNotRegexp('echo !test', '^!test$')
self.assertResponse('echo .test', '.test')
def testConfigurableSuffixes(self):
with conf.supybot.plugins.notrigger.suffixes.context("abcd +"):
self.assertNotRegexp('echo moo test+', '^moo test+$')
self.assertNotRegexp('echo 1234 abcd', '^!1234 abcd$')
self.assertResponse('echo test-', 'test-')
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: