{"title":"Insecure Temporary File Creation via Predictable /tmp Filename","language":"Python","severity":"High","cwe":"CWE-377","source_lines":[4],"flow_lines":[4,5,6,4,12,13],"sink_lines":[5,6,13],"vulnerable_code":"import os\nimport json\n\ndef cache_ml_inference_results(model_id, prediction_data):\n    cache_file = f\"/tmp/ml_inference_{model_id}.json\"\n    with open(cache_file, 'w') as f:\n        json.dump(prediction_data, f)\n    os.chmod(cache_file, 0o666)\n    return cache_file\n\ndef retrieve_cached_prediction(model_id):\n    cache_file = f\"/tmp/ml_inference_{model_id}.json\"\n    if os.path.exists(cache_file):\n        with open(cache_file, 'r') as f:\n            return json.load(f)\n    return None","explanation":"The code creates temporary files with predictable filenames based on user-controlled model_id in /tmp directory with world-writable permissions (0o666). An attacker can predict the filename, create symlinks to sensitive files, or replace cached data with malicious content, leading to unauthorized access or data injection attacks.","remediation":"The fix uses a secure application-specific temporary directory created with tempfile.mkdtemp() with restrictive permissions (0o700), sanitizes model_id using SHA-256 hashing to prevent path traversal, uses os.open() with O_NOFOLLOW flag to prevent symlink attacks, sets restrictive file permissions (0o600), and verifies file ownership before reading cached data.","secure_code":"import os\nimport json\nimport tempfile\nimport hashlib\nimport stat\n\n# Use a secure, application-specific cache directory\nCACHE_DIR = None\n\ndef _get_cache_dir():\n    global CACHE_DIR\n    if CACHE_DIR is None:\n        CACHE_DIR = tempfile.mkdtemp(prefix='ml_inference_cache_')\n        os.chmod(CACHE_DIR, stat.S_IRWXU)  # 0o700 - owner only\n    return CACHE_DIR\n\ndef _sanitize_model_id(model_id):\n    \"\"\"Sanitize model_id to prevent path traversal attacks.\"\"\"\n    # Hash the model_id to create a safe filename\n    safe_id = hashlib.sha256(str(model_id).encode()).hexdigest()\n    return safe_id\n\ndef cache_ml_inference_results(model_id, prediction_data):\n    cache_dir = _get_cache_dir()\n    safe_id = _sanitize_model_id(model_id)\n    cache_file = os.path.join(cache_dir, f\"{safe_id}.json\")\n    \n    # Use os.open with exclusive flags to prevent race conditions\n    # O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW to avoid symlink attacks\n    fd = os.open(cache_file, os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_NOFOLLOW, 0o600)\n    try:\n        with os.fdopen(fd, 'w') as f:\n            json.dump(prediction_data, f)\n    except Exception:\n        os.close(fd)\n        raise\n    return cache_file\n\ndef retrieve_cached_prediction(model_id):\n    cache_dir = _get_cache_dir()\n    safe_id = _sanitize_model_id(model_id)\n    cache_file = os.path.join(cache_dir, f\"{safe_id}.json\")\n    \n    # Verify the file is a regular file and not a symlink\n    if os.path.exists(cache_file):\n        if os.path.islink(cache_file):\n            # Refuse to read symlinks\n            os.unlink(cache_file)\n            return None\n        # Verify ownership\n        file_stat = os.stat(cache_file)\n        if file_stat.st_uid != os.getuid():\n            return None\n        with open(cache_file, 'r') as f:\n            return json.load(f)\n    return None"}