def setUp(self, nick='test', forceSetup=False): if not forceSetup and \ self.__class__ in (PluginTestCase, ChannelPluginTestCase): # Necessary because there's a test in here that shouldn\'t run. return SupyTestCase.setUp(self) # Just in case, let's do this. Too many people forget to call their # super methods. for irc in world.ircs[:]: irc._reallyDie() # Set conf variables appropriately. conf.supybot.reply.whenAddressedBy.chars.setValue('@') conf.supybot.reply.error.detailed.setValue(True) conf.supybot.reply.whenNotCommand.setValue(True) self.myVerbose = world.myVerbose def rmFiles(dir): for filename in os.listdir(dir): file = os.path.join(dir, filename) if os.path.isfile(file): os.remove(file) else: shutil.rmtree(file) if self.cleanConfDir: rmFiles(conf.supybot.directories.conf()) if self.cleanDataDir: rmFiles(conf.supybot.directories.data()) ircdb.users.reload() ircdb.ignores.reload() ircdb.channels.reload() if self.plugins is None: raise ValueError, 'PluginTestCase must have a "plugins" attribute.' self.nick = nick self.prefix = ircutils.joinHostmask(nick, 'user', 'host.domain.tld') self.irc = getTestIrc() MiscModule = plugin.loadPluginModule('Misc') OwnerModule = plugin.loadPluginModule('Owner') ConfigModule = plugin.loadPluginModule('Config') _ = plugin.loadPluginClass(self.irc, MiscModule) _ = plugin.loadPluginClass(self.irc, OwnerModule) _ = plugin.loadPluginClass(self.irc, ConfigModule) if isinstance(self.plugins, str): self.plugins = [self.plugins] else: for name in self.plugins: if name not in ('Owner', 'Misc', 'Config'): module = plugin.loadPluginModule(name, ignoreDeprecation=True) cb = plugin.loadPluginClass(self.irc, module) self.irc.addCallback(TestInstance) for (name, value) in self.config.iteritems(): group = conf.supybot parts = registry.split(name) if parts[0] == 'supybot': parts.pop(0) for part in parts: group = group.get(part) self.originals[group] = group() group.setValue(value)
def __init__(self, irc=None): if irc is not None: assert not irc.getCallback(self.name()) self.__parent = super(Owner, self) self.__parent.__init__(irc) # Setup command flood detection. self.commands = ircutils.FloodQueue( conf.supybot.abuse.flood.interval()) conf.supybot.abuse.flood.interval.addCallback( self.setFloodQueueTimeout) # Setup plugins and default plugins for commands. # # This needs to be done before we connect to any networks so that the # children of supybot.plugins (the actual plugins) exist and can be # loaded. for (name, s) in registry._cache.items(): if 'alwaysLoadDefault' in name or 'alwaysLoadImportant' in name: continue if name.startswith('supybot.plugins'): try: (_, _, name) = registry.split(name) except ValueError: # unpack list of wrong size. continue # This is just for the prettiness of the configuration file. # There are no plugins that are all-lowercase, so we'll at # least attempt to capitalize them. if name == name.lower(): name = name.capitalize() conf.registerPlugin(name) if name.startswith('supybot.commands.defaultPlugins'): try: (_, _, _, name) = registry.split(name) except ValueError: # unpack list of wrong size. continue registerDefaultPlugin(name, s) # Setup Irc objects, connected to networks. If world.ircs is already # populated, chances are that we're being reloaded, so don't do this. if not world.ircs: for network in conf.supybot.networks(): try: self._connect(network) except socket.error as e: self.log.error('Could not connect to %s: %s.', network, e) except Exception as e: self.log.exception('Exception connecting to %s:', network) self.log.error('Could not connect to %s: %s.', network, e)
def __init__(self, irc=None): if irc is not None: assert not irc.getCallback(self.name()) self.__parent = super(Owner, self) self.__parent.__init__(irc) # Setup log object/command. self.log = LogProxy(self.log) # Setup command flood detection. self.commands = ircutils.FloodQueue(60) # Setup plugins and default plugins for commands. # # This needs to be done before we connect to any networks so that the # children of supybot.plugins (the actual plugins) exist and can be # loaded. for (name, s) in registry._cache.iteritems(): if 'alwaysLoadDefault' in name or 'alwaysLoadImportant' in name: continue if name.startswith('supybot.plugins'): try: (_, _, name) = registry.split(name) except ValueError: # unpack list of wrong size. continue # This is just for the prettiness of the configuration file. # There are no plugins that are all-lowercase, so we'll at # least attempt to capitalize them. if name == name.lower(): name = name.capitalize() conf.registerPlugin(name) if name.startswith('supybot.commands.defaultPlugins'): try: (_, _, _, name) = registry.split(name) except ValueError: # unpack list of wrong size. continue registerDefaultPlugin(name, s) # Setup Irc objects, connected to networks. If world.ircs is already # populated, chances are that we're being reloaded, so don't do this. if not world.ircs: for network in conf.supybot.networks(): try: self._connect(network) except socket.error, e: self.log.error('Could not connect to %s: %s.', network, e) except Exception, e: self.log.exception('Exception connecting to %s:', network) self.log.error('Could not connect to %s: %s.', network, e)
def __init__(self, irc): self.__parent = super(Alias, self) self.__parent.__init__(irc) # Schema: {alias: [command, locked, commandMethod]} self.aliases = {} # XXX This should go. aliases should be a space separate list, etc. group = conf.supybot.plugins.Alias.aliases group2 = conf.supybot.plugins.Alias.escapedaliases prefixLen = len(registry.split('supybot.plugins.alias.aliases')) for (name, alias) in registry._cache.items(): name = name.lower() nameSplit = registry.split(name) if len(nameSplit) > prefixLen + 1: continue if name.startswith('supybot.plugins.alias.aliases.'): name = nameSplit[-1] conf.registerGlobalValue(group, name, registry.String('', '')) conf.registerGlobalValue(group.get(name), 'locked', registry.Boolean(False, '')) elif name.startswith('supybot.plugins.alias.escapedaliases.'): name = nameSplit[-1] conf.registerGlobalValue(group2, name, registry.String('', '')) conf.registerGlobalValue(group2.get(name), 'locked', registry.Boolean(False, '')) for (name, value) in group.getValues(fullNames=False): name = name.lower() # Just in case. command = value() locked = value.locked() self.aliases[name] = [command, locked, None] for (name, value) in group2.getValues(fullNames=False): name = name.lower() # Just in case. command = value() locked = value.locked() self.aliases[unescapeAlias(name)] = [command, locked, None] for (alias, (command, locked, _)) in self.aliases.copy().items(): try: self.addAlias(irc, alias, command, locked) except Exception as e: self.log.exception( 'Exception when trying to add alias %s. ' 'Removing from the Alias database.', alias) del self.aliases[alias]
def __init__(self, irc): self.__parent = super(Alias, self) self.__parent.__init__(irc) # Schema: {alias: [command, locked, commandMethod]} self.aliases = {} # XXX This should go. aliases should be a space separate list, etc. group = conf.supybot.plugins.Alias.aliases group2 = conf.supybot.plugins.Alias.escapedaliases prefixLen = len(registry.split('supybot.plugins.alias.aliases')) for (name, alias) in registry._cache.items(): name = name.lower() nameSplit = registry.split(name) if len(nameSplit) > prefixLen+1: continue if name.startswith('supybot.plugins.alias.aliases.'): name = nameSplit[-1] conf.registerGlobalValue(group, name, registry.String('', '')) conf.registerGlobalValue(group.get(name), 'locked', registry.Boolean(False, '')) elif name.startswith('supybot.plugins.alias.escapedaliases.'): name = nameSplit[-1] conf.registerGlobalValue(group2, name, registry.String('', '')) conf.registerGlobalValue(group2.get(name), 'locked', registry.Boolean(False, '')) for (name, value) in group.getValues(fullNames=False): name = name.lower() # Just in case. command = value() locked = value.locked() self.aliases[name] = [command, locked, None] for (name, value) in group2.getValues(fullNames=False): name = name.lower() # Just in case. command = value() locked = value.locked() self.aliases[unescapeAlias(name)] = [command, locked, None] for (alias, (command, locked, _)) in self.aliases.copy().items(): try: self.addAlias(irc, alias, command, locked) except Exception as e: self.log.exception('Exception when trying to add alias %s. ' 'Removing from the Alias database.', alias) del self.aliases[alias]
def getCapability(name): capability = 'owner' # Default to requiring the owner capability. parts = registry.split(name) while parts: part = parts.pop() if ircutils.isChannel(part): # If a registry value has a channel in it, it requires a channel.op # capability, or so we assume. We'll see if we're proven wrong. capability = ircdb.makeChannelCapability(part, 'op') ### Do more later, for specific capabilities/sections. return capability
def registerChannelValue(group, name, value): value._supplyDefault = True value.channelValue = True g = group.register(name, value) gname = g._name.lower() for name in registry._cache.iterkeys(): if name.lower().startswith(gname) and len(gname) < len(name): name = name[len(gname) + 1:] # +1 for . parts = registry.split(name) if len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]): # This gets the channel values so they always persist. g.get(parts[0])()
def registerChannelValue(group, name, value): value._supplyDefault = True value.channelValue = True g = group.register(name, value) gname = g._name.lower() for name in registry._cache.iterkeys(): if name.lower().startswith(gname) and len(gname) < len(name): name = name[len(gname)+1:] # +1 for . parts = registry.split(name) if len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]): # This gets the channel values so they always persist. g.get(parts[0])()
def getWrapper(name): parts = registry.split(name) if not parts or parts[0] not in ('supybot', 'users'): raise registry.InvalidRegistryName(name) group = getattr(conf, parts.pop(0)) while parts: try: group = group.get(parts.pop(0)) # We'll catch registry.InvalidRegistryName and re-raise it here so # that we have a useful error message for the user. except (registry.NonExistentRegistryEntry, registry.InvalidRegistryName): raise registry.InvalidRegistryName(name) return group
def getWrapper(name): parts = registry.split(name) if not parts or parts[0] not in ('supybot', 'users'): raise InvalidRegistryName, name group = getattr(conf, parts.pop(0)) while parts: try: group = group.get(parts.pop(0)) # We'll catch registry.InvalidRegistryName and re-raise it here so # that we have a useful error message for the user. except (registry.NonExistentRegistryEntry, registry.InvalidRegistryName): raise registry.InvalidRegistryName, name return group
def getWrapper(name): parts = registry.split(name) if not parts or parts[0] not in ('supybot', 'users'): raise registry.InvalidRegistryName(name) group = getattr(conf, parts.pop(0)) while parts: part = parts.pop(0) if group.__hasattr__(part): group = group.get(part) else: # We'll raise registry.InvalidRegistryName here so # that we have a useful error message for the user. raise registry.InvalidRegistryName(name) return group
def search(self, irc, msg, args, word): """<word> Searches for <word> in the current configuration variables. """ L = [] for (name, x) in conf.supybot.getValues(getChildren=True): if word in name.lower(): possibleChannel = registry.split(name)[-1] if not ircutils.isChannel(possibleChannel): L.append(name) if L: irc.reply(format('%L', L)) else: irc.reply(_('There were no matching configuration variables.'))
def searchvalues(self, irc, msg, args, word): """<word> Searches for <word> in the values of current configuration variables. """ L = [] for (name, x) in conf.supybot.getValues(getChildren=True): if (hasattr(x, 'value') # not a group and word in str(x()).lower()): last_name_part = registry.split(name)[-1] L.append(name) if L: irc.reply(format('%L', L)) else: irc.reply(_('There were no matching configuration variables.'))
def searchhelp(self, irc, msg, args, phrase): """<phrase> Searches for <phrase> in the help of current configuration variables. """ L = [] for (name, x) in conf.supybot.getValues(getChildren=True): if phrase in x.help().lower(): last_name_part = registry.split(name)[-1] if not irc.isChannel(last_name_part) \ and not last_name_part.startswith(':'): # network L.append(name) if L: irc.reply(format('%L', L)) else: irc.reply(_('There were no matching configuration variables.'))
def registryValue(plugin, name, channel=None, value=True): group = conf.supybot.plugins.get(plugin) names = registry.split(name) for name in names: group = group.get(name) if channel is not None: try: if ircutils.isChannel(channel): group = group.get(channel) else: log.debug('registryValue got channel=%r', channel) except registry.NonExistentRegistryEntry: log.debug('non existent registry entry %r for channel %r', name, channel) pass if value: return group() else: return group
def getCapability(name): capability = 'owner' # Default to requiring the owner capability. if not name.startswith('supybot') and not name.startswith('users'): name = 'supybot.' + name parts = registry.split(name) group = getattr(conf, parts.pop(0)) while parts: part = parts.pop(0) group = group.get(part) if not getattr(group, '_opSettable', True): return 'owner' if ircutils.isChannel(part): # If a registry value has a channel in it, it requires a # 'channel,op' capability, or so we assume. We'll see if we're # proven wrong. capability = ircdb.makeChannelCapability(part, 'op') ### Do more later, for specific capabilities/sections. return capability
def isReadOnly(name): """Prevents changing certain config variables to gain shell access via a vulnerable IRC network.""" parts = registry.split(name.lower()) if parts[0] != 'supybot': parts.insert(0, 'supybot') if parts == ['supybot', 'commands', 'allowshell'] and \ not conf.supybot.commands.allowShell(): # allow setting supybot.commands.allowShell from True to False, # but not from False to True. # Otherwise an IRC network could overwrite it. return True elif parts[0:2] == ['supybot', 'directories'] and \ not conf.supybot.commands.allowShell(): # Setting plugins directory allows for arbitrary code execution if # an attacker can both use the IRC network to MITM and upload files # on the server (eg. with a web CMS). # Setting other directories allows writing data at arbitrary # locations. return True else: return False