{"title":"Code Injection via eval() on Untrusted User Input","language":"Python","severity":"Critical","cwe":"CWE-94","source_lines":[4],"flow_lines":[4,9],"sink_lines":[9],"vulnerable_code":"@app.route('/api/iot/device/metrics', methods=['POST'])\ndef calculate_device_metrics():\n    device_id = request.json.get('device_id')\n    metric_formula = request.json.get('formula')\n    sensor_data = fetch_sensor_readings(device_id)\n    temp = sensor_data['temperature']\n    humidity = sensor_data['humidity']\n    pressure = sensor_data['pressure']\n    try:\n        computed_value = eval(metric_formula)\n        store_metric(device_id, computed_value)\n        return jsonify({'device': device_id, 'result': computed_value, 'status': 'success'})\n    except Exception as e:\n        return jsonify({'error': 'Invalid formula syntax'}), 400","explanation":"The application accepts an untrusted user-controlled 'formula' parameter via request.json.get('formula') and directly passes it to the eval() function without any validation or sanitization. This allows attackers to execute arbitrary Python code on the server by injecting malicious expressions instead of legitimate mathematical formulas.","remediation":"The fix replaces the dangerous eval() call with a custom safe_eval_formula() function that uses Python's ast module to parse the formula into an Abstract Syntax Tree, then walks the tree and only evaluates nodes that are numeric constants, whitelisted variable names, or safe mathematical operators. This approach prevents any arbitrary code execution while still allowing legitimate mathematical expressions using sensor data variables.","secure_code":"import ast\nimport operator\n\n# Safe mathematical operations whitelist\nSAFE_OPERATORS = {\n    ast.Add: operator.add,\n    ast.Sub: operator.sub,\n    ast.Mult: operator.mul,\n    ast.Div: operator.truediv,\n    ast.FloorDiv: operator.floordiv,\n    ast.Mod: operator.mod,\n    ast.Pow: operator.pow,\n    ast.USub: operator.neg,\n    ast.UAdd: operator.pos,\n}\n\ndef safe_eval_formula(formula, variables):\n    \"\"\"Safely evaluate a mathematical formula with only allowed operations and variables.\"\"\"\n    try:\n        tree = ast.parse(formula, mode='eval')\n    except SyntaxError:\n        raise ValueError(\"Invalid formula syntax\")\n\n    def _eval_node(node):\n        if isinstance(node, ast.Expression):\n            return _eval_node(node.body)\n        elif isinstance(node, ast.Constant):\n            if isinstance(node.value, (int, float)):\n                return node.value\n            raise ValueError(f\"Unsupported constant type: {type(node.value).__name__}\")\n        elif isinstance(node, ast.Name):\n            if node.id in variables:\n                return variables[node.id]\n            raise ValueError(f\"Unknown variable: {node.id}\")\n        elif isinstance(node, ast.BinOp):\n            op_type = type(node.op)\n            if op_type not in SAFE_OPERATORS:\n                raise ValueError(f\"Unsupported operator: {op_type.__name__}\")\n            left = _eval_node(node.left)\n            right = _eval_node(node.right)\n            if op_type == ast.Pow and right > 100:\n                raise ValueError(\"Exponent too large\")\n            if op_type in (ast.Div, ast.FloorDiv, ast.Mod) and right == 0:\n                raise ValueError(\"Division by zero\")\n            return SAFE_OPERATORS[op_type](left, right)\n        elif isinstance(node, ast.UnaryOp):\n            op_type = type(node.op)\n            if op_type not in SAFE_OPERATORS:\n                raise ValueError(f\"Unsupported unary operator: {op_type.__name__}\")\n            operand = _eval_node(node.operand)\n            return SAFE_OPERATORS[op_type](operand)\n        else:\n            raise ValueError(f\"Unsupported expression type: {type(node).__name__}\")\n\n    return _eval_node(tree)\n\n\n@app.route('/api/iot/device/metrics', methods=['POST'])\ndef calculate_device_metrics():\n    device_id = request.json.get('device_id')\n    metric_formula = request.json.get('formula')\n\n    if not device_id or not metric_formula:\n        return jsonify({'error': 'Missing device_id or formula'}), 400\n\n    if len(metric_formula) > 500:\n        return jsonify({'error': 'Formula too long'}), 400\n\n    sensor_data = fetch_sensor_readings(device_id)\n    variables = {\n        'temp': sensor_data['temperature'],\n        'humidity': sensor_data['humidity'],\n        'pressure': sensor_data['pressure'],\n    }\n\n    try:\n        computed_value = safe_eval_formula(metric_formula, variables)\n        store_metric(device_id, computed_value)\n        return jsonify({'device': device_id, 'result': computed_value, 'status': 'success'})\n    except ValueError as e:\n        return jsonify({'error': str(e)}), 400\n    except Exception as e:\n        return jsonify({'error': 'Invalid formula syntax'}), 400"}