Skip to content

pdd sync silently runs pip install for missing packages without user consent #494

@Serhan-Asad

Description

@Serhan-Asad

Summary

During pdd sync, when a PDD-generated example file crashes with a ModuleNotFoundError, the function _try_auto_fix_import_error() automatically runs pip install <package_name> for any package name extracted from the error message — with no user confirmation, no allowlist, and no logging of what was installed.

Reproduction

  1. Create a prompt that causes the LLM to generate an example importing a package not in your environment (this happens naturally ~5-15% of the time when LLMs hallucinate imports)
  2. Run pdd sync <basename>
  3. During the crash operation, if the example has a ModuleNotFoundError, PDD silently runs pip install for the missing package name

Affected Code

The vulnerable pattern exists in two files:

sync_orchestration.py lines 519-532:

# It's an external package - try pip install
try:
    result = subprocess.run(
        [sys.executable, '-m', 'pip', 'install', top_level_package],
        capture_output=True,
        text=True,
        timeout=120
    )

pin_example_hack.py lines 561-574: (identical copy)

Impact

  • Environment contamination: pdd sync installs packages the user never asked for. These don't appear in requirements.txt or pyproject.toml, making environments non-reproducible.
  • User surprise: No developer expects a code generation tool to modify their Python environment. Violates the principle of least astonishment.
  • CI/CD pollution: Teams running pdd sync --force in CI get phantom dependencies that aren't tracked in lockfiles. Builds work today, break tomorrow when recreated from scratch.
  • Security (low probability): Since the package name comes from LLM-generated code, hallucinated package names could theoretically match typosquat packages on PyPI, though this is unlikely in practice.

Suggested Fix

At minimum, add a click.confirm() prompt before running pip install:

if click.confirm(f"Install missing package '{ top_level_package}'?", default=False):
    subprocess.run([sys.executable, '-m', 'pip', 'install', top_level_package], ...)

Better options:

  • Add an --allow-install CLI flag (off by default)
  • Maintain an allowlist of common safe packages for --force mode
  • Log what was installed so users can review and uninstall

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions