Skip to content

feat: add support for setup script execution before scans#611

Open
TheArtificialQ wants to merge 1 commit into
usestrix:mainfrom
TheArtificialQ:feature/setup-script
Open

feat: add support for setup script execution before scans#611
TheArtificialQ wants to merge 1 commit into
usestrix:mainfrom
TheArtificialQ:feature/setup-script

Conversation

@TheArtificialQ

Copy link
Copy Markdown

Adding --setup-script CLI parameter.

It's a path to a bash script to execute inside the Docker container as the first step before the scan begins. Use this to install dependencies, seed databases, establish VPN connections, or perform other environment preparation. The script is bind-mounted read-only into the sandbox and run with bash.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a --setup-script CLI parameter that bind-mounts a host bash script into the Docker sandbox and runs it before the scan begins, enabling dependency installation, database seeding, VPN setup, and similar environment preparation. The feature is wired through the argument parser, persisted in run records for resume support, and surfaced in the TUI with its own renderer and event flow.

  • session_manager.py: New build_setup_script_mount and execute_setup_script functions handle mounting and running the script; a container leak exists when the script fails (see inline comment).
  • main.py: _resolve_setup_script_path validates the path at parse time; the resume path skips this validation.
  • TUI (app.py, live_view.py, shell_renderer.py, tui_styles.tcss): Clean integration using the existing agent/tool-call event model with a dedicated SetupScriptRenderer.

Confidence Score: 3/5

The feature works correctly for the happy path, but a failed setup script leaves the Docker container running with no automatic cleanup.

When the setup script exits non-zero, execute_setup_script raises before the session bundle is stored in _SESSION_CACHE. Every cleanup path downstream calls session_manager.cleanup(scan_id), which pops from that cache — finding nothing — and skips client.delete. The Docker container created for the scan is silently orphaned. Users who frequently test with failing setup scripts would accumulate stranded containers.

strix/runtime/session_manager.py — the setup-script execution block in create_or_reuse needs a try/except that tears down the container on failure.

Important Files Changed

Filename Overview
strix/runtime/session_manager.py Core of the feature — adds setup script mounting, execution, and event emission. Container leak when setup script fails (bundle not cached before the execute call), plus an unused contextlib import.
strix/interface/main.py Adds --setup-script argument and _resolve_setup_script_path validation. Resume path loads the persisted script path without re-validating it, so a missing file produces a bare exception instead of a friendly error.
strix/core/runner.py Passes setup_script and setup_script_event_sink through to create_or_reuse. The create_or_reuse call sits outside the inner try/finally that performs sandbox cleanup, which contributes to the container-leak issue.
strix/interface/tui/app.py Wires up setup script event handling in the TUI: status display, event recording, and _capture_setup_script_event dispatch. Logic is clean and consistent with existing patterns.
strix/interface/tui/live_view.py Adds record_setup_script_event that synthesises a virtual agent node and tool-call events for the setup script, following the same pattern as existing agent tracking.
strix/interface/tui/renderers/shell_renderer.py Adds SetupScriptRenderer and _parse_setup_script_result, rendering stdout/stderr in the terminal-tool style. Consistent with existing renderers.
tests/test_setup_script.py Good coverage of mount resolution, exec invocation, event sequencing, failure path, and the create_or_reuse ordering. No issues found.
strix/interface/cli.py Adds setup_script to the scan_config dict for CLI (non-TUI) mode. No setup_script_event_sink is passed, which is intentional — CLI mode has no live event panel.

Comments Outside Diff (1)

  1. strix/runtime/session_manager.py, line 240-252 (link)

    P1 Container leaked when setup script fails

    When execute_setup_script raises RuntimeError (non-zero exit code), the exception escapes create_or_reuse before _SESSION_CACHE[scan_id] = bundle is reached. The cleanup path in both runner.py and cli.py calls session_manager.cleanup(scan_id), which does _SESSION_CACHE.pop(scan_id, None) — finding nothing, it returns without calling client.delete(session). The Docker container that was created earlier (and successfully bootstrapped Caido) is never destroyed.

    The simplest fix is to wrap the setup-script execution in a try/except inside create_or_reuse and call client.delete(session) on failure, or to cache the bundle before running the script and rely on the existing cleanup path.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: strix/runtime/session_manager.py
    Line: 240-252
    
    Comment:
    **Container leaked when setup script fails**
    
    When `execute_setup_script` raises `RuntimeError` (non-zero exit code), the exception escapes `create_or_reuse` before `_SESSION_CACHE[scan_id] = bundle` is reached. The cleanup path in both `runner.py` and `cli.py` calls `session_manager.cleanup(scan_id)`, which does `_SESSION_CACHE.pop(scan_id, None)` — finding nothing, it returns without calling `client.delete(session)`. The Docker container that was created earlier (and successfully bootstrapped Caido) is never destroyed.
    
    The simplest fix is to wrap the setup-script execution in a try/except inside `create_or_reuse` and call `client.delete(session)` on failure, or to cache the bundle before running the script and rely on the existing cleanup path.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
strix/runtime/session_manager.py:240-252
**Container leaked when setup script fails**

When `execute_setup_script` raises `RuntimeError` (non-zero exit code), the exception escapes `create_or_reuse` before `_SESSION_CACHE[scan_id] = bundle` is reached. The cleanup path in both `runner.py` and `cli.py` calls `session_manager.cleanup(scan_id)`, which does `_SESSION_CACHE.pop(scan_id, None)` — finding nothing, it returns without calling `client.delete(session)`. The Docker container that was created earlier (and successfully bootstrapped Caido) is never destroyed.

The simplest fix is to wrap the setup-script execution in a try/except inside `create_or_reuse` and call `client.delete(session)` on failure, or to cache the bundle before running the script and rely on the existing cleanup path.

### Issue 2 of 3
strix/runtime/session_manager.py:5-8
`contextlib` is imported but never referenced anywhere in this file — it looks like a leftover from development. This will produce an `F401` lint warning.

```suggestion
import logging
import time
from collections.abc import Callable
```

### Issue 3 of 3
strix/interface/main.py:651-652
**Resumed setup-script path not re-validated**

When a scan is resumed and the setup script path is loaded from persisted state, the value bypasses `_resolve_setup_script_path`. If the file was moved or deleted between runs, `build_setup_script_mount` raises a bare `FileNotFoundError` rather than a user-friendly `parser.error()` message. Consider calling `_resolve_setup_script_path` here (or at least catching `FileNotFoundError` at the call site and converting it to a `parser.error`).

Reviews (1): Last reviewed commit: "feat: add support for setup script execu..." | Re-trigger Greptile

Comment on lines +5 to +8
import contextlib
import logging
import time
from collections.abc import Callable

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 contextlib is imported but never referenced anywhere in this file — it looks like a leftover from development. This will produce an F401 lint warning.

Suggested change
import contextlib
import logging
import time
from collections.abc import Callable
import logging
import time
from collections.abc import Callable
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/runtime/session_manager.py
Line: 5-8

Comment:
`contextlib` is imported but never referenced anywhere in this file — it looks like a leftover from development. This will produce an `F401` lint warning.

```suggestion
import logging
import time
from collections.abc import Callable
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread strix/interface/main.py
Comment on lines +651 to +652
if args.setup_script is None and state.get("setup_script"):
args.setup_script = state.get("setup_script")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Resumed setup-script path not re-validated

When a scan is resumed and the setup script path is loaded from persisted state, the value bypasses _resolve_setup_script_path. If the file was moved or deleted between runs, build_setup_script_mount raises a bare FileNotFoundError rather than a user-friendly parser.error() message. Consider calling _resolve_setup_script_path here (or at least catching FileNotFoundError at the call site and converting it to a parser.error).

Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/interface/main.py
Line: 651-652

Comment:
**Resumed setup-script path not re-validated**

When a scan is resumed and the setup script path is loaded from persisted state, the value bypasses `_resolve_setup_script_path`. If the file was moved or deleted between runs, `build_setup_script_mount` raises a bare `FileNotFoundError` rather than a user-friendly `parser.error()` message. Consider calling `_resolve_setup_script_path` here (or at least catching `FileNotFoundError` at the call site and converting it to a `parser.error`).

How can I resolve this? If you propose a fix, please make it concise.

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.

1 participant