{"title":"Unsafe ast.literal_eval Replacement via eval() on Untrusted Input","language":"Python","severity":"Critical","cwe":"CWE-94","source_lines":[10],"flow_lines":[10,15],"sink_lines":[15],"vulnerable_code":"from flask import Flask, request, jsonify\nimport hmac\nimport hashlib\n\napp = Flask(__name__)\n\n@app.route('/hsm/verify_signature', methods=['POST'])\ndef hsm_signature_verification():\n    payload_data = request.json.get('payload')\n    sig_params = request.json.get('signature_params')\n    key_id = request.json.get('key_identifier')\n    \n    hsm_key = retrieve_hsm_key(key_id)\n    \n    sig_config = eval(sig_params)\n    \n    algorithm = sig_config.get('algorithm', 'sha256')\n    encoding = sig_config.get('encoding', 'utf-8')\n    \n    computed_sig = hmac.new(\n        hsm_key.encode(),\n        payload_data.encode(encoding),\n        getattr(hashlib, algorithm)\n    ).hexdigest()\n    \n    return jsonify({'signature': computed_sig, 'key_id': key_id})\n\ndef retrieve_hsm_key(kid):\n    return 'hsm_master_key_' + kid","explanation":"The application accepts untrusted user input via 'signature_params' from a JSON POST request and directly passes it to eval() without any validation or sanitization. This allows an attacker to execute arbitrary Python code on the server, leading to complete system compromise including data exfiltration, privilege escalation, or denial of service.","remediation":"The fix replaces the dangerous eval() call with json.loads() for safe parsing of the signature parameters string, and also handles the case where the input is already a dictionary. Additionally, whitelist validation is added for both the algorithm and encoding parameters to prevent attackers from accessing arbitrary hashlib attributes or using unexpected encodings.","secure_code":"from flask import Flask, request, jsonify\nimport hmac\nimport hashlib\nimport json\n\napp = Flask(__name__)\n\nALLOWED_ALGORITHMS = {'sha256', 'sha384', 'sha512', 'sha224', 'sha1', 'md5'}\nALLOWED_ENCODINGS = {'utf-8', 'ascii', 'latin-1'}\n\n@app.route('/hsm/verify_signature', methods=['POST'])\ndef hsm_signature_verification():\n    payload_data = request.json.get('payload')\n    sig_params = request.json.get('signature_params')\n    key_id = request.json.get('key_identifier')\n    \n    hsm_key = retrieve_hsm_key(key_id)\n    \n    if isinstance(sig_params, str):\n        try:\n            sig_config = json.loads(sig_params)\n        except (json.JSONDecodeError, ValueError):\n            return jsonify({'error': 'Invalid signature_params format. Must be a valid JSON string or object.'}), 400\n    elif isinstance(sig_params, dict):\n        sig_config = sig_params\n    else:\n        return jsonify({'error': 'Invalid signature_params type. Must be a JSON string or object.'}), 400\n    \n    if not isinstance(sig_config, dict):\n        return jsonify({'error': 'signature_params must resolve to a dictionary.'}), 400\n    \n    algorithm = sig_config.get('algorithm', 'sha256')\n    encoding = sig_config.get('encoding', 'utf-8')\n    \n    if algorithm not in ALLOWED_ALGORITHMS:\n        return jsonify({'error': f'Invalid algorithm. Allowed: {sorted(ALLOWED_ALGORITHMS)}'}), 400\n    \n    if encoding not in ALLOWED_ENCODINGS:\n        return jsonify({'error': f'Invalid encoding. Allowed: {sorted(ALLOWED_ENCODINGS)}'}), 400\n    \n    computed_sig = hmac.new(\n        hsm_key.encode(),\n        payload_data.encode(encoding),\n        getattr(hashlib, algorithm)\n    ).hexdigest()\n    \n    return jsonify({'signature': computed_sig, 'key_id': key_id})\n\ndef retrieve_hsm_key(kid):\n    return 'hsm_master_key_' + kid"}