diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index 59e2ddd1..60314160 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -434,7 +434,7 @@ def checkupdates_app(app): .format(field=app.VercodeOperation)) oldvercode = str(int(vercode)) op = app.VercodeOperation.replace("%c", oldvercode) - vercode = str(eval(op)) + vercode = str(common.calculate_math_string(op)) logging.debug("Applied vercode operation: %s -> %s" % (oldvercode, vercode)) if version and any(version.startswith(s) for s in [ diff --git a/fdroidserver/common.py b/fdroidserver/common.py index dcda699a..2cde1dc1 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -24,6 +24,7 @@ import io import os import sys import re +import ast import shutil import glob import stat @@ -3205,3 +3206,28 @@ def get_git_describe_link(): else: logging.error(_("'{path}' failed to execute!").format(path='git describe')) return '' + + +def calculate_math_string(expr): + ops = {ast.Add: operator.add, ast.Sub: operator.sub, + ast.Mult: operator.mul} + + def execute_ast(node): + if isinstance(node, ast.Num): # + return node.n + elif isinstance(node, ast.BinOp): # + return ops[type(node.op)](execute_ast(node.left), + execute_ast(node.right)) + elif isinstance(node, ast.UnaryOp): # e.g., -1 + return ops[type(node.op)](eval(node.operand)) + else: + raise SyntaxError(node) + + try: + if '#' in expr: + raise SyntaxError('no comments allowed') + return execute_ast(ast.parse(expr, mode='eval').body) + except SyntaxError as e: + raise SyntaxError("could not parse expression '{expr}', " + "only basic math operations are allowed (+, -, *)" + .format(expr=expr)) diff --git a/tests/common.TestCase b/tests/common.TestCase index f95a0820..e9dcafa7 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -779,6 +779,19 @@ class CommonTest(unittest.TestCase): self.assertEqual(('1.0-free', '1', 'com.kunzisoft.fdroidtest.applicationidsuffix'), fdroidserver.common.parse_androidmanifests(paths, app)) + def test_calculate_math_string(self): + self.assertEqual(1234, fdroidserver.common.calculate_math_string('1234')) + self.assertEqual(4, fdroidserver.common.calculate_math_string('(1+1)*2')) + self.assertEqual(2, fdroidserver.common.calculate_math_string('(1-1)*2+3*1-1')) + with self.assertRaises(SyntaxError): + fdroidserver.common.calculate_math_string('__import__("urllib")') + with self.assertRaises(SyntaxError): + fdroidserver.common.calculate_math_string('self') + with self.assertRaises(SyntaxError): + fdroidserver.common.calculate_math_string('1+1; print(1)') + with self.assertRaises(SyntaxError): + fdroidserver.common.calculate_math_string('1-1 # no comment') + if __name__ == "__main__": parser = optparse.OptionParser()