Skip to content

fix: sanitize PyInstaller loader env for subprocesses#115

Merged
F16shen merged 2 commits intoAI-Shell-Team:mainfrom
F16shen:split/pyinstaller-subprocess-env
Apr 21, 2026
Merged

fix: sanitize PyInstaller loader env for subprocesses#115
F16shen merged 2 commits intoAI-Shell-Team:mainfrom
F16shen:split/pyinstaller-subprocess-env

Conversation

@F16shen
Copy link
Copy Markdown
Collaborator

@F16shen F16shen commented Apr 20, 2026

Summary

This PR sanitizes subprocess environments when AISH is running from a PyInstaller bundle.

The change removes bundle-private loader paths before launching child processes, while preserving the user's normal shell environment as much as possible. When PyInstaller provides the original loader path state, the subprocess environment restores that directly; otherwise it falls back to pruning only the extracted bundle path.

Why

System commands launched from the packaged app should not inherit AISH's private runtime loader configuration.

Without this cleanup, subprocesses can observe a modified library search path that differs from the user's real environment. That makes shell execution under the frozen build less predictable and can cause linkage behavior that only exists inside the packaged app.

Testing

  • Ran /home/lixin/workspace/aishell/aish/.venv/bin/python -m pytest tests/shell/ui/test_shell_editor.py tests/tools/test_bash_executor.py -q
  • Result: 28 passed

Summary by Sourcery

Sanitize subprocess environments to strip PyInstaller-specific loader paths while preserving user environment variables across shell, PTY, and script executors.

Bug Fixes:

  • Prevent child processes from inheriting PyInstaller bundle-private LD_LIBRARY_PATH settings that can alter system command behavior.

Enhancements:

  • Introduce a shared helper for building sanitized subprocess environments and integrate it into environment management, bash execution, PTY execution, script runtime, and shell theme scripts.

Tests:

  • Add tests to verify that shell executors and prompt theme environments restore the original loader environment and retain user-defined variables under PyInstaller.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 20, 2026

Reviewer's Guide

Sanitizes subprocess environments under PyInstaller by centralizing LD_LIBRARY_PATH cleanup and ensuring all shell/script/PTY execution paths use the same sanitized environment, plus tests to validate the behavior.

Sequence diagram for sanitized environment in BashExecutor.execute

sequenceDiagram
    actor User
    participant BashExecutor
    participant EnvironmentManager
    participant sanitize_subprocess_loader_env
    participant Subprocess

    User->>BashExecutor: execute(command)
    alt env_manager is present
        BashExecutor->>EnvironmentManager: get_subprocess_env()
        EnvironmentManager->>sanitize_subprocess_loader_env: sanitize_subprocess_loader_env(exported_vars)
        sanitize_subprocess_loader_env-->>EnvironmentManager: sanitized_env
        EnvironmentManager-->>BashExecutor: sanitized_env
    else env_manager is None
        BashExecutor->>sanitize_subprocess_loader_env: sanitize_subprocess_loader_env(os_environ_copy)
        sanitize_subprocess_loader_env-->>BashExecutor: sanitized_env
    end
    BashExecutor->>Subprocess: subprocess_run(command, env=sanitized_env)
Loading

Class diagram for environment management and subprocess sanitization

classDiagram

    class EnvironmentManager {
        + get_exported_vars() Dict~str,str~
        + get_subprocess_env() Dict~str,str~
    }

    class sanitize_subprocess_loader_env {
        + sanitize_subprocess_loader_env(env: Mapping~str,str~) Dict~str,str~
    }

    class BashExecutor {
        - env_manager: EnvironmentManager
        + execute(command: str) CommandResult
    }

    class PtyExecutor {
        - env_manager: EnvironmentManager
        + execute_command_with_pty(command: str) CommandResult
    }

    class ScriptExecutor {
        + _build_runtime_env(script: Script, session: LLMSession) Dict~str,str~
    }

    class ShellEditor {
        + _build_theme_env(cwd: str, exit_code: int, mode: str) Dict~str,str~
    }

    EnvironmentManager ..> sanitize_subprocess_loader_env: calls
    BashExecutor ..> EnvironmentManager: uses
    BashExecutor ..> sanitize_subprocess_loader_env: calls when no env_manager
    PtyExecutor ..> EnvironmentManager: uses
    PtyExecutor ..> sanitize_subprocess_loader_env: calls when no env_manager
    ScriptExecutor ..> sanitize_subprocess_loader_env: calls
    ShellEditor ..> sanitize_subprocess_loader_env: calls
Loading

Flow diagram for sanitize_subprocess_loader_env logic

flowchart TD
    A[Start sanitize_subprocess_loader_env] --> B[Copy source env to sanitized]
    B --> C{Running in PyInstaller bundle<br/>getattr sys.frozen and sys._MEIPASS}
    C -- No --> Z[Return sanitized]
    C -- Yes --> D{LD_LIBRARY_PATH_ORIG set}
    D -- Yes --> E{LD_LIBRARY_PATH_ORIG nonempty}
    E -- Yes --> F[Set LD_LIBRARY_PATH to LD_LIBRARY_PATH_ORIG]
    E -- No --> G[Remove LD_LIBRARY_PATH]
    F --> Z
    G --> Z
    D -- No --> H{LD_LIBRARY_PATH set}
    H -- No --> Z
    H -- Yes --> I[Resolve real _MEIPASS path]
    I --> J[Filter LD_LIBRARY_PATH entries<br/>not under _MEIPASS]
    J --> K{Filtered list nonempty}
    K -- Yes --> L[Set LD_LIBRARY_PATH to<br/>filtered path list]
    K -- No --> M[Remove LD_LIBRARY_PATH]
    L --> Z
    M --> Z
Loading

File-Level Changes

Change Details Files
Introduce centralized helper for sanitizing subprocess environments under PyInstaller and wire it into environment manager APIs.
  • Add sanitize_subprocess_loader_env to strip PyInstaller bundle paths from LD_LIBRARY_PATH while preserving user environment, using LD_LIBRARY_PATH_ORIG when available or pruning _MEIPASS paths otherwise
  • Update imports to include sys and Mapping typing support
  • Expose EnvironmentManager.get_subprocess_env to return exported variables with loader paths sanitized for subprocess use
src/aish/shell/environment.py
Ensure bash executor and PTY executor always run commands with sanitized subprocess environments instead of raw exported vars or os.environ.
  • Change BashExecutor.execute to build env from EnvironmentManager.get_subprocess_env when available, otherwise from sanitize_subprocess_loader_env(os.environ.copy())
  • Update PTY executor to construct env_vars using get_subprocess_env or sanitize_subprocess_loader_env(dict(os.environ)) and pass that env into subprocess.Popen
  • Add comments documenting why sanitized environments are required for both non-PTY and PTY paths
src/aish/tools/bash_executor.py
src/aish/shell/pty/executor.py
Use sanitized environments when running scripts and prompt themes so their subprocesses don’t inherit PyInstaller loader state.
  • Wrap script runtime environment construction in sanitize_subprocess_loader_env instead of raw os.environ
  • Wrap prompt theme environment construction in sanitize_subprocess_loader_env and import helper into editor module
src/aish/scripts/executor.py
src/aish/shell/ui/editor.py
Add tests that validate LD_LIBRARY_PATH restoration/sanitization for bash executor and shell prompt theme under simulated PyInstaller conditions.
  • Add bash executor test that simulates a frozen PyInstaller environment, sets LD_LIBRARY_PATH and LD_LIBRARY_PATH_ORIG via the EnvironmentManager, and asserts subprocess env restores LD_LIBRARY_PATH_ORIG while preserving other user vars
  • Add shell prompt controller test that simulates PyInstaller, sets loader-related environment variables, and asserts _build_theme_env restores LD_LIBRARY_PATH_ORIG and preserves other user variables
tests/tools/test_bash_executor.py
tests/shell/ui/test_shell_editor.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions
Copy link
Copy Markdown
Contributor

Thanks for the pull request. A maintainer will review it when available.

Please keep the PR focused, explain the why in the description, and make sure local checks pass before requesting review.

Contribution guide: https://github.com/AI-Shell-Team/aish/blob/main/CONTRIBUTING.md

@github-actions
Copy link
Copy Markdown
Contributor

This pull request description looks incomplete. Please update the missing sections below before review.

Missing items:

  • User-visible Changes
  • Compatibility
  • Change Type
  • Scope

sourcery-ai[bot]

This comment was marked as resolved.

@github-actions github-actions Bot added size: M and removed size: S labels Apr 21, 2026
@F16shen F16shen merged commit a2b2eed into AI-Shell-Team:main Apr 21, 2026
9 of 10 checks passed
@F16shen F16shen deleted the split/pyinstaller-subprocess-env branch April 22, 2026 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant