mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 11:09:23 +01:00
ChannelStats: Use the safe math evaluator.
This commit is contained in:
parent
99dd6f1506
commit
a6ae9f51a3
@ -44,6 +44,7 @@ import supybot.ircmsgs as ircmsgs
|
|||||||
import supybot.plugins as plugins
|
import supybot.plugins as plugins
|
||||||
import supybot.ircutils as ircutils
|
import supybot.ircutils as ircutils
|
||||||
import supybot.callbacks as callbacks
|
import supybot.callbacks as callbacks
|
||||||
|
from supybot.utils.math_evaluator import safe_eval, InvalidNode
|
||||||
|
|
||||||
_ = PluginInternationalization('ChannelStats')
|
_ = PluginInternationalization('ChannelStats')
|
||||||
|
|
||||||
@ -292,15 +293,6 @@ class ChannelStats(callbacks.Plugin):
|
|||||||
name, channel))
|
name, channel))
|
||||||
stats = wrap(stats, ['channeldb', additional('something')])
|
stats = wrap(stats, ['channeldb', additional('something')])
|
||||||
|
|
||||||
_calc_match_forbidden_chars = re.compile('[_\[\]]')
|
|
||||||
_env = {'__builtins__': types.ModuleType('__builtins__')}
|
|
||||||
_env.update(math.__dict__)
|
|
||||||
def _factorial(x):
|
|
||||||
if x<=10000:
|
|
||||||
return math.factorial(x)
|
|
||||||
else:
|
|
||||||
raise Exception('factorial argument too large')
|
|
||||||
_env['factorial'] = _factorial
|
|
||||||
@internationalizeDocstring
|
@internationalizeDocstring
|
||||||
def rank(self, irc, msg, args, channel, expr):
|
def rank(self, irc, msg, args, channel, expr):
|
||||||
"""[<channel>] <stat expression>
|
"""[<channel>] <stat expression>
|
||||||
@ -314,30 +306,26 @@ class ChannelStats(callbacks.Plugin):
|
|||||||
if msg.nick not in irc.state.channels[channel].users:
|
if msg.nick not in irc.state.channels[channel].users:
|
||||||
irc.error(format('You must be in %s to use this command.', channel))
|
irc.error(format('You must be in %s to use this command.', channel))
|
||||||
return
|
return
|
||||||
# XXX I could do this the right way, and abstract out a safe eval,
|
|
||||||
# or I could just copy/paste from the Math plugin.
|
|
||||||
if self._calc_match_forbidden_chars.match(expr):
|
|
||||||
irc.error(_('There\'s really no reason why you should have '
|
|
||||||
'underscores or brackets in your mathematical '
|
|
||||||
'expression. Please remove them.'), Raise=True)
|
|
||||||
if 'lambda' in expr:
|
|
||||||
irc.error(_('You can\'t use lambda in this command.'), Raise=True)
|
|
||||||
expr = expr.lower()
|
expr = expr.lower()
|
||||||
users = []
|
users = []
|
||||||
for ((c, id), stats) in self.db.items():
|
for ((c, id), stats) in self.db.items():
|
||||||
if ircutils.strEqual(c, channel) and \
|
if ircutils.strEqual(c, channel) and \
|
||||||
(id == 0 or ircdb.users.hasUser(id)):
|
(id == 0 or ircdb.users.hasUser(id)):
|
||||||
e = self._env.copy()
|
e = {}
|
||||||
for attr in stats._values:
|
for attr in stats._values:
|
||||||
e[attr] = float(getattr(stats, attr))
|
e[attr] = float(getattr(stats, attr))
|
||||||
try:
|
try:
|
||||||
v = eval(expr, e, e)
|
v = safe_eval(expr, allow_ints=True, variables=e)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
v = float('inf')
|
v = float('inf')
|
||||||
except NameError as e:
|
except NameError as e:
|
||||||
irc.errorInvalid(_('stat variable'), str(e).split()[1])
|
irc.errorInvalid(_('stat variable'), str(e))
|
||||||
|
except InvalidNode as e:
|
||||||
|
irc.error(_('Invalid syntax: %s') % e.args[0], Raise=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
irc.error(utils.exnToString(e), Raise=True)
|
irc.error(utils.exnToString(e), Raise=True)
|
||||||
|
else:
|
||||||
|
v = float(v)
|
||||||
if id == 0:
|
if id == 0:
|
||||||
users.append((v, irc.nick))
|
users.append((v, irc.nick))
|
||||||
else:
|
else:
|
||||||
|
@ -27,6 +27,10 @@
|
|||||||
# POSSIBILITY OF SUCH DAMAGE.
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
###
|
###
|
||||||
|
|
||||||
|
"""A safe evaluator for math expressions using Python syntax.
|
||||||
|
Unlike eval(), it can be run on untrusted input.
|
||||||
|
"""
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
import math
|
import math
|
||||||
import cmath
|
import cmath
|
||||||
@ -122,9 +126,12 @@ UNSAFE_ENV.update(filter_module(math, 'ceil floor factorial gcd'.split()))
|
|||||||
# It would be nice if ast.literal_eval used a visitor so we could subclass
|
# It would be nice if ast.literal_eval used a visitor so we could subclass
|
||||||
# to extend it, but it doesn't, so let's reimplement it entirely.
|
# to extend it, but it doesn't, so let's reimplement it entirely.
|
||||||
class SafeEvalVisitor(ast.NodeVisitor):
|
class SafeEvalVisitor(ast.NodeVisitor):
|
||||||
def __init__(self, allow_ints):
|
def __init__(self, allow_ints, variables=None):
|
||||||
self._allow_ints = allow_ints
|
self._allow_ints = allow_ints
|
||||||
self._env = UNSAFE_ENV if allow_ints else SAFE_ENV
|
self._env = UNSAFE_ENV if allow_ints else SAFE_ENV
|
||||||
|
if variables:
|
||||||
|
self._env = self._env.copy()
|
||||||
|
self._env.update(variables)
|
||||||
|
|
||||||
def _convert_num(self, x):
|
def _convert_num(self, x):
|
||||||
"""Converts numbers to complex if ints are not allowed."""
|
"""Converts numbers to complex if ints are not allowed."""
|
||||||
@ -178,6 +185,6 @@ class SafeEvalVisitor(ast.NodeVisitor):
|
|||||||
def generic_visit(self, node):
|
def generic_visit(self, node):
|
||||||
raise InvalidNode('illegal construct %s' % node.__class__.__name__)
|
raise InvalidNode('illegal construct %s' % node.__class__.__name__)
|
||||||
|
|
||||||
def safe_eval(text, allow_ints):
|
def safe_eval(text, allow_ints, variables=None):
|
||||||
node = ast.parse(text, mode='eval')
|
node = ast.parse(text, mode='eval')
|
||||||
return SafeEvalVisitor(allow_ints).visit(node)
|
return SafeEvalVisitor(allow_ints, variables=variables).visit(node)
|
||||||
|
Loading…
Reference in New Issue
Block a user