{"title":"Dependency Confusion via Unpinned pip Package Installation","language":"Python","severity":"Critical","cwe":"CWE-829","source_lines":[3],"flow_lines":[3,4,5,6,7,8,10],"sink_lines":[8,10],"vulnerable_code":"import subprocess\nimport os\n\ndef bootstrap_ml_inference_environment(model_type):\n    required_libs = ['tensorflow', 'keras-preprocessing', 'scikit-learn', 'numpy']\n    if model_type == 'vision':\n        required_libs.extend(['opencv-python', 'pillow-simd'])\n    for package in required_libs:\n        subprocess.run(['pip', 'install', package, '--upgrade'], check=False)\n    subprocess.run(['pip', 'install', 'company-ml-utils'], check=False)\n    os.environ['MODEL_READY'] = '1'\n    return True","explanation":"The function installs ML packages without version pinning or integrity verification via subprocess.run(['pip', 'install', package, '--upgrade']). This enables dependency confusion attacks where a malicious package with the same name as an internal package (e.g., 'company-ml-utils') can be published to a public repository with a higher version number and automatically installed instead of the intended private package. Additionally, the absence of version constraints on all dependencies means any compromised or malicious version available on PyPI could be installed at runtime.","remediation":"The fix pins all dependencies to exact versions preventing arbitrary version upgrades, validates model_type against an allowlist to prevent misuse, uses a private index URL for internal packages to prevent dependency confusion, adds --no-deps to prevent transitive dependency attacks, and sets check=True with shell=False for safer subprocess execution.","secure_code":"import subprocess\nimport os\n\nPINNED_PACKAGES = {\n    'tensorflow': 'tensorflow==2.15.0',\n    'keras-preprocessing': 'keras-preprocessing==1.1.2',\n    'scikit-learn': 'scikit-learn==1.3.2',\n    'numpy': 'numpy==1.26.2',\n    'opencv-python': 'opencv-python==4.8.1.78',\n    'pillow-simd': 'pillow-simd==9.5.0.post1'\n}\n\nPRIVATE_INDEX_URL = os.environ.get('PRIVATE_PYPI_URL', 'https://private.pypi.company.com/simple/')\n\nALLOWED_MODEL_TYPES = {'vision', 'nlp', 'tabular', 'default'}\n\ndef bootstrap_ml_inference_environment(model_type):\n    if model_type not in ALLOWED_MODEL_TYPES:\n        raise ValueError(f\"Invalid model_type: {model_type!r}. Must be one of {ALLOWED_MODEL_TYPES}\")\n\n    required_libs = ['tensorflow', 'keras-preprocessing', 'scikit-learn', 'numpy']\n    if model_type == 'vision':\n        required_libs.extend(['opencv-python', 'pillow-simd'])\n\n    for package in required_libs:\n        pinned = PINNED_PACKAGES.get(package)\n        if pinned is None:\n            raise ValueError(f\"Unknown package requested: {package!r}\")\n        subprocess.run(\n            ['pip', 'install', '--no-deps', pinned],\n            check=True,\n            shell=False\n        )\n\n    subprocess.run(\n        ['pip', 'install', '--no-deps', '--index-url', PRIVATE_INDEX_URL, 'company-ml-utils==1.0.0'],\n        check=True,\n        shell=False\n    )\n\n    os.environ['MODEL_READY'] = '1'\n    return True"}