{"title":"TOCTOU Race Condition via os.path.exists() Before File Creation","language":"Python","severity":"High","cwe":"CWE-367","source_lines":[5],"flow_lines":[5,6,7],"sink_lines":[7],"vulnerable_code":"import os\nimport json\n\ndef provision_iot_device_cert(device_id, cert_data):\n    cert_path = f\"/var/iot/certs/{device_id}.pem\"\n    if not os.path.exists(cert_path):\n        time.sleep(0.1)\n        with open(cert_path, 'w') as cert_file:\n            cert_file.write(cert_data)\n        os.chmod(cert_path, 0o600)\n        return {\"status\": \"provisioned\", \"device\": device_id}\n    return {\"status\": \"already_exists\", \"device\": device_id}","explanation":"This is a TOCTOU (Time-of-check to time-of-use) race condition vulnerability. Between checking if the file exists (line 5) and creating it (line 7), an attacker can create a malicious symlink at cert_path pointing to a sensitive file, causing the application to overwrite that target file with certificate data. The deliberate time.sleep(0.1) widens the attack window.","remediation":"The fix replaces the vulnerable check-then-act pattern (os.path.exists followed by open) with an atomic os.open() call using O_CREAT | O_EXCL | O_NOFOLLOW flags. O_EXCL ensures the operation fails atomically if the file already exists, eliminating the race window. O_NOFOLLOW prevents symlink following, and permissions are set atomically at creation time rather than after the fact with chmod.","secure_code":"import os\nimport json\nimport tempfile\nimport stat\n\ndef provision_iot_device_cert(device_id, cert_data):\n    cert_dir = \"/var/iot/certs\"\n    cert_path = f\"{cert_dir}/{device_id}.pem\"\n    \n    # Validate device_id to prevent path traversal\n    if not device_id.isalnum() and not all(c.isalnum() or c in ('-', '_') for c in device_id):\n        return {\"status\": \"invalid_device_id\", \"device\": device_id}\n    \n    # Ensure the resolved path is within the expected directory\n    real_cert_dir = os.path.realpath(cert_dir)\n    \n    try:\n        # Use os.open with O_CREAT | O_EXCL to atomically create the file\n        # O_EXCL ensures the call fails if the file already exists (no race window)\n        # O_NOFOLLOW prevents following symlinks\n        fd = os.open(cert_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)\n        \n        # Verify the created file is within the expected directory (defense in depth)\n        real_path = os.path.realpath(f\"/proc/self/fd/{fd}\")\n        if not real_path.startswith(real_cert_dir + os.sep):\n            os.close(fd)\n            os.unlink(cert_path)\n            return {\"status\": \"error_path_traversal\", \"device\": device_id}\n        \n        # Write cert data using the file descriptor\n        with os.fdopen(fd, 'w') as cert_file:\n            cert_file.write(cert_data)\n        \n        return {\"status\": \"provisioned\", \"device\": device_id}\n    except FileExistsError:\n        return {\"status\": \"already_exists\", \"device\": device_id}\n    except OSError as e:\n        return {\"status\": \"error\", \"device\": device_id, \"message\": str(e)}"}