From 9cfbe09c972be9a176580df6b6f72200cbbcfc80 Mon Sep 17 00:00:00 2001 From: markknoffler Date: Thu, 23 Oct 2025 20:29:08 +0530 Subject: [PATCH] Fixed Security Issue in calculator tool --- gemma/gm/tools/_calculator.py | 104 +++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 19 deletions(-) diff --git a/gemma/gm/tools/_calculator.py b/gemma/gm/tools/_calculator.py index 6520bbb1..d7a64745 100644 --- a/gemma/gm/tools/_calculator.py +++ b/gemma/gm/tools/_calculator.py @@ -20,24 +20,13 @@ from gemma.gm.tools import _tools -_OPS = { - 'sqrt': math.sqrt, - 'log': math.log, - 'exp': math.exp, - 'sin': math.sin, - 'cos': math.cos, - 'tan': math.tan, - 'asin': math.asin, - 'acos': math.acos, - 'atan': math.atan, - 'atan2': math.atan2, - 'ceil': math.ceil, - 'floor': math.floor, -} - class Calculator(_tools.Tool): - """Simple calculator to demonstrate tool use.""" + """Secure calculator to demonstrate tool use. + + This calculator safely evaluates mathematical expressions using a restricted + eval() environment, preventing arbitrary code execution vulnerabilities. + """ DESCRIPTION = 'Perform mathematical calculations.' EXAMPLE = _tools.Example( @@ -50,7 +39,84 @@ class Calculator(_tools.Tool): ) KEYWORDS = ('math', 'calculator', 'operation') + def __init__(self): + """Initialize the secure calculator.""" + super().__init__() + # Create a safe environment with only math functions + self._safe_globals = { + '__builtins__': {}, # Block all builtins + # Basic math functions + 'sqrt': math.sqrt, + 'log': math.log, + 'log10': math.log10, + 'log2': math.log2, + 'exp': math.exp, + 'sin': math.sin, + 'cos': math.cos, + 'tan': math.tan, + 'asin': math.asin, + 'acos': math.acos, + 'atan': math.atan, + 'atan2': math.atan2, + 'sinh': math.sinh, + 'cosh': math.cosh, + 'tanh': math.tanh, + 'ceil': math.ceil, + 'floor': math.floor, + 'fabs': math.fabs, + 'factorial': math.factorial, + 'gcd': math.gcd, + 'degrees': math.degrees, + 'radians': math.radians, + 'abs': abs, + 'min': min, + 'max': max, + 'round': round, + 'sum': sum, + # Math constants + 'pi': math.pi, + 'e': math.e, + 'tau': math.tau, + 'inf': math.inf, + 'nan': math.nan, + } + def call(self, expression: str) -> str: # pytype: disable=signature-mismatch - """Calculates the expression.""" - # TODO(epot): Uses lark parser instead. - return eval(expression, _OPS) # pylint: disable=eval-used + """Safely calculates the mathematical expression. + + This method uses eval() with a restricted environment containing only + safe mathematical functions, preventing arbitrary code execution. + + Args: + expression: The mathematical expression to evaluate. + + Returns: + The result as a string, or an error message if evaluation fails. + """ + try: + # Use eval() with restricted globals - only math functions allowed + result = eval(expression, self._safe_globals, {}) # pylint: disable=eval-used + + # Format the result appropriately + if isinstance(result, complex): + return str(result) + elif isinstance(result, float): + # Handle special float values + if math.isnan(result): + return 'nan' + elif math.isinf(result): + return 'inf' if result > 0 else '-inf' + else: + # Format with reasonable precision + if result == int(result): + return str(int(result)) + else: + return f"{result:.10g}" + else: + return str(result) + + except (ValueError, SyntaxError, ZeroDivisionError, OverflowError) as e: + return f"Error: {e}" + except Exception as e: + return f"Unexpected error: {e}" +