Maestro plugins execute arbitrary shell commands by design. When a plugin is configured in maestro.toml, its run field is passed directly to sh -c.
Treat maestro.toml as an executable runbook. Plugin run fields in that file are executed as shell commands the next time the matching hook runs, including hooks such as session_completed. The execution path is src/plugins/runner.rs, where PluginRunner creates Command::new("sh") and passes ["-c", &plugin.run].
In shared or collaborative repositories, review maestro.toml changes from pulls, rebases, or PR branches before running maestro locally. A practical check is:
git diff HEAD -- maestro.tomlIf the branch was just pulled or checked out, inspect any added or changed plugin run commands before invoking maestro.
Plugin commands receive environment variables from HookContext. To prevent override of security-sensitive system variables (PATH, LD_PRELOAD, DYLD_INSERT_LIBRARIES, etc.), only variables with the MAESTRO_ prefix are injected into the plugin subprocess.
- Variable names must match
^[A-Z][A-Z0-9_]*$ - Variable names must start with
MAESTRO_ - Variables that fail validation are silently skipped with a warning log
All user-controlled inputs are validated before use:
- Branch names: validated against
^[a-zA-Z0-9/_.\-]+$,..sequences rejected - Worktree slugs: validated against
^[a-zA-Z0-9_\-]+$(no path separators) - GitHub CLI arguments: must not start with
-or contain null bytes
The auto-updater verifies SHA-256 checksums from a sha256sums.txt file published alongside release binaries. If verification fails, the update is aborted and the existing binary is not modified.
The state store uses advisory file locks to prevent concurrent read/write races between multiple maestro processes.
If you find a security vulnerability, please open a GitHub issue with the security label.