{"title":"Symlink Race Condition in Insecure Temporary File Creation","language":"Python","severity":"High","cwe":"CWE-367","source_lines":[9],"flow_lines":[9,10,11,12],"sink_lines":[12],"vulnerable_code":"import os\nimport tempfile\nimport time\n\ndef export_iot_device_telemetry(device_id, metrics_data):\n    temp_dir = tempfile.gettempdir()\n    export_filename = f\"device_{device_id}_telemetry.json\"\n    export_path = os.path.join(temp_dir, export_filename)\n    if os.path.exists(export_path):\n        os.remove(export_path)\n    time.sleep(0.1)\n    with open(export_path, 'w') as telemetry_file:\n        telemetry_file.write(metrics_data)\n    os.chmod(export_path, 0o644)\n    return export_path","explanation":"The code creates a predictable temporary file path using untrusted device_id input in a shared directory. Between checking if the file exists (line 9), deleting it (line 10), sleeping (line 11), and recreating it (line 12), an attacker can create a symlink at that path pointing to a sensitive file, causing the application to overwrite arbitrary files on the system.","remediation":"The fix uses tempfile.mkstemp() which atomically creates a unique temporary file with restrictive permissions (0600), eliminating the race condition window entirely. It also sanitizes the device_id input to prevent path traversal attacks and removes the predictable filename pattern and sleep delay that widened the exploitation window.","secure_code":"import os\nimport tempfile\nimport re\n\ndef export_iot_device_telemetry(device_id, metrics_data):\n    sanitized_device_id = re.sub(r'[^a-zA-Z0-9_\\-]', '', str(device_id))\n    if not sanitized_device_id:\n        raise ValueError(\"Invalid device_id\")\n    \n    prefix = f\"device_{sanitized_device_id}_telemetry_\"\n    \n    fd, export_path = tempfile.mkstemp(prefix=prefix, suffix='.json')\n    try:\n        with os.fdopen(fd, 'w') as telemetry_file:\n            telemetry_file.write(metrics_data)\n    except Exception:\n        os.unlink(export_path)\n        raise\n    \n    return export_path"}