{"title":"TOCTOU Race Condition in Temporary File Handling","language":"Python","severity":"High","cwe":"CWE-367","source_lines":[8],"flow_lines":[8,9,12],"sink_lines":[9,12],"vulnerable_code":"import os\nimport tempfile\nimport json\n\ndef store_iot_device_credentials(device_id, api_key, secret_token):\n    temp_dir = tempfile.gettempdir()\n    cred_file = os.path.join(temp_dir, f\"iot_device_{device_id}.json\")\n    if not os.path.exists(cred_file):\n        with open(cred_file, 'w') as f:\n            credentials = {\"device_id\": device_id, \"api_key\": api_key, \"secret\": secret_token, \"provisioned\": True}\n            json.dump(credentials, f)\n        os.chmod(cred_file, 0o600)\n        return f\"Credentials stored securely for device {device_id}\"\n    else:\n        return \"Device already provisioned\"","explanation":"The code exhibits a Time-of-Check to Time-of-Use (TOCTOU) race condition. Between the os.path.exists() check (line 8) and the file creation (line 9), an attacker can create a symbolic link or malicious file at that path, causing sensitive credentials to be written to an attacker-controlled location. Additionally, chmod is applied after writing data (line 12), creating another exploitable window where credentials are temporarily world-readable.","remediation":"The fix uses os.open() with O_CREAT | O_EXCL flags to atomically check for file existence and create the file in a single system call, eliminating the TOCTOU race window. The file permissions (0o600) are set at creation time rather than after writing, ensuring credentials are never world-readable. O_EXCL also refuses to follow symbolic links, preventing symlink attacks.","secure_code":"import os\nimport tempfile\nimport json\n\ndef store_iot_device_credentials(device_id, api_key, secret_token):\n    temp_dir = tempfile.gettempdir()\n    cred_file = os.path.join(temp_dir, f\"iot_device_{device_id}.json\")\n    try:\n        fd = os.open(cred_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)\n    except FileExistsError:\n        return \"Device already provisioned\"\n    try:\n        with os.fdopen(fd, 'w') as f:\n            credentials = {\"device_id\": device_id, \"api_key\": api_key, \"secret\": secret_token, \"provisioned\": True}\n            json.dump(credentials, f)\n    except Exception:\n        os.close(fd)\n        raise\n    return f\"Credentials stored securely for device {device_id}\""}