Skip to content

.devo/config.toml can execute SessionStart shell hooks without workspace trust or approval #159

Description

@glmgbj233

Summary

A repository can include <workspace>/.devo/config.toml containing a SessionStart command hook. When a user creates or resumes a Devo session whose cwd is that workspace, Devo loads the project configuration, constructs a hook runner from it, and executes the configured shell command without a workspace-trust prompt, hook confirmation, or tool approval.

Reproduction

Create a repository with this file:

# .devo/config.toml
[[hooks.SessionStart]]

[[hooks.SessionStart.hooks]]
type = "command"
command = "printf DEVO_WORKSPACE_HOOK_POC > .devo-workspace-hook-poc"
shell = "bash"

Then use that repository as the cwd for a new Devo session, or resume a Devo session whose cwd is that repository.

Expected secure behavior: Devo should show a project-executable-config trust prompt and avoid running the hook until the user approves it.

Actual behavior: the SessionStart hook executes and creates .devo-workspace-hook-poc without any prompt or tool approval.

Impact

An attacker who can convince a user to open a repository as a Devo session can run arbitrary shell commands with the privileges and environment of the Devo process. This can affect files accessible to that user, inherited credentials, and reachable network resources.

Root Cause

FileSystemAppConfigLoader merges <workspace>/.devo/config.toml over user configuration without source-specific restrictions:

  • crates/config/src/app.rs, FileSystemAppConfigLoader::load()

The session startup path loads this effective configuration for the requested workspace:

  • crates/server/src/runtime/handlers/session.rs, start_session_with_registry()
  • crates/server/src/session_context.rs, SessionRuntimeContext::load_for_workspace()

After creating the session, the runtime unconditionally fires SessionStart:

  • crates/server/src/runtime/handlers/session.rs, HookEvent::SessionStart

HookRunner dispatches command hooks, and the command implementation invokes the user's shell with -lc on non-Windows systems (or cmd /C on Windows):

  • crates/core/src/hooks.rs, HookRunner::run()
  • crates/core/src/hooks/command.rs, build_command() and bash_command()

The configuration parser test documents the accepted [[hooks.SessionStart]] shape by analogy with the tested PreToolUse command-hook structure:

  • crates/config/src/tests.rs, loader_reads_hook_command_config()

Suggested Fix

Treat hooks in workspace configuration as untrusted executable configuration. Do not merge them into the active hook runner until the workspace is explicitly trusted. Bind approval to the hook configuration and re-prompt when its command or referenced executable changes. At minimum, skip workspace-defined command hooks by default while retaining user-scoped hooks.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions