Skip to content

sandboxing for shell command execution#128

Merged
priyanshujain merged 11 commits intomasterfrom
sandbox-commands-cli
Dec 15, 2025
Merged

sandboxing for shell command execution#128
priyanshujain merged 11 commits intomasterfrom
sandbox-commands-cli

Conversation

@priyanshujain
Copy link
Copy Markdown
Collaborator

@priyanshujain priyanshujain commented Dec 12, 2025

Summary by CodeRabbit

  • New Features

    • Sandbox mode: run CLI commands in isolated Docker containers with startup checks, lifecycle management, live streaming, and cleanup.
    • Multi-architecture image build/push support for the sandbox.
  • Documentation

    • Added brief CLI guidance recommending a lightweight runner for Python scripts and minimal comments.
  • Chores

    • Bumped CLI dependency to include Docker client >=7.1.0.
  • Bug Fixes / UX

    • Streamlined executor selection, interactive agent flow, history logging, and error handling for simpler, more robust runtime behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces sandboxing functionality for shell command execution by running commands inside Docker containers instead of directly on the host system. This provides isolation and security for executing potentially dangerous commands.

Key Changes:

  • Implements a new Docker-based container execution module with streaming output support
  • Adds an abstract ExecutorInterface to allow switching between host and container execution
  • Introduces sandbox mode (enabled by default) controllable via INFRAGPT_ISOLATED environment variable

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 20 comments.

Show a summary per file
File Description
cli/src/infragpt/container.py New module implementing Docker container-based command execution with ExecutorInterface, ContainerRunner class, and helper functions for Docker availability checks and cleanup
cli/src/infragpt/shell.py Modified CommandExecutor to implement ExecutorInterface and added cleanup method for consistency
cli/src/infragpt/tools.py Added executor factory pattern to switch between host and container executors based on sandbox mode, with module-level singleton management
cli/src/infragpt/main.py Added sandbox initialization at startup with Docker availability checks, container cleanup in finally block, and error handling for Docker issues
cli/src/infragpt/images/sandbox/Dockerfile New Dockerfile defining the sandbox container image with common utilities, Google Cloud SDK, and kubectl
cli/src/infragpt/images/sandbox/Makefile Build automation for creating multi-architecture sandbox images and pushing to GitHub Container Registry
cli/pyproject.toml Added docker>=7.1.0 dependency for container management
cli/CLAUDE.md New file documenting development guidelines (use uv, minimal comments)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 15, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a Docker-backed sandbox executor and image, integrates sandbox-aware executor selection and lifecycle into CLI startup and shutdown, updates executor interfaces, small docs and dependency changes, and simplifies several internal agent/history/LLM adapter flows.

Changes

Cohort / File(s) Summary
Docs & Dependency
cli/CLAUDE.md, cli/pyproject.toml
Two brief lines added to CLAUDE.md; added docker>=7.1.0 to project dependencies.
Sandbox Executor Module
cli/src/infragpt/container.py
New Docker-backed execution module: ExecutorInterface, ContainerRunner, Docker availability checks, container lifecycle (start/execute/stop/cleanup), streaming output with cwd markers, singleton helpers, and DockerNotAvailableError.
Docker Image & Build
cli/src/infragpt/images/sandbox/Dockerfile, cli/src/infragpt/images/sandbox/Makefile
New Ubuntu 24.04 sandbox Dockerfile installing CLI tooling, gcloud, kubectl; Makefile for multi-arch build/push and local image workflows.
CLI Integration & Executors
cli/src/infragpt/main.py, cli/src/infragpt/shell.py, cli/src/infragpt/tools.py
Startup now checks sandbox mode, cleans old containers, and starts container executor; CommandExecutor implements ExecutorInterface and adds cleanup(); tools.py gains sandbox-aware get_executor()/cleanup_executor() and uses shared executor for shell commands.
Agent / History / LLM Adapter
cli/src/infragpt/agent.py, cli/src/infragpt/history.py, cli/src/infragpt/llm_adapter.py
Internal simplifications and comment removals; streamlined history/sanitization and streaming/tool-execution flows with no public API signature changes.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (main)
    participant Tools as tools.get_executor
    participant Container as ContainerRunner
    participant Docker as Docker daemon

    CLI->>Tools: startup -> is_sandbox_mode?
    Tools->>Container: cleanup_old_containers()
    Tools->>Container: get_executor() / start()
    Container->>Docker: pull image / create & start container
    Docker-->>Container: container running

    Note over CLI,Tools: User issues shell command
    Tools->>Container: execute_command(command)
    Container->>Docker: docker exec -> run command
    Docker-->>Container: stream stdout/stderr
    Container-->>Tools: return (exit_code, output, was_cancelled)

    Note over CLI: Shutdown
    CLI->>Tools: cleanup_executor()
    Tools->>Container: stop & remove container
    Container->>Docker: remove container / close client
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay extra attention to:
    • cli/src/infragpt/container.py: Docker SDK usage, streaming loop, timeout/cancellation handling, cwd-marker parsing, singleton lifecycle and resource cleanup.
    • cli/src/infragpt/main.py and cli/src/infragpt/tools.py: startup/shutdown sequencing, error handling for DockerNotAvailableError, and ensuring no executor leaks.
    • cli/src/infragpt/images/sandbox/Makefile and Dockerfile: multi-arch build flags and installed tool versions.

Possibly related PRs

Poem

🐰
I hopped into a sandbox bright,
Containers hummed beneath moonlight,
Commands like carrots, quick and neat,
Workspace warm where code and rabbit meet,
I twitched my nose — the run was sweet.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'sandboxing for shell command execution' directly and accurately summarizes the main changes: introducing Docker-based sandbox execution for shell commands via the ContainerRunner class and related infrastructure.
Docstring Coverage ✅ Passed Docstring coverage is 95.65% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81e23d5 and 42f17e0.

📒 Files selected for processing (1)
  • cli/src/infragpt/main.py (3 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
cli/src/infragpt/images/sandbox/Dockerfile (1)

40-44: Verify kubectl binary integrity with checksum.

The kubectl binary is downloaded without checksum verification, which poses a supply-chain security risk if the download is tampered with.

 # Install kubectl
 ARG TARGETARCH
-RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/${TARGETARCH}/kubectl" \
+RUN KUBECTL_VERSION="$(curl -L -s https://dl.k8s.io/release/stable.txt)" \
+    && curl -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl" \
+    && curl -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl.sha256" \
+    && echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check \
     && install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl \
-    && rm kubectl
+    && rm kubectl kubectl.sha256
cli/src/infragpt/main.py (1)

148-163: Potential resource leak if executor.start() fails.

If get_executor() succeeds but executor.start() raises an exception other than DockerNotAvailableError, the executor object is created but never cleaned up. Consider wrapping the start in a try block that cleans up on failure.

         try:
             removed = cleanup_old_containers()
             if removed > 0:
                 console.print(f"[dim]Cleaned up {removed} old sandbox container(s)[/dim]")
             console.print(
                 "[yellow]Sandbox mode enabled - starting Docker container...[/yellow]"
             )
             executor = get_executor()
-            executor.start()  # This checks Docker availability
+            try:
+                executor.start()  # This checks Docker availability
+            except Exception:
+                executor.cleanup()
+                raise
             console.print("[green]Sandbox container ready.[/green]\n")
         except DockerNotAvailableError as e:
cli/src/infragpt/container.py (2)

183-192: remove=True may cause issues in stop() method.

With remove=True, the container is automatically removed when stopped. In the stop() method (lines 290-299), if container.stop() succeeds but subsequent code tries to interact with the container, or if the container was already auto-removed, operations will fail silently.

This is mostly benign since you set self.container = None after stop, but be aware that container.kill() in the fallback (line 296) may fail if auto-removal already occurred.


244-253: pkill -P 1 may not effectively cancel the running command.

In Docker containers, PID 1 is typically the container's init process (tail -f /dev/null in this case), not the parent of exec'd commands. The pkill -P 1 approach won't reliably kill the exec'd process since exec'd commands have their own PID hierarchy.

A more reliable approach would be to track the exec's PID:

try:
    exec_info = self.client.api.exec_inspect(exec_id)
    pid = exec_info.get("Pid")
    if pid and pid > 0:
        self.client.api.exec_start(
            self.client.api.exec_create(
                container=self.container.id,
                cmd=["/bin/sh", "-c", f"kill -9 {pid} 2>/dev/null"],
            )
        )
except Exception:
    pass  # Best effort cancellation
🧹 Nitpick comments (4)
cli/src/infragpt/images/sandbox/Makefile (1)

4-4: Consider adding a clean target.

A clean target would be helpful for removing local images and resetting the buildx builder during development.

-.PHONY: build build-amd64 build-arm64 push push-amd64 push-arm64 all login setup
+.PHONY: build build-amd64 build-arm64 push push-amd64 push-arm64 all login setup clean

+clean:
+	docker rmi $(LOCAL_IMAGE) 2>/dev/null || true
+	docker rmi $(IMAGE_NAME):latest-amd64 2>/dev/null || true
+	docker rmi $(IMAGE_NAME):latest-arm64 2>/dev/null || true
cli/src/infragpt/images/sandbox/Dockerfile (1)

21-21: Consider whether sudo is necessary in this sandbox container.

Installing sudo increases the attack surface. If the container always runs as root and doesn't need privilege escalation, consider removing it.

cli/src/infragpt/tools.py (1)

40-51: Late import creates potential for double cleanup.

The late import of cleanup_executor as cleanup_container from .container is fine for avoiding circular imports, but note that main.py also imports cleanup_executor directly from container and calls both. This could result in double cleanup when main.py calls both cleanup_executor() and cleanup_tools_executor().

Consider documenting that callers should only call one cleanup function, or make the cleanup idempotent by checking if already cleaned.

cli/src/infragpt/container.py (1)

70-77: Add exception chaining for better traceability.

When re-raising as a custom exception, chain it with from e to preserve the original traceback for debugging.

 def ensure_docker_available() -> None:
     """Ensure Docker daemon is available and running. Raises DockerNotAvailableError if not."""
     try:
         client = docker.from_env()
         client.ping()
         client.close()
     except docker.errors.DockerException as e:
-        raise DockerNotAvailableError(f"Docker error: {e}")
+        raise DockerNotAvailableError(f"Docker error: {e}") from e
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb61701 and eae6dbb.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • cli/CLAUDE.md (1 hunks)
  • cli/pyproject.toml (2 hunks)
  • cli/src/infragpt/container.py (1 hunks)
  • cli/src/infragpt/images/sandbox/Dockerfile (1 hunks)
  • cli/src/infragpt/images/sandbox/Makefile (1 hunks)
  • cli/src/infragpt/main.py (3 hunks)
  • cli/src/infragpt/shell.py (2 hunks)
  • cli/src/infragpt/tools.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
cli/src/infragpt/tools.py (2)
cli/src/infragpt/container.py (6)
  • ExecutorInterface (39-58)
  • is_sandbox_mode (61-67)
  • get_executor (113-118)
  • cleanup_executor (121-126)
  • cleanup (56-58)
  • cleanup (305-307)
cli/src/infragpt/shell.py (2)
  • CommandExecutor (31-259)
  • cleanup (257-259)
cli/src/infragpt/container.py (1)
cli/src/infragpt/tools.py (2)
  • get_executor (26-37)
  • cleanup_executor (40-51)
cli/src/infragpt/shell.py (1)
cli/src/infragpt/container.py (3)
  • ExecutorInterface (39-58)
  • cleanup (56-58)
  • cleanup (305-307)
🪛 checkmake (0.2.2)
cli/src/infragpt/images/sandbox/Makefile

[warning] 4-4: Missing required phony target "clean"

(minphony)


[warning] 4-4: Missing required phony target "test"

(minphony)

🪛 Ruff (0.14.8)
cli/src/infragpt/container.py

77-77: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


77-77: Avoid specifying long messages outside the exception class

(TRY003)


98-99: try-except-pass detected, consider logging the exception

(S110)


98-98: Do not catch blind exception: Exception

(BLE001)


102-103: try-except-pass detected, consider logging the exception

(S110)


102-102: Do not catch blind exception: Exception

(BLE001)


105-105: Consider moving this statement to an else block

(TRY300)


106-106: Do not catch blind exception: Exception

(BLE001)


173-173: Do not catch blind exception: Exception

(BLE001)


174-177: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


174-177: Avoid specifying long messages outside the exception class

(TRY003)


205-205: Avoid specifying long messages outside the exception class

(TRY003)


252-253: try-except-pass detected, consider logging the exception

(S110)


252-252: Do not catch blind exception: Exception

(BLE001)


263-263: Consider moving this statement to an else block

(TRY300)


265-265: Do not catch blind exception: Exception

(BLE001)


285-286: try-except-pass detected, consider logging the exception

(S110)


285-285: Do not catch blind exception: Exception

(BLE001)


294-294: Do not catch blind exception: Exception

(BLE001)


297-298: try-except-pass detected, consider logging the exception

(S110)


297-297: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (17)
cli/CLAUDE.md (1)

1-2: LGTM!

Simple and clear AI guidance instructions.

cli/src/infragpt/shell.py (2)

25-31: LGTM!

The CommandExecutor correctly implements ExecutorInterface, enabling polymorphic executor selection between host and container-based execution. The import and inheritance are properly structured.


257-259: LGTM!

The no-op cleanup() implementation is appropriate for the host executor since there are no container resources to manage.

cli/src/infragpt/main.py (1)

173-179: Good use of try/finally for cleanup.

The cleanup logic ensures sandbox resources are released even if an exception occurs during agent execution.

cli/src/infragpt/images/sandbox/Makefile (1)

1-37: Well-structured multi-architecture build configuration.

The Makefile properly handles buildx setup, multi-arch builds, and push operations. The --provenance=false flag avoids attestation compatibility issues.

cli/src/infragpt/images/sandbox/Dockerfile (1)

1-1: LGTM!

Good choice to pin Ubuntu to a specific version (24.04) for reproducible builds.

cli/src/infragpt/tools.py (2)

22-37: LGTM!

Good implementation of the sandbox-aware executor selection with lazy initialization for the host executor singleton.


185-192: LGTM!

The command execution correctly uses get_executor() to obtain the appropriate executor based on sandbox mode, maintaining consistent behavior for both host and container execution paths.

cli/pyproject.toml (1)

30-30: Docker dependency appropriately added for container-based sandbox execution.

The docker>=7.1.0 dependency correctly enables the Docker SDK functionality used in container.py for sandbox command execution. The code uses standard Docker API calls (docker.from_env(), client.containers.list(), container.stop()) that are fully compatible with version 7.1.0 and later. The version constraint allows for appropriate patch and minor updates.

cli/src/infragpt/container.py (8)

23-30: LGTM!

Architecture detection logic is sound, correctly mapping ARM variants and defaulting to amd64.


39-58: LGTM!

Clean abstract interface definition with appropriate method signatures and documentation.


61-67: LGTM!

Opt-out design for sandbox mode is sensible for security-by-default behavior.


84-110: LGTM with minor note.

Resource cleanup with the finally block is correctly implemented. Silent exception handling during container cleanup is acceptable for best-effort cleanup semantics.


113-126: LGTM!

Singleton pattern is appropriate for CLI context where single-threaded execution is expected.


212-220: LGTM - Shell injection mitigated and timeout implemented.

Good use of shlex.quote() on self.current_cwd to prevent shell injection, and the timeout command prefix correctly applies the configured timeout.


269-286: LGTM!

The marker-based approach for extracting the working directory is a good improvement over re-executing commands. Silent failure handling is acceptable since cwd tracking is a convenience feature that shouldn't break command execution.


288-307: LGTM!

The stop-then-kill fallback pattern is appropriate for reliable container cleanup. The cleanup() alias correctly implements the ExecutorInterface.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
cli/src/infragpt/main.py (1)

145-163: Consider adding cleanup before early exit on Docker failure.

If executor.start() fails partway through (after the container is created but before completion), the container may remain until the next CLI invocation when cleanup_old_containers() runs. While this is mitigated by the startup cleanup and remove=True on the container, adding explicit cleanup before the early exit would be more robust.

         except DockerNotAvailableError as e:
             console.print(f"[red]Error: {e}[/red]")
             console.print(
                 "Please fix the issue above or disable sandbox mode with INFRAGPT_ISOLATED=false"
             )
+            cleanup_executor()
             sys.exit(1)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eae6dbb and 3a65b5a.

📒 Files selected for processing (1)
  • cli/src/infragpt/main.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
cli/src/infragpt/main.py (4)
cli/src/infragpt/container.py (6)
  • is_sandbox_mode (61-67)
  • get_executor (113-118)
  • cleanup_old_containers (84-110)
  • DockerNotAvailableError (33-36)
  • cleanup_executor (121-126)
  • start (161-192)
cli/src/infragpt/tools.py (2)
  • get_executor (26-37)
  • cleanup_executor (40-51)
services/agent/src/grpc/server.py (1)
  • start (26-53)
cli/src/infragpt/agent.py (1)
  • run_shell_agent (324-327)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
cli/src/infragpt/main.py (1)

15-16: Imports are correct and past redundancy concern is addressed.

The imports are now properly structured - cleanup_executor is imported only from infragpt.tools, which correctly delegates to container.cleanup_executor() in sandbox mode per the code in tools.py (Lines 44-48).

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
cli/src/infragpt/container.py (3)

70-77: Consider exception chaining for better debugging.

The function works correctly, but could benefit from explicit exception chaining:

-    except docker.errors.DockerException as e:
-        raise DockerNotAvailableError(f"Docker error: {e}")
+    except docker.errors.DockerException as e:
+        raise DockerNotAvailableError(f"Docker error: {e}") from e

This preserves the full stack trace for debugging.


84-110: Consider logging cleanup failures for debugging.

The function properly uses a finally block to close the Docker client (addressing a past review comment). However, silent exception handling at lines 98-99 and 102-103 could make debugging difficult.

                 try:
                     container.stop(timeout=5)
-                except Exception:
-                    pass
+                except Exception as e:
+                    if console:  # Import console at module level
+                        console.print(f"[dim]Warning: Failed to stop container: {e}[/dim]")
                 try:
                     container.remove(force=True)
-                except Exception:
-                    pass
+                except Exception as e:
+                    if console:
+                        console.print(f"[dim]Warning: Failed to remove container: {e}[/dim]")

This helps diagnose issues during cleanup without breaking the flow.


161-190: Container setup is correct. Consider security implications of rw mount.

The container setup properly handles image pulling and volume mounting. The read-write mount at line 178 is intentional for sandbox functionality but does allow container to modify host files. This is a documented trade-off.

For production use, consider:

  • Adding a flag to restrict mounts to read-only when write access isn't needed
  • Documenting which operations require write access
  • Considering user namespacing to limit damage from container escape
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a65b5a and 81e23d5.

📒 Files selected for processing (6)
  • cli/src/infragpt/agent.py (2 hunks)
  • cli/src/infragpt/container.py (1 hunks)
  • cli/src/infragpt/history.py (2 hunks)
  • cli/src/infragpt/llm_adapter.py (2 hunks)
  • cli/src/infragpt/shell.py (6 hunks)
  • cli/src/infragpt/tools.py (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
cli/src/infragpt/container.py (2)
cli/src/infragpt/shell.py (2)
  • execute_command (47-121)
  • cleanup (223-225)
cli/src/infragpt/tools.py (2)
  • get_executor (25-34)
  • cleanup_executor (37-46)
cli/src/infragpt/tools.py (2)
cli/src/infragpt/container.py (6)
  • ExecutorInterface (39-58)
  • is_sandbox_mode (61-67)
  • get_executor (113-118)
  • cleanup_executor (121-126)
  • cleanup (56-58)
  • cleanup (288-290)
cli/src/infragpt/shell.py (2)
  • CommandExecutor (30-225)
  • cleanup (223-225)
🪛 GitHub Check: CodeQL
cli/src/infragpt/history.py

[failure] 99-99: Clear-text storage of sensitive information
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.
This expression stores sensitive data (password) as clear text.

🪛 Ruff (0.14.8)
cli/src/infragpt/container.py

77-77: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


77-77: Avoid specifying long messages outside the exception class

(TRY003)


98-99: try-except-pass detected, consider logging the exception

(S110)


98-98: Do not catch blind exception: Exception

(BLE001)


102-103: try-except-pass detected, consider logging the exception

(S110)


102-102: Do not catch blind exception: Exception

(BLE001)


105-105: Consider moving this statement to an else block

(TRY300)


106-106: Do not catch blind exception: Exception

(BLE001)


172-172: Do not catch blind exception: Exception

(BLE001)


173-176: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


173-176: Avoid specifying long messages outside the exception class

(TRY003)


203-203: Avoid specifying long messages outside the exception class

(TRY003)


244-245: try-except-pass detected, consider logging the exception

(S110)


244-244: Do not catch blind exception: Exception

(BLE001)


253-253: Consider moving this statement to an else block

(TRY300)


255-255: Do not catch blind exception: Exception

(BLE001)


268-269: try-except-pass detected, consider logging the exception

(S110)


268-268: Do not catch blind exception: Exception

(BLE001)


277-277: Do not catch blind exception: Exception

(BLE001)


280-281: try-except-pass detected, consider logging the exception

(S110)


280-280: Do not catch blind exception: Exception

(BLE001)

cli/src/infragpt/tools.py

157-157: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (12)
cli/src/infragpt/shell.py (1)

25-25: LGTM! Clean ExecutorInterface implementation.

The CommandExecutor now properly implements ExecutorInterface with the required execute_command and cleanup methods. The no-op cleanup() is appropriate for the host executor since it doesn't manage external resources like containers.

Also applies to: 30-30, 223-225

cli/src/infragpt/tools.py (2)

22-34: LGTM! Sandbox-aware executor selection is clean.

The module-level singleton pattern for _host_executor and the get_executor() function properly route execution based on sandbox mode. The lazy initialization is appropriate for CLI usage.


37-46: LGTM! Cleanup logic is symmetric with executor selection.

The cleanup_executor() properly delegates to the appropriate executor and handles module-level state correctly. The dynamic import at line 42 is a reasonable approach to avoid circular dependencies.

cli/src/infragpt/history.py (1)

73-105: Sanitization logic is sound, but be aware of user input risks.

The CodeQL warning at line 99 is partially a false positive. The code properly sanitizes data and uses an allowlist approach. However, there's an inherent limitation: if users type sensitive information directly in their prompts (e.g., "set my password to secret123"), it will be logged since user_input and assistant_response are in the allowlist.

This is a known trade-off for conversation logging. Consider documenting this behavior in user-facing documentation.

cli/src/infragpt/llm_adapter.py (1)

90-90: LGTM! Minor formatting adjustments.

The changes are cosmetic and maintain correct behavior. Setting content: None at line 171 is the proper OpenAI format for tool call messages.

Also applies to: 171-171

cli/src/infragpt/agent.py (1)

113-113: Good security awareness in the comment.

The warning about FileHistory writing clear-text inputs to disk is important and well-documented. This aligns with the security considerations in history.py.

cli/src/infragpt/container.py (6)

23-30: LGTM! Platform detection is appropriate.

The platform detection correctly handles ARM64 variants and defaults to AMD64. This covers the most common architectures for this use case.


132-159: LGTM! Configuration setup is clean.

The __init__ method properly initializes all configuration. Note that timeout (line 154) is indeed used in execute_command at line 212 via the timeout command prefix, so the past review concern about unused timeout is no longer valid.


210-216: Command construction properly handles shell injection.

The use of shlex.quote() for self.current_cwd at line 214 correctly prevents shell injection for the directory path. The user command is intentionally not quoted to allow shell interpretation.


259-269: LGTM! Working directory tracking is efficient and safe.

The implementation properly extracts the working directory from command output using a marker approach. This is much better than re-executing commands (as noted in past reviews) and correctly validates the path before updating.


271-290: LGTM! Container cleanup is defensive and thorough.

The stop() method properly handles cleanup with fallback to kill() if graceful stop fails. The defensive exception handling is appropriate for cleanup code where we want to ensure resources are released even if some steps fail.

The container is created with remove=True (line 189), so it auto-removes when stopped, but the exception handling ensures this method is safe to call even if the container is already gone.


80-81: LGTM! Singleton pattern is correctly implemented with proper cleanup.

The module-level singleton pattern for _executor is appropriately used for single-threaded CLI execution. The implementation is safe: cleanup_executor() is called in a finally block in the main execution flow (main.py:174), ensuring resources are always cleaned up when the application exits. This approach is consistent with the pattern used in tools.py for _host_executor.

Comment on lines +234 to +245
except KeyboardInterrupt:
self.cancelled = True
console.print("\n[yellow]Command cancelled by user[/yellow]")
try:
self.client.api.exec_start(
self.client.api.exec_create(
container=self.container.id,
cmd=["/bin/sh", "-c", "pkill -P 1"],
)
)
except Exception:
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Cancellation logic may not reliably kill the target process.

The pkill -P 1 command at line 241 attempts to kill all child processes of PID 1 (init), which may not effectively target the specific command being executed. This was noted in a past review and remains unaddressed.

The current approach has limitations:

  • May kill unrelated processes in the container
  • May fail to kill the target if it's not a direct child of PID 1
  • No guarantee the exec'd command tree is terminated

Consider tracking the exec PID and using a more targeted kill:

except KeyboardInterrupt:
    self.cancelled = True
    console.print("\n[yellow]Command cancelled by user[/yellow]")
    try:
        # Get the PID of the exec process
        exec_info = self.client.api.exec_inspect(exec_id)
        pid = exec_info.get("Pid")
        if pid and pid > 0:
            # Kill the specific process and its children
            kill_cmd = f"kill -TERM -{pid} 2>/dev/null || kill -9 -{pid}"
            self.client.api.exec_start(
                self.client.api.exec_create(
                    container=self.container.id,
                    cmd=["/bin/sh", "-c", kill_cmd],
                )
            )
    except Exception:
        pass  # Ignore errors during cancellation

Note: The negative PID (-pid) sends the signal to the entire process group.

🧰 Tools
🪛 Ruff (0.14.8)

244-245: try-except-pass detected, consider logging the exception

(S110)


244-244: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In cli/src/infragpt/container.py around lines 234 to 245, the cancellation block
uses `pkill -P 1` which can kill unrelated processes and may not target the
exec'd command; instead, when handling KeyboardInterrupt set self.cancelled and
print the cancellation message, capture the exec id when starting the exec, call
the API to inspect that exec to retrieve its PID, and if a valid PID is returned
use a targeted kill of the exec process group (send TERM to -PID, fallback to
KILL) by exec'ing a shell command in the container; wrap the inspect/kill in a
try/except and ignore errors during cancellation so cancellation remains
best-effort without affecting other container processes.

@priyanshujain priyanshujain merged commit b71105b into master Dec 15, 2025
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants