{"title":"NoSQL Injection via MongoDB Operator Injection","language":"Python","severity":"High","cwe":"CWE-943","source_lines":[9,10],"flow_lines":[9,11,12],"sink_lines":[12],"vulnerable_code":"from flask import Flask, request, jsonify\nfrom pymongo import MongoClient\n\napp = Flask(__name__)\ndb = MongoClient('mongodb://localhost:27017/').iot_platform\n\n@app.route('/api/devices/telemetry', methods=['POST'])\ndef fetch_device_telemetry():\n    device_id = request.json.get('device_id')\n    threshold = request.json.get('threshold')\n    query = {'device_id': device_id, 'temperature': threshold}\n    telemetry_data = db.sensor_readings.find(query)\n    return jsonify([doc for doc in telemetry_data])","explanation":"The code directly passes user-controlled JSON input (device_id and threshold) into a MongoDB query without validation or sanitization. An attacker can inject MongoDB operators like {'$gt': 0} or {'$ne': null} through the threshold parameter to bypass intended query logic and extract unauthorized data.","remediation":"The fix validates that device_id is strictly a string and threshold is strictly a numeric value (int or float), rejecting any dict or list inputs that could contain MongoDB operators like $gt or $ne. This prevents NoSQL operator injection by ensuring only primitive, expected types are passed into the query. Additionally, the threshold is now used in an explicit $lte operator to clarify the intended query semantics.","secure_code":"from flask import Flask, request, jsonify\nfrom pymongo import MongoClient\n\napp = Flask(__name__)\ndb = MongoClient('mongodb://localhost:27017/').iot_platform\n\n@app.route('/api/devices/telemetry', methods=['POST'])\ndef fetch_device_telemetry():\n    device_id = request.json.get('device_id')\n    threshold = request.json.get('threshold')\n\n    # Validate device_id is a plain string\n    if not isinstance(device_id, str):\n        return jsonify({'error': 'device_id must be a string'}), 400\n\n    # Validate threshold is a numeric value (int or float), not a dict/list\n    if not isinstance(threshold, (int, float)):\n        return jsonify({'error': 'threshold must be a numeric value'}), 400\n\n    query = {'device_id': device_id, 'temperature': {'$lte': threshold}}\n    telemetry_data = list(db.sensor_readings.find(query, {'_id': 0}))\n    return jsonify(telemetry_data)"}