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.
Summary
A repository can include
<workspace>/.devo/config.tomlcontaining aSessionStartcommand 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:
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
SessionStarthook executes and creates.devo-workspace-hook-pocwithout 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
FileSystemAppConfigLoadermerges<workspace>/.devo/config.tomlover 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::SessionStartHookRunnerdispatches command hooks, and the command implementation invokes the user's shell with-lcon non-Windows systems (orcmd /Con Windows):crates/core/src/hooks.rs,HookRunner::run()crates/core/src/hooks/command.rs,build_command()andbash_command()The configuration parser test documents the accepted
[[hooks.SessionStart]]shape by analogy with the testedPreToolUsecommand-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.