← MCP server and skill grades

serena

oraios/serena

F
Type
MCP server
Scanned
Oct 29, 2026
Version
ede329aaf0c8
Files read
1171

Serena is a server that gives AI coding assistants the ability to navigate, edit, and refactor code, manage project notes, and run commands using language servers or a JetBrains IDE plugin. The biggest risk is that a malicious project, a prompt injection attack, or the unauthenticated local web dashboard could be used to run arbitrary system commands, execute Python code, or modify files anywhere on your computer.

What it can reach

Files
Reads, creates, edits, moves, and deletes source code, symbol caches, memory files, and global or project configuration files.
Shell commands
Executes arbitrary shell commands and Python code, runs project startup scripts, and launches language servers and package managers like npm, uv, and dotnet.
Network
Starts local web servers for its dashboard and services, connects to local IDE ports, downloads language server binaries, and contacts external servers for updates, news, and default usage telemetry.
Secrets
Reads environment variables like ANTHROPIC_API_KEY and stores a shared authentication secret in the global configuration file.
Browser
Opens the local web dashboard in your default web browser or a dedicated window on launch or when triggered by the AI.
System
Inspects and terminates running processes and network connections to manage background language servers and database locks.

Findings

2 critical5 high7 medium4 low
  1. CriticalPrompt-injectable shell command execution via ExecuteShellCommandTool

    src/serena/tools/cmd_tools.py:54

    ExecuteShellCommandTool is enabled by default in general contexts and runs whatever command string the agent passes using a system shell (shell=True). If the agent reads untrusted content (issues, web pages, PRs) containing a prompt injection, an attacker can run arbitrary commands on the user's machine.

    Fix: Disable ExecuteShellCommandTool by default (mark it with ToolMarkerOptional) or require explicit user confirmation before running shell commands.

  2. CriticalUnrestricted Python code execution in SerenaReplTool

    src/serena/repl/repl.py:354

    When configured to use the REPL agent interface, Serena exposes the serena_repl tool, which compiles and executes arbitrary Python statements via exec() and eval() in the server process without sandboxing. A prompt injection can use this tool to import standard library modules (such as os or subprocess) and execute arbitrary code on the host.

    Fix: Restrict the REPL execution environment or require explicit user approval for REPL tool invocations, and clearly document that REPL mode grants full Python execution privileges.

  3. HighAll project paths are trusted by default, enabling automatic command execution from project configs

    src/serena/config/serena_config.py:859

    SerenaConfig defaults trusted_project_path_patterns to ['**'], treating every project on disk as trusted if the setting is not overridden. For trusted projects, Serena automatically runs the project's .serena/project.yml activation_command via subprocess.Popen(..., shell=True), honors project-level ls_specific_settings (such as custom language server executables in ls_path or ls_base_cmd), and follows symlinks outside the repository. Opening an untrusted repo—or an attacker using Serena's file-editing tools via prompt injection to modify .serena/project.yml—can result in arbitrary command execution.

    Fix: Default trusted_project_path_patterns to an empty list [] so projects are untrusted until explicitly allowlisted by the user, and block agent file-editing tools from modifying the .serena/ directory.

  4. HighUnauthenticated Web Dashboard API exposes secrets and allows overwriting global configuration

    src/serena/dashboard/dashboard_api.py:439

    The Flask web dashboard is enabled by default and has no authentication on any of its endpoints. Any local process—or any network attacker when web_dashboard_listen_address is set to 0.0.0.0 (as in the provided Dockerfile)—can call /get_serena_config to read ~/.serena/serena_config.yml (including auth_secret) and call /save_serena_config to overwrite it (for example, injecting a malicious ls_path and calling /add_language to execute arbitrary commands).

    Fix: Require Bearer token authentication (using auth_secret, as ProjectServer does) or a session token on all dashboard API routes, and avoid binding the dashboard to 0.0.0.0 without authentication.

  5. HighActivateProjectTool allows switching to arbitrary directories on the host filesystem

    src/serena/agent.py:1576

    When not operating in single-project mode, the activate_project tool accepts any directory path on the user's machine, auto-registers it as a project, and activates it. A prompt-injected agent can call activate_project on sensitive directories (such as the user's home directory or ~/.ssh) to read or modify files outside the intended workspace.

    Fix: Restrict activate_project to already-registered projects or directories within an explicitly configured workspace root.

  6. HighDOM XSS in Web Dashboard via Unescaped Project and Memory Names

    src/serena/resources/dashboard/dashboard.js:975

    The Serena web dashboard (which opens automatically in the browser and polls /get_config_overview every second) interpolates several values directly into HTML strings passed to .html() without escaping. In particular, displayProjects inserts project.name and project.path (loaded from .serena/project.yml in a repository) without calling this.escapeHtml(), displayConfig inserts config.available_memories (derived from .serena/memories/ filenames or write_memory calls) unescaped, and BannerRotation.loadBanners inserts remote banner metadata unescaped. Opening an untrusted repository with a crafted project_name or memory filename executes arbitrary JavaScript in the dashboard origin, which can call /save_serena_config to mark all projects as trusted and configure arbitrary commands in the global configuration.

    Fix: Escape all dynamic values (project.name, project.path, memory, toolName, language, and banner fields) using this.escapeHtml() before concatenating them into HTML, or construct DOM elements using .text() and .attr().

  7. HighMissing Path Confinement Validation in JetBrains Backend API

    src/serena/repl/api/jb_api.py:413

    Unlike FsApi, EditApi, and LspApi, none of the methods in JetBrainsApi call self._get_project().validate_relative_path(relative_path). In find_declaration, self._get_project().read_file(relative_path) is called directly with the unvalidated path, allowing .. path traversal or symlink escapes outside an untrusted project root. Similarly, rename, move, safe_delete, inline_symbol, and run_inspections forward unvalidated relative_path and target_relative_path arguments directly to the JetBrains IDE plugin.

    Fix: Call self._get_project().validate_relative_path(relative_path) (and validate target_relative_path in move) in all JetBrainsApi methods before reading files from disk or sending requests to the JetBrains plugin client (allowing <ext: prefixes only on read-only dependency queries).

  8. MediumPath traversal and unsafe pickle loading in session hooks

    src/serena/hooks.py:53

    Hook.__init__ constructs session_persistence_dir by joining serena_home_dir/hook_data with the raw session_id from stdin JSON without checking for '..' or path separators. SessionEndCleanupHook calls shutil.rmtree on this path, and PreToolUseRemindAboutSymbolicToolsHook.ToolUseCounter.load calls pickle.load on tool_use_counter.pkl inside it. A crafted session_id can delete arbitrary directories or deserialize untrusted pickle files.

    Fix: Validate that session_id contains only safe characters (e.g. alphanumeric, hyphens, and underscores) and replace pickle persistence with JSON.

  9. MediumUnauthenticated Tray Manager HTTP server allows opening arbitrary URLs

    src/serena/dashboard/dashboard_tray_manager.py:103

    SerenaDashboardTrayManager listens on 127.0.0.1:24224 without authentication. Its /register endpoint accepts arbitrary JSON and, when open_viewer is true, opens the supplied dashboard_url in the user's default browser.

    Fix: Authenticate requests to the tray manager using the shared auth_secret and validate that dashboard_url points to a local http://127.0.0.1 or http://localhost dashboard endpoint.

  10. MediumUnrestricted Python REPL Interface Enabled by Default in Configuration Template

    src/serena/resources/serena_config.template.yml:22

    The global configuration template defaults agent_interface to REPL. When initialized with this default, Serena exposes the serena_repl tool, which executes arbitrary Python code in the Serena server process. As noted in the documentation, REPL code execution cannot be constrained by read_only: true, planning mode, excluded_tools, or excluded_apis, meaning a prompt-injected agent can execute arbitrary system commands or modify any file on the user's machine even in read-only or planning configurations.

    Fix: Default agent_interface to tools in serena_config.template.yml so that tool exclusions, client-side per-tool approval prompts, and read_only project settings are effective unless the user explicitly opts into REPL.

  11. MediumArbitrary Shell Command Execution and Unconfined Working Directory in ShellApi

    src/serena/repl/api/shell_api.py:87

    The execute_shell_command method runs arbitrary shell commands supplied by the AI agent and permits any absolute path on the host filesystem as cwd. When enabled (such as in the default desktop-app context or via the REPL interface), a prompt injection in untrusted code, issues, or documents read by the agent can execute arbitrary commands on the user's machine.

    Fix: Disable shell execution by default across contexts unless explicitly enabled by the user, and validate that cwd resolves within the active project root.

  12. MediumMissing Zip-Slip Path Traversal Check in SafeZipExtractor

    src/solidlsp/util/zip.py:92

    SafeZipExtractor._extract_member joins self.extract_dir / member.filename and writes directly to the resulting path with open(final_path, "wb") without verifying that the resolved destination stays inside self.extract_dir. It also applies os.chmod(final_path, unix_mode) without masking privileged bits (such as SUID/SGID). If a custom or compromised archive contains ../ path segments, files can be overwritten outside the target extraction directory.

    Fix: Resolve self.extract_dir and target_path using .resolve() and verify target_path.resolve().is_relative_to(self.extract_dir.resolve()) before creating parent directories or extracting the member, and mask unix_mode with 0o777.

  13. MediumUnsafe Pickle Deserialization Fallback in load_cache

    src/solidlsp/util/cache.py:15

    load_cache accepts an optional loader: SafePickleLoader | None = None parameter and falls back to load_pickle(path) (which uses standard pickle.load) when loader is None. Because symbol caches are stored inside project directories (.serena/cache/), any call to load_cache that omits loader can execute arbitrary code when opening an untrusted project containing a crafted .pkl file.

    Fix: Default loader to SafePickleLoader() instead of falling back to load_pickle(path).

  14. MediumWeb Dashboard Bound to 0.0.0.0 in Docker Image

    Dockerfile:67

    The Dockerfile configures web_dashboard_listen_address: 0.0.0.0. Because DOCKER.md instructs users to run the container with --network host, the unauthenticated web dashboard (which exposes endpoints to overwrite global Serena configuration and shut down the server) is exposed on all network interfaces of the host machine. The Dockerfile also pipes unpinned remote install scripts (https://astral.sh/uv/install.sh and https://sh.rustup.rs) directly to sh.

    Fix: Keep web_dashboard_listen_address set to 127.0.0.1 by default, avoid recommending --network host when 0.0.0.0 is used, and pin installer versions and checksums in the Dockerfile.

  15. LowUsage telemetry is sent to an external server on startup by default

    src/serena/agent.py:804

    Every time SerenaAgent starts, _send_usage_info sends the operating system, Serena version (including the short git commit hash if running from a git checkout), active backend, context name, and interface mode to https://oraios-software.de/serena_usage.php unless SERENA_USAGE_REPORTING=false is set in the environment.

    Fix: Make usage reporting opt-in or clearly document theSERENA_USAGE_REPORTING=false setting in the configuration file and README.

  16. LowUnquoted Executable Path in Shell Command String for Elixir Language Server

    src/solidlsp/language_servers/elixir_tools/elixir_tools.py:242

    ElixirTools constructs ProcessLaunchInfo with a raw string f"{expert_executable_path} --stdio" rather than a list of arguments. Because ManagedSubprocessLauncher.launch runs commands with shell=True and only quotes list commands via oslex.join, spaces or shell metacharacters in expert_executable_path will be interpreted by the shell.

    Fix: Pass the command as a list: ProcessLaunchInfo(cmd=[expert_executable_path, "--stdio"], cwd=repository_root_path).

  17. LowDocker configuration binds web dashboard to all network interfaces

    Dockerfile:67

    When building the development Docker image, the configuration is modified to bind the web dashboard to 0.0.0.0 instead of localhost. If container ports are published to the host without restriction, other machines on the network could access the dashboard without authentication.

    Fix: Keep web_dashboard_listen_address set to 127.0.0.1 by default, or document that port mappings should be bound to 127.0.0.1 on the host.

  18. LowUnpinned remote install scripts piped to shell in Dockerfile

    Dockerfile:42

    The Dockerfile downloads and executes the latest uv and rustup installer scripts directly via curl piped to sh without version pinning or checksum verification. A compromise of those upstream URLs during image build would execute arbitrary code in the container.

    Fix: Pin installer versions and verify SHA-256 checksums before executing downloaded installation scripts, or install from pinned package manager releases.