Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 85 additions & 19 deletions gemma/gm/tools/_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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}"