Python eval() Function Detection Vulnerability

Critical Risk Code Injection
PythonevalCode InjectionRemote Code ExecutionDynamic Code ExecutionInput Validation

What it is

Application uses the eval() function which can execute arbitrary Python code, creating serious security risks including remote code execution when processing untrusted input.

from flask import Flask, request @app.route('/calculate') def calculate(): # Vulnerable: eval() with user input allows arbitrary code execution expression = request.args.get('expr') try: result = eval(expression) # DANGEROUS: RCE vulnerability return f'Result: {result}' except: return 'Error in calculation', 400 @app.route('/config') def update_config(): # Vulnerable: Using eval() to parse configuration config_data = request.form.get('config') try: # Dangerous: eval() allows arbitrary code execution config = eval(config_data) # Can execute malicious code return f'Config updated: {config}' except: return 'Invalid config', 400 @app.route('/process') def process_data(): # Vulnerable: Dynamic function execution func_name = request.args.get('function') args = request.args.get('args') # Extremely dangerous: eval() with user-controlled function calls code = f'{func_name}({args})' result = eval(code) # Complete RCE vulnerability return str(result)
from flask import Flask, request import ast import operator import json import re # Safe operators for mathematical expressions SAFE_OPERATORS = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.Pow: operator.pow, ast.USub: operator.neg, ast.UAdd: operator.pos } def safe_eval_math(expression): """Safely evaluate mathematical expressions.""" try: # Parse the expression into an AST node = ast.parse(expression, mode='eval') return eval_ast_node(node.body) except (ValueError, TypeError, SyntaxError): raise ValueError('Invalid mathematical expression') def eval_ast_node(node): """Recursively evaluate AST nodes with only safe operations.""" if isinstance(node, ast.Constant): # Numbers return node.value elif isinstance(node, ast.BinOp): # Binary operations left = eval_ast_node(node.left) right = eval_ast_node(node.right) if type(node.op) in SAFE_OPERATORS: return SAFE_OPERATORS[type(node.op)](left, right) else: raise ValueError('Unsupported operation') elif isinstance(node, ast.UnaryOp): # Unary operations operand = eval_ast_node(node.operand) if type(node.op) in SAFE_OPERATORS: return SAFE_OPERATORS[type(node.op)](operand) else: raise ValueError('Unsupported unary operation') else: raise ValueError('Unsupported expression type') @app.route('/calculate') def calculate(): # Secure: Use safe mathematical expression evaluator expression = request.args.get('expr', '') # Validate expression format if not re.match(r'^[0-9+\-*/()\. ]+$', expression): return 'Invalid expression format', 400 try: result = safe_eval_math(expression) return f'Result: {result}' except ValueError as e: return f'Error: {str(e)}', 400 except ZeroDivisionError: return 'Error: Division by zero', 400 @app.route('/config') def update_config(): # Secure: Use JSON parsing instead of eval() config_data = request.form.get('config', '{}') try: # Safe: JSON parsing instead of eval() config = json.loads(config_data) # Validate configuration structure allowed_keys = ['theme', 'language', 'timeout'] if not isinstance(config, dict): return 'Config must be a dictionary', 400 for key in config.keys(): if key not in allowed_keys: return f'Invalid config key: {key}', 400 return f'Config updated: {config}' except json.JSONDecodeError: return 'Invalid JSON format', 400 @app.route('/process') def process_data(): # Secure: Use allowlist of permitted functions func_name = request.args.get('function', '') args_str = request.args.get('args', '') # Allowlist of safe functions safe_functions = { 'upper': lambda x: x.upper() if isinstance(x, str) else '', 'lower': lambda x: x.lower() if isinstance(x, str) else '', 'length': lambda x: len(x) if isinstance(x, (str, list)) else 0, 'reverse': lambda x: x[::-1] if isinstance(x, (str, list)) else x } if func_name not in safe_functions: return 'Function not allowed', 400 try: # Safe: Parse arguments as JSON args = json.loads(args_str) if args_str else '' result = safe_functions[func_name](args) return str(result) except (json.JSONDecodeError, TypeError) as e: return f'Error processing: {str(e)}', 400 # Alternative secure approach using ast.literal_eval for simple data @app.route('/parse_data') def parse_data(): # Secure: Use ast.literal_eval for safe literal evaluation data = request.args.get('data', '') try: # Safe: Only evaluates literals (strings, numbers, tuples, lists, dicts, booleans, None) parsed = ast.literal_eval(data) return f'Parsed data: {parsed}' except (ValueError, SyntaxError): return 'Invalid data format', 400

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Code uses eval() on user input: result = eval(request.form['expression']). eval() executes arbitrary Python code. Attackers inject: __import__('os').system('command') for code execution. Common when developers need to evaluate expressions but choose insecure eval() over safe alternatives like ast.literal_eval().

Root causes

Using eval() to Parse or Process User-Supplied Data

Code uses eval() on user input: result = eval(request.form['expression']). eval() executes arbitrary Python code. Attackers inject: __import__('os').system('command') for code execution. Common when developers need to evaluate expressions but choose insecure eval() over safe alternatives like ast.literal_eval().

Using eval() with Incomplete or Bypassable Validation

Validation before eval() insufficient: if 'import' not in expr: eval(expr). Blocklists miss alternatives: __import__, getattr(__builtins__, 'eval'), or encoded strings. Even restricting __builtins__ can be bypassed through inheritance chain. String validation cannot make eval() safe with untrusted input.

Using eval() for Configuration or Data Deserialization

Loading config with eval(): config = eval(config_string). Treating Python literals as data format. Attackers inject code in configuration files. Alternatives like json.loads(), yaml.safe_load(), or ast.literal_eval() safer. eval() inappropriate for data parsing, only code execution.

Dynamically Evaluating Code from External Sources

Evaluating templates, plugins, or user scripts: eval(plugin_code). Processing formulas from spreadsheets or user-defined functions. External code not properly sandboxed. eval() without isolation gives full Python access. Plugin systems require proper sandboxing, not direct eval().

Using eval() in Debugging or Development Code Left in Production

Debug features using eval(): if debug: eval(debug_command). Development utilities accidentally deployed. Interactive Python shells or admin panels with eval(). Debug functionality should be completely removed from production, not just disabled with flags. Presence of eval() creates vulnerability.

Fixes

1

Never Use eval() on Untrusted Input, Use ast.literal_eval() Instead

For safe literal evaluation: import ast; data = ast.literal_eval(user_string). Only evaluates literals: strings, numbers, lists, dicts, booleans, None. No code execution. No imports or function calls. Appropriate for parsing Python literal expressions safely.

2

Use Safe Parsers for Data: json.loads(), yaml.safe_load()

For data deserialization, use safe formats: data = json.loads(json_string) or yaml.safe_load(yaml_string). JSON only supports data types, no code. YAML SafeLoader prevents object instantiation. Proper parsers for data eliminate code execution risk entirely.

3

Use Expression Evaluators or Domain-Specific Languages

For expression evaluation, use safe libraries: simpleeval, numexpr for math, or pyparsing for custom grammar. These libraries provide restricted expression evaluation without full Python access. Define allowed functions and operators. Safe evaluation without eval() code execution risk.

4

Implement Plugin Systems with Proper Sandboxing

Use RestrictedPython for sandboxing: from RestrictedPython import compile_restricted. Restricts imports, builtins, and dangerous operations. Or use separate processes with limited permissions. Plugin systems need isolation, not direct eval(). Validate plugin code through security review before execution.

5

Remove All eval() Usage from Production Code

Eliminate eval() entirely from production: search codebase for eval( and remove all occurrences. Replace with appropriate alternatives based on use case. If absolutely required, use sandboxed environment with restricted __builtins__ and thorough security review. Never expose eval() to user input.

6

Use Static Analysis to Detect eval() Usage

Scan codebase with bandit: bandit -r . to detect eval() usage. Add pre-commit hooks preventing eval(). Use linters with security rules. Fail CI/CD builds if eval() detected. Static analysis ensures no accidental eval() introduction during development.

Detect This Vulnerability in Your Code

Sourcery automatically identifies python eval() function detection vulnerability and many other security issues in your codebase.