Skip to content

Development#19

Open
khulnasoft-bot wants to merge 9 commits into
FortiShield:developmentfrom
khulnasoft-bot:development
Open

Development#19
khulnasoft-bot wants to merge 9 commits into
FortiShield:developmentfrom
khulnasoft-bot:development

Conversation

@khulnasoft-bot
Copy link
Copy Markdown

@khulnasoft-bot khulnasoft-bot commented Apr 1, 2026

Summary by Sourcery

Improve development-time observability and Docker workflows while hardening plugin, file browsing, and upload handling.

New Features:

  • Add dedicated base Docker image build and multi-arch push targets, plus a Docker-based test runner target.
  • Enable development mode logging and WebSocket diagnostics via runtime flags and supervisor environment configuration.

Bug Fixes:

  • Prevent errors when plugin root directories do not exist in plugin discovery and enhanced plugin listing.
  • Fix path validation and upload directory handling in file browser and message attachment upload logic to avoid crashes and insecure paths.

Enhancements:

  • Enable debug logging for Flask-SocketIO and uvicorn when running in development mode, while keeping production logging concise.
  • Tighten security for serving plugin web UI assets by enforcing directory constraints and clearer error handling.
  • Clean up and standardize string quoting, formatting, and minor style issues across helpers and Docker utilities.
  • Adjust self-update manager to launch the UI process with explicit non-dockerized and development flags for local runs.
  • Update Python dependencies to newer patch versions for dotenv and pydantic, and align pypdf to a compatible version.

Build:

  • Switch Docker build to use a dedicated local Dockerfile and introduce a reusable base image built from docker/base.
  • Refine base Dockerfile locale initialization and file layout to improve caching and reproducibility.

Deployment:

  • Expose debug-related environment variables for the run_ui supervisor program to improve observability in containerized deployments.

Tests:

  • Add a Makefile target to run the test suite inside a Docker container for easier validation in containerized environments.

Summary by CodeRabbit

  • New Features

    • Added Docker containerized testing support
    • Multi-architecture Docker image building for deployments
  • Bug Fixes

    • Plugin system now gracefully handles missing plugin directories
  • Chores

    • Updated dependencies (python-dotenv, pydantic)
    • Enhanced development-mode debug logging and configuration
    • Code formatting and structural improvements across multiple modules

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 1, 2026

Reviewer's Guide

Introduce development-focused behavior (debug logging, dev flags, Docker flows) and robustness improvements (plugin dir existence checks, safer file browsing and uploads), alongside formatting cleanups and some dependency/Docker build updates.

Sequence diagram for user message upload and extension processing

sequenceDiagram
    actor User
    participant WebUI as WebUI_API_message_communicate
    participant Request as FastAPI_Request
    participant Files as helpers_files
    participant Ext as extension_system
    participant MQ as message_queue
    participant Agent as AgentContext

    User->>WebUI: POST /api/message (form + attachments)
    WebUI->>Request: request.files.getlist("attachments")
    alt attachments_present
        WebUI->>Files: get_abs_path("usr/uploads")
        WebUI->>Files: os.makedirs(upload_folder_ext, exist_ok=True)
        loop for each attachment
            WebUI->>Files: save attachment to upload_folder_ext
            WebUI->>Files: build path (upload_folder_int or upload_folder_ext)
            WebUI-->>WebUI: append to attachment_paths
        end
    end
    WebUI-->>WebUI: data = {message, attachment_paths}
    WebUI->>Ext: call_extensions_async("user_message_ui", agent=context.get_agent(), data)
    Ext-->>WebUI: possibly modified data
    WebUI-->>WebUI: update message, attachment_paths from data
    WebUI->>MQ: log_user_message(context, message, attachment_paths, message_id)
    WebUI->>Agent: context.communicate(UserMessage(message, attachments, id))
    Agent-->>WebUI: response
    WebUI-->>User: response
Loading

Sequence diagram for secure plugin asset serving in the web UI

sequenceDiagram
    actor Browser
    participant WebUI as run_ui__serve_plugin_asset
    participant Plugins as helpers_plugins
    participant Files as helpers_files

    Browser->>WebUI: GET /usr/plugins/<plugin_name>/<asset_path>
    WebUI->>Plugins: find_plugin_dir(plugin_name)
    alt plugin_dir_not_found
        Plugins-->>WebUI: None
        WebUI-->>Browser: 404 Plugin not found
    else plugin_dir_found
        Plugins-->>WebUI: plugin_dir
        WebUI->>Files: get_abs_path(plugin_dir, asset_path) as asset_file
        WebUI->>Files: get_abs_path(plugin_dir, "webui") as webui_dir
        WebUI->>Files: get_abs_path(plugin_dir, "extensions/webui") as webui_extensions_dir
        WebUI->>Files: is_in_dir(asset_file, webui_dir)
        WebUI->>Files: is_in_dir(asset_file, webui_extensions_dir)
        alt not_in_allowed_dirs
            WebUI-->>Browser: 403 Access denied
        else in_allowed_dirs
            WebUI->>Files: is_file(asset_file)
            alt not_a_file
                WebUI-->>Browser: 404 Asset not found
            else is_file
                WebUI-->>Browser: send_file(asset_file)
            end
        end
    end
Loading

Updated class diagram for FileBrowser and related helpers

classDiagram
    class FileBrowser {
        -Path base_dir
        +ALLOWED_EXTENSIONS: dict
        +MAX_FILE_SIZE: int
        +MAX_UPLOAD_DIR_SIZE: int
        +FileBrowser()
        -_validate_path(path: Union_str_Path) Path
        -_check_file_size(file) bool
        -_calculate_directory_size(directory: Path) int
        -_is_allowed_file(filename: str, file) bool
        -_get_file_extension(filename: str) str
        -_get_files_via_ls(full_path: Path) Tuple_List_Dict_List_Dict
        -_get_file_type(filename: str) str
        +get_files(current_path: str) dict
        +read_file(path: str, max_size: int) dict
        +upload_file(file, current_path: str) dict
        +delete_file(path: str) dict
    }

    class RuntimeHelper {
        +is_dockerized() bool
    }

    class FilesHelper {
        +get_abs_path(*paths: str) str
    }

    class PrintStyle {
        +error(message: str) void
    }

    FileBrowser --> RuntimeHelper : uses
    FileBrowser --> FilesHelper : uses
    FileBrowser --> PrintStyle : logs errors
Loading

File-Level Changes

Change Details Files
Harden plugin discovery and configuration handling against missing roots and simplify several helpers.
  • Guard plugin root iteration in get_plugins_list and get_enhanced_plugins_list by skipping non-existent directories.
  • Normalize type hints and inline several helper calls (e.g., get_plugin_meta, find_plugin_dir, get_plugin_paths, toggle and config helpers) without changing behavior.
  • Ensure delete_plugin correctly detects Python files using explicit path concatenation before deletion.
helpers/plugins.py
Add development-aware logging and WebSocket diagnostics for the UI server.
  • Set root logging level to DEBUG in development and WARNING otherwise in run_ui.
  • Enable Socket.IO and engineio logging only in development mode and propagate dev-aware log level and access_log config to uvicorn.
  • Minor cleanup of auth handlers, plugin asset serving, and WebSocket namespace registration without functional changes aside from consistent quoting/formatting.
run_ui.py
Improve file browser and message upload robustness and style consistency.
  • Fix indentation bug in FileBrowser._validate_path and normalize string quoting and dict/return formatting.
  • Use POSIX-style ls parsing with clearer symlink handling and extended metadata in _get_files_via_ls without altering external API.
  • Correct upload path variable name indentation and keep separate internal/external upload roots in API message handler, plus minor async extension-call formatting.
helpers/file_browser.py
api/message.py
Adjust Docker update flow, base image build, and supervisor configuration for development and multi-arch usage.
  • Change self_update_manager to launch run_ui with --dockerized=false and --development=true and compact various log/error messages.
  • Add Makefile targets and variables for building/pushing a reusable base image and running tests in Docker; switch docker-build to use DockerfileLocal.
  • Update base Dockerfile to copy from docker/base/fs and adjust locale setup commands.
  • Set supervisor run_ui environment to enable debug logging flags inside the container.
docker/run/fs/exe/self_update_manager.py
Makefile
docker/base/Dockerfile
docker/run/fs/etc/supervisor/conf.d/supervisord.conf
Update a few Python package versions in requirements.
  • Downgrade pypdf from 6.9.2 to 6.9.1 and bump python-dotenv and pydantic to newer patch versions.
requirements.txt

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Introduces Docker base image build targets and multi-architecture support to Makefile; updates dependency versions in requirements.txt; adds conditional development-mode logging and configuration to run_ui.py; enhances plugin discovery with filesystem existence checks; refactors environment variables in supervisor configuration and application startup; normalizes code formatting across multiple Python files.

Changes

Cohort / File(s) Summary
Docker Build Infrastructure
Makefile
Adds Docker base image build targets (docker-base, docker-base-push) with multi-platform support (linux/amd64,linux/arm64); modifies docker-build to explicitly use DockerfileLocal; introduces docker-test target for pytest execution in containers.
Docker Base Image Configuration
docker/base/Dockerfile
Changes locale generation from dpkg-reconfigure to locale-gen with explicit environment file writes; updates COPY source path from ./fs/ to docker/base/fs/.
Application Runtime Environment
docker/run/fs/etc/supervisor/conf.d/supervisord.conf, docker/run/fs/exe/self_update_manager.py
Adds explicit debug environment variables (CTX0_DEBUG, CTX0_WS_DEBUG, LOGLEVEL, UVICORN_LOG_LEVEL) to supervisor configuration; updates launch_ui_process() to pass --dockerized=false --development=true flags instead of --dockerized=true.
Core Application Configuration
run_ui.py
Implements development-mode conditional logic: switches logging between DEBUG (development) and WARNING (production); enables Socket.IO and Uvicorn logging only in development; normalizes string quoting throughout.
Helper Utilities Refactoring
helpers/file_browser.py, helpers/plugins.py
Adds filesystem existence guards in plugin discovery (get_plugins_list(), get_enhanced_plugins_list()); normalizes code formatting and quote styles; collapses multi-line expressions into single-line calls; adds Union type import to file_browser.py.
API Formatting
api/message.py
Collapses multi-line function calls and assignments into single-line format without changing runtime semantics or control flow.
Dependency Updates
requirements.txt
Downgrades pypdf (6.9.2 → 6.9.1); upgrades python-dotenv (1.1.0 → 1.2.1) and pydantic (2.11.7 → 2.12.5).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A sprinkle of Docker, a dash of debug logs,
Environment variables dance through the cogs,
From formatting clean to conditions so wise,
Plugin guards watching with filesystem eyes,
Development modes in the night softly glow. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title "Development" is too vague and generic; it does not convey any meaningful information about the changeset's primary purpose. Use a descriptive title that summarizes the main change, such as 'Add Docker build improvements and enable debug logging' or another title reflecting the key updates.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 security issue, 4 other issues, and left some high level feedback:

Security issues:

  • Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)

General comments:

  • In docker/run/fs/exe/self_update_manager.py::launch_ui_process, the UI is now launched with --dockerized=false and --development=true inside the Docker environment, which seems counter to the expected production/docker runtime behavior and may break path assumptions and logging defaults; consider keeping --dockerized=true and making --development configurable instead of hardcoded.
  • The Supervisor config for run_ui now hardcodes CTX0_DEBUG="true", CTX0_WS_DEBUG="true", LOGLEVEL="DEBUG", UVICORN_LOG_LEVEL="debug", which will force verbose logging in all environments; it would be safer to gate these on environment variables or a profile so production deployments can run with lower log levels.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `docker/run/fs/exe/self_update_manager.py::launch_ui_process`, the UI is now launched with `--dockerized=false` and `--development=true` inside the Docker environment, which seems counter to the expected production/docker runtime behavior and may break path assumptions and logging defaults; consider keeping `--dockerized=true` and making `--development` configurable instead of hardcoded.
- The Supervisor config for `run_ui` now hardcodes `CTX0_DEBUG="true", CTX0_WS_DEBUG="true", LOGLEVEL="DEBUG", UVICORN_LOG_LEVEL="debug"`, which will force verbose logging in all environments; it would be safer to gate these on environment variables or a profile so production deployments can run with lower log levels.

## Individual Comments

### Comment 1
<location path="helpers/file_browser.py" line_range="44" />
<code_context>
             full_path = path.resolve()

         if not str(full_path).startswith(str(self.base_dir)):
-             raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
+            raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
         return full_path
</code_context>
<issue_to_address>
**🚨 issue (security):** Strengthen the path validation to avoid false positives and potential path traversal issues.

Using `str(full_path).startswith(str(self.base_dir))` can both misclassify paths (e.g. `/ctx0_backup/foo` passes when `base_dir` is `/ctx0`) and leave room for traversal issues. Prefer comparing resolved paths, for example:

```python
full_path = full_path.resolve()
base_dir = self.base_dir.resolve()
if os.path.commonpath([full_path, base_dir]) != str(base_dir):
    raise ValueError(...)
```

or `full_path.is_relative_to(base_dir)` on Python 3.9+ (with a fallback).
</issue_to_address>

### Comment 2
<location path="docker/run/fs/exe/self_update_manager.py" line_range="680-686" />
<code_context>
         [
             sys.executable,
             str(repo_dir / "run_ui.py"),
-            "--dockerized=true",
+            "--dockerized=false",
+            "--development=true",
             "--port=80",
             "--host=0.0.0.0",
         ],
</code_context>
<issue_to_address>
**issue (bug_risk):** Re-evaluate `--dockerized=false` in a Docker-managed UI process; it likely should stay dockerized.

Passing `--dockerized=false` here will make `runtime.is_dockerized()` return false for a process that’s actually running in a container, which can change paths and config (e.g., upload/base directories) and potentially break container-specific behavior. If the goal is just to enable dev logging, keep `--dockerized=true` and only add `--development=true` so the process still behaves as containerized code.
</issue_to_address>

### Comment 3
<location path="Makefile" line_range="169-173" />
<code_context>
+
+docker-base: ## Build base Docker image for current platform
+	@echo "$(BLUE)Building base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
+	docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .
+
+docker-base-push: ## Build and push multi-arch base Docker image
+	@echo "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
+	docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
+
 docker-build: ## Build Docker image
</code_context>
<issue_to_address>
**suggestion:** Avoid hard-coding the `buildx` builder name to reduce setup friction.

These targets currently depend on a preconfigured BuildKit builder named `mybuilder`, so they’ll fail on machines where that builder doesn’t exist.

Consider either:
- dropping the `--builder` flag and using the default builder, or
- making the builder name configurable (e.g. `DOCKER_BUILDER ?= mybuilder`) and documenting the requirement, or
- having the Makefile create/configure the builder if it’s missing.

That would make these targets more portable across different environments.

Suggested implementation:

```
docker-base: ## Build base Docker image for current platform
	@echo "$(BLUE)Building base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
	docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .

```

```
docker-base-push: ## Build and push multi-arch base Docker image
	@echo "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
	docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .

```
</issue_to_address>

### Comment 4
<location path="api/message.py" line_range="59" />
<code_context>

         # call extension point, alow it to modify data
         data = {"message": message, "attachment_paths": attachment_paths}
-        await extension.call_extensions_async(
</code_context>
<issue_to_address>
**nitpick (typo):** Fix the typo in the comment for clarity.

Change `alow` to `allow` in the comment:

```python
# call extension point, allow it to modify data
```

```suggestion
        # call extension point, allow it to modify data
```
</issue_to_address>

### Comment 5
<location path="docker/run/fs/exe/self_update_manager.py" line_range="680-687" />
<code_context>
    return subprocess.Popen(
        [
            sys.executable,
            str(repo_dir / "run_ui.py"),
            "--dockerized=false",
            "--development=true",
            "--port=80",
            "--host=0.0.0.0",
        ],
        cwd=repo_dir,
    )
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread helpers/file_browser.py
@@ -41,7 +42,7 @@ def _validate_path(self, path: Union[str, Path]) -> Path:
full_path = path.resolve()

if not str(full_path).startswith(str(self.base_dir)):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 issue (security): Strengthen the path validation to avoid false positives and potential path traversal issues.

Using str(full_path).startswith(str(self.base_dir)) can both misclassify paths (e.g. /ctx0_backup/foo passes when base_dir is /ctx0) and leave room for traversal issues. Prefer comparing resolved paths, for example:

full_path = full_path.resolve()
base_dir = self.base_dir.resolve()
if os.path.commonpath([full_path, base_dir]) != str(base_dir):
    raise ValueError(...)

or full_path.is_relative_to(base_dir) on Python 3.9+ (with a fallback).

Comment on lines 680 to 686
[
sys.executable,
str(repo_dir / "run_ui.py"),
"--dockerized=true",
"--dockerized=false",
"--development=true",
"--port=80",
"--host=0.0.0.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Re-evaluate --dockerized=false in a Docker-managed UI process; it likely should stay dockerized.

Passing --dockerized=false here will make runtime.is_dockerized() return false for a process that’s actually running in a container, which can change paths and config (e.g., upload/base directories) and potentially break container-specific behavior. If the goal is just to enable dev logging, keep --dockerized=true and only add --development=true so the process still behaves as containerized code.

Comment thread Makefile
Comment on lines +169 to +173
docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .

docker-base-push: ## Build and push multi-arch base Docker image
@echo "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Avoid hard-coding the buildx builder name to reduce setup friction.

These targets currently depend on a preconfigured BuildKit builder named mybuilder, so they’ll fail on machines where that builder doesn’t exist.

Consider either:

  • dropping the --builder flag and using the default builder, or
  • making the builder name configurable (e.g. DOCKER_BUILDER ?= mybuilder) and documenting the requirement, or
  • having the Makefile create/configure the builder if it’s missing.

That would make these targets more portable across different environments.

Suggested implementation:

docker-base: ## Build base Docker image for current platform
	@echo "$(BLUE)Building base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
	docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .

docker-base-push: ## Build and push multi-arch base Docker image
	@echo "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
	docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .

Comment thread api/message.py
@@ -60,9 +58,7 @@ async def communicate(self, input: dict, request: Request):

# call extension point, alow it to modify data
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick (typo): Fix the typo in the comment for clarity.

Change alow to allow in the comment:

# call extension point, allow it to modify data
Suggested change
# call extension point, alow it to modify data
# call extension point, allow it to modify data

Comment on lines 680 to 687
[
sys.executable,
str(repo_dir / "run_ui.py"),
"--dockerized=true",
"--dockerized=false",
"--development=true",
"--port=80",
"--host=0.0.0.0",
],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

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: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker/run/fs/exe/self_update_manager.py`:
- Around line 683-684: The UI launcher in self_update_manager.py currently
passes "--dockerized=false" and "--development=true"; change
"--dockerized=false" to "--dockerized=true" to reflect the container runtime
(matching the prepare.py invocation) and remove the redundant
"--development=true" flag because is_development() derives its value from
is_dockerized(); update the argument list where the UI is launched so it passes
"--dockerized=true" and omit "--development=true".

In `@helpers/file_browser.py`:
- Line 207: The subprocess.run call that executes ["ls", "-la", str(full_path)]
should use an absolute, validated path to the ls executable to avoid PATH-hijack
issues: resolve the executable with shutil.which("ls") (or hardcode a trusted
path like "/bin/ls"), check the result is not None and is an absolute path, then
call subprocess.run([resolved_ls, "-la", str(full_path)], capture_output=True,
text=True, timeout=30); update the code around the result = subprocess.run(...)
line in helpers/file_browser.py and add a clear error/exception path if the
resolver returns None.
- Around line 44-45: The current boundary check uses string startswith on
full_path and self.base_dir which is bypassable by prefix collisions; update the
check in the method that computes full_path (refer to variables full_path and
self.base_dir) to use resolved pathlib path comparison instead — resolve both
paths (e.g., full_path.resolve() and self.base_dir.resolve()) and either use
Path.relative_to(self.base_dir) in a try/except to ensure full_path is inside
the base directory or verify that self.base_dir is a parent of full_path by
checking self.base_dir in full_path.parents; raise the same ValueError if the
validation fails.

In `@helpers/plugins.py`:
- Around line 592-593: The code assumes find_plugin_dir(plugin_name) always
returns a path; guard that call in the functions using file_path/default_used
(where file_path = files.get_abs_path(find_plugin_dir(plugin_name),
CONFIG_DEFAULT_FILE_NAME) and the similar usage at the other occurrence) by
checking the result of find_plugin_dir(plugin_name) first, and if it is None
return None immediately for unknown plugins; ensure you only call
files.get_abs_path(...) when the plugin_dir is non-None and preserve setting
default_used = True only after confirming the plugin dir exists.

In `@Makefile`:
- Around line 169-173: The Makefile hardcodes "--builder mybuilder" in the
docker buildx invocations (see the top docker buildx build line and the
docker-base-push target), which breaks on clean CI/dev hosts; change these
invocations to use a configurable variable (e.g. DOCKER_BUILDX_BUILDER) or omit
the flag entirely so the default builder is used, and only pass "--builder
$(DOCKER_BUILDX_BUILDER)" when that variable is set (or document/ensure builder
creation separately); update the docker-base-push and the other build rule to
reference the variable instead of the literal "mybuilder".

In `@run_ui.py`:
- Around line 34-37: Move both logging and Socket.IO logger configuration out of
module import time and run them after runtime.initialize() is called: remove
calls to runtime.is_development() at top-level and instead invoke the existing
_configure_logging() (or create it if missing) from inside run() immediately
after calling runtime.initialize(), and similarly defer the Socket.IO logger
flag setup (the code currently at lines referencing Socket.IO config) until
after initialization; use runtime.is_development() only after
runtime.initialize() so the flags and levels reflect the initialized args.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74594a7a-40e5-46f9-9758-1db7648edecd

📥 Commits

Reviewing files that changed from the base of the PR and between 61053a1 and ae22df7.

📒 Files selected for processing (9)
  • Makefile
  • api/message.py
  • docker/base/Dockerfile
  • docker/run/fs/etc/supervisor/conf.d/supervisord.conf
  • docker/run/fs/exe/self_update_manager.py
  • helpers/file_browser.py
  • helpers/plugins.py
  • requirements.txt
  • run_ui.py

Comment on lines +683 to +684
"--dockerized=false",
"--development=true",
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify how docker/development flags are interpreted and consumed
rg -n -C3 'def is_dockerized|def is_development|def initialize|get_arg\(' helpers/runtime.py
rg -n -C3 '--dockerized|--development|is_dockerized\(|is_development\(' run_ui.py docker/run/fs/exe/self_update_manager.py

Repository: FortiShield/CtxAI

Length of output: 1353


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check exact lines 683-684 in self_update_manager.py
echo "=== Lines 680-690 in self_update_manager.py ==="
sed -n '680,690p' docker/run/fs/exe/self_update_manager.py | cat -n

# Search for flag usage patterns separately
echo -e "\n=== Flag usage in self_update_manager.py ==="
rg -n 'dockerized|development' docker/run/fs/exe/self_update_manager.py

# Search for how this is invoked from run_ui or other places
echo -e "\n=== References to self_update_manager ==="
rg -n 'self_update_manager' --type py

Repository: FortiShield/CtxAI

Length of output: 3658


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get broader context around lines 674 and 683
echo "=== Context around line 674 (prepare_script) ==="
sed -n '670,680p' docker/run/fs/exe/self_update_manager.py | cat -n

echo -e "\n=== Context around line 683 (run_ui) ==="
sed -n '675,695p' docker/run/fs/exe/self_update_manager.py | cat -n

# Search for function definition containing these lines
echo -e "\n=== Function containing these lines ==="
rg -n -B20 'run_command.*prepare_script.*--dockerized=true' docker/run/fs/exe/self_update_manager.py | head -40

# Check run_ui.py to see how it uses these flags
echo -e "\n=== run_ui.py usage of dockerized and development ==="
rg -n 'dockerized|development' run_ui.py | head -20

Repository: FortiShield/CtxAI

Length of output: 2651


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check where launch_ui_process is called from
echo "=== Where is launch_ui_process called? ==="
rg -n 'launch_ui_process' docker/run/fs/exe/self_update_manager.py

# Check the broader context of the docker-run-ui command
echo -e "\n=== docker-run-ui command context ==="
rg -n -B5 -A10 'docker-run-ui' docker/run/fs/exe/self_update_manager.py | head -50

# Check if there are any other UI launches and how they set these flags
echo -e "\n=== Other run_ui.py invocations ==="
rg -n 'run_ui.py' --type py

# Check run_ui.py itself to see how it determines if it should run in dev mode
echo -e "\n=== run_ui.py initialization of runtime args ==="
head -50 run_ui.py

Repository: FortiShield/CtxAI

Length of output: 3509


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full docker_run_ui function to understand the overall flow
echo "=== docker_run_ui function ==="
rg -n -A50 'def docker_run_ui\(' docker/run/fs/exe/self_update_manager.py | head -70

# Check if there are comments or documentation about these flags
echo -e "\n=== Documentation/comments about dockerized flag ==="
rg -n -B2 -A2 'dockerized' docker/run/fs/exe/self_update_manager.py

# Check if run_ui.py is ever launched with --dockerized=true
echo -e "\n=== All run_ui.py invocations with flags ==="
rg -n -A10 'run_ui.py' docker/run/fs/exe/self_update_manager.py

Repository: FortiShield/CtxAI

Length of output: 3381


Set --dockerized=true to match the actual runtime environment.

The self_update_manager runs inside a docker container (as indicated by its path in docker/run/fs/exe/). Setting --dockerized=false when launching the UI creates a runtime mismatch that can break environment-specific behavior, path handling, and feature detection. Additionally, line 674 correctly uses --dockerized=true for prepare.py, but line 683 contradicts this for the UI launcher.

Since is_development() in runtime.py returns not is_dockerized(), the explicit --development=true at line 684 is redundant once the dockerized flag is fixed.

🔧 Proposed fix
-            "--dockerized=false",
-            "--development=true",
+            "--dockerized=true",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"--dockerized=false",
"--development=true",
"--dockerized=true",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker/run/fs/exe/self_update_manager.py` around lines 683 - 684, The UI
launcher in self_update_manager.py currently passes "--dockerized=false" and
"--development=true"; change "--dockerized=false" to "--dockerized=true" to
reflect the container runtime (matching the prepare.py invocation) and remove
the redundant "--development=true" flag because is_development() derives its
value from is_dockerized(); update the argument list where the UI is launched so
it passes "--dockerized=true" and omit "--development=true".

Comment thread helpers/file_browser.py
Comment on lines 44 to +45
if not str(full_path).startswith(str(self.base_dir)):
raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
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 | 🔴 Critical

Path boundary check is bypassable with prefix collisions.

Line 44 uses a string startswith check, so paths like /ctx0_evil/... can pass when base dir is /ctx0. Use path-relative validation instead.

🔧 Proposed fix
-        if not str(full_path).startswith(str(self.base_dir)):
+        try:
+            full_path.relative_to(self.base_dir)
+        except ValueError:
             raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not str(full_path).startswith(str(self.base_dir)):
raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
try:
full_path.relative_to(self.base_dir)
except ValueError:
raise ValueError(f"Access denied: path {full_path} is outside base directory {self.base_dir}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@helpers/file_browser.py` around lines 44 - 45, The current boundary check
uses string startswith on full_path and self.base_dir which is bypassable by
prefix collisions; update the check in the method that computes full_path (refer
to variables full_path and self.base_dir) to use resolved pathlib path
comparison instead — resolve both paths (e.g., full_path.resolve() and
self.base_dir.resolve()) and either use Path.relative_to(self.base_dir) in a
try/except to ensure full_path is inside the base directory or verify that
self.base_dir is a parent of full_path by checking self.base_dir in
full_path.parents; raise the same ValueError if the validation fails.

Comment thread helpers/file_browser.py
text=True,
timeout=30
)
result = subprocess.run(["ls", "-la", str(full_path)], capture_output=True, text=True, timeout=30)
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

Harden subprocess executable resolution for ls.

Line 207 launches ls via PATH lookup. Use an absolute executable path to reduce PATH-hijack risk and satisfy S607.

🔧 Proposed fix
-            result = subprocess.run(["ls", "-la", str(full_path)], capture_output=True, text=True, timeout=30)
+            result = subprocess.run(["/bin/ls", "-la", str(full_path)], capture_output=True, text=True, timeout=30)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result = subprocess.run(["ls", "-la", str(full_path)], capture_output=True, text=True, timeout=30)
result = subprocess.run(["/bin/ls", "-la", str(full_path)], capture_output=True, text=True, timeout=30)
🧰 Tools
🪛 Ruff (0.15.7)

[error] 207-207: subprocess call: check for execution of untrusted input

(S603)


[error] 207-207: Starting a process with a partial executable path

(S607)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@helpers/file_browser.py` at line 207, The subprocess.run call that executes
["ls", "-la", str(full_path)] should use an absolute, validated path to the ls
executable to avoid PATH-hijack issues: resolve the executable with
shutil.which("ls") (or hardcode a trusted path like "/bin/ls"), check the result
is not None and is an absolute path, then call subprocess.run([resolved_ls,
"-la", str(full_path)], capture_output=True, text=True, timeout=30); update the
code around the result = subprocess.run(...) line in helpers/file_browser.py and
add a clear error/exception path if the resolver returns None.

Comment thread helpers/plugins.py
Comment on lines +592 to 593
file_path = files.get_abs_path(find_plugin_dir(plugin_name), CONFIG_DEFAULT_FILE_NAME)
default_used = True
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 | 🟠 Major

Guard find_plugin_dir(...) before building default-config paths.

If find_plugin_dir(plugin_name) returns None, these path builds can fail unexpectedly. Return None early for unknown plugins.

🔧 Proposed fix
 def get_plugin_config(
@@
-    if not file_path:
-        file_path = files.get_abs_path(find_plugin_dir(plugin_name), CONFIG_DEFAULT_FILE_NAME)
+    if not file_path:
+        plugin_dir = find_plugin_dir(plugin_name)
+        if not plugin_dir:
+            return None
+        file_path = files.get_abs_path(plugin_dir, CONFIG_DEFAULT_FILE_NAME)
         default_used = True
@@
 def get_default_plugin_config(plugin_name: str):
-    file_path = files.get_abs_path(find_plugin_dir(plugin_name), CONFIG_DEFAULT_FILE_NAME)
+    plugin_dir = find_plugin_dir(plugin_name)
+    if not plugin_dir:
+        return None
+    file_path = files.get_abs_path(plugin_dir, CONFIG_DEFAULT_FILE_NAME)

Also applies to: 616-617

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@helpers/plugins.py` around lines 592 - 593, The code assumes
find_plugin_dir(plugin_name) always returns a path; guard that call in the
functions using file_path/default_used (where file_path =
files.get_abs_path(find_plugin_dir(plugin_name), CONFIG_DEFAULT_FILE_NAME) and
the similar usage at the other occurrence) by checking the result of
find_plugin_dir(plugin_name) first, and if it is None return None immediately
for unknown plugins; ensure you only call files.get_abs_path(...) when the
plugin_dir is non-None and preserve setting default_used = True only after
confirming the plugin dir exists.

Comment thread Makefile
Comment on lines +169 to +173
docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .

docker-base-push: ## Build and push multi-arch base Docker image
@echo "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
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

Avoid hardcoding buildx builder name.

Using --builder mybuilder assumes local pre-configuration and makes these targets fail in clean CI/dev environments.

🔧 Proposed fix
-docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .
+docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .

-docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
+docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .
docker-base-push: ## Build and push multi-arch base Docker image
@echo "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
docker buildx build --builder mybuilder -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --load .
docker-base-push: ## Build and push multi-arch base Docker image
`@echo` "$(BLUE)Building and pushing multi-arch base Docker image $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG)...$(NC)"
docker buildx build -f docker/base/Dockerfile -t $(DOCKER_BASE_IMAGE):$(DOCKER_BASE_TAG) --platform $(DOCKER_PLATFORMS) --push .
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 169 - 173, The Makefile hardcodes "--builder
mybuilder" in the docker buildx invocations (see the top docker buildx build
line and the docker-base-push target), which breaks on clean CI/dev hosts;
change these invocations to use a configurable variable (e.g.
DOCKER_BUILDX_BUILDER) or omit the flag entirely so the default builder is used,
and only pass "--builder $(DOCKER_BUILDX_BUILDER)" when that variable is set (or
document/ensure builder creation separately); update the docker-base-push and
the other build rule to reference the variable instead of the literal
"mybuilder".

Comment thread run_ui.py
Comment on lines +34 to +37
if runtime.is_development():
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.WARNING)
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'runtime\.is_development\(\)|runtime\.initialize\(' run_ui.py
rg -n -C3 'def initialize|def is_development' helpers/runtime.py

Repository: FortiShield/CtxAI

Length of output: 1707


Logging configuration happens before runtime initialization, causing incorrect development mode detection.

Lines 34–37 and 74–75 call runtime.is_development() at module import time, but runtime.initialize() (which sets the args global that is_development() depends on) isn't called until line 472. The logging level and Socket.IO logger flags will be determined using an uninitialized state.

Suggested fix
- if runtime.is_development():
-     logging.getLogger().setLevel(logging.DEBUG)
- else:
-     logging.getLogger().setLevel(logging.WARNING)
+ def _configure_logging() -> None:
+     logging.getLogger().setLevel(logging.DEBUG if runtime.is_development() else logging.WARNING)

Call _configure_logging() after runtime.initialize() inside the run() function. Apply the same pattern to lines 74–75 by deferring Socket.IO configuration until after initialization is guaranteed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@run_ui.py` around lines 34 - 37, Move both logging and Socket.IO logger
configuration out of module import time and run them after runtime.initialize()
is called: remove calls to runtime.is_development() at top-level and instead
invoke the existing _configure_logging() (or create it if missing) from inside
run() immediately after calling runtime.initialize(), and similarly defer the
Socket.IO logger flag setup (the code currently at lines referencing Socket.IO
config) until after initialization; use runtime.is_development() only after
runtime.initialize() so the flags and levels reflect the initialized args.

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