Module: Clacky::Tools::Security
- Defined in:
- lib/clacky/tools/security.rb
Overview
Pre-execution safety layer for shell-style commands.
Design principle: protect against the handful of commands that are irreversibly destructive or can compromise the host. Everything else is the user’s (or agent’s) business. Over-protection burns tool-call rounds and forces awkward work-arounds (e.g. the infamous “cp ~/.clacky /xxx ./ … Blocked: outside project directory” dance).
Responsibilities (applied to the ‘command` string BEFORE it is handed to a shell / PTY for execution):
1. Block hard-dangerous commands: sudo, pkill clacky, eval, exec,
`...`, $(...), | sh, | bash,
redirect to /etc /usr /bin.
2. Rewrite `curl ... | bash` → save script to a file for manual
review instead of exec.
3. Protect credential/secret files: .env, .ssh/, .aws/ — block
writes to these only. Other
"project" files (Gemfile,
README.md, package.json, …)
are NOT protected — editing
them is a normal dev task.
Note on ‘rm`:
`rm` is NOT rewritten here — it's intercepted at runtime by a shell
function installed in each PTY session (see Terminal::SAFE_RM_BASH
and Terminal#install_marker). This lets the shell's own parser
handle heredocs / multi-line / globs / variables correctly. A
static Ruby-side rewrite cannot — it would mis-parse heredoc
bodies and destroy legitimate commands.
Notes:
- `cp`, `mv`, `mkdir`, `touch`, `echo` are allowed to touch ANY path
(including outside the project root). The source of a `cp` is
read-only to the FS, and writing to arbitrary dirs is a legitimate
need (copying from ~/.clacky/skills/..., writing to /tmp, etc.).
Raises SecurityError on block. Returns a (possibly rewritten) command string on success.
This module was extracted from the former ‘SafeShell` tool. It is now shared by any tool that executes shell-style commands (currently: `terminal`).
Defined Under Namespace
Constant Summary collapse
- SAFE_READONLY_COMMANDS =
Read-only commands that are considered safe for auto-execution (permission mode :confirm_safes).
%w[ ls pwd cat less more head tail grep find which whereis whoami ps top htop df du git echo printf wc date file stat env printenv curl wget ].freeze
Class Method Summary collapse
-
.command_safe_for_auto_execution?(command) ⇒ Boolean
True iff the command is safe to auto-execute in :confirm_safes mode.
-
.make_safe(command, project_root: Dir.pwd) ⇒ String
Process ‘command` and return a (possibly rewritten) safe version.
Class Method Details
.command_safe_for_auto_execution?(command) ⇒ Boolean
True iff the command is safe to auto-execute in :confirm_safes mode. (Either a known read-only command, or one that Security.make_safe returns unchanged.)
84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/clacky/tools/security.rb', line 84 def command_safe_for_auto_execution?(command) return false unless command cmd_name = command.strip.split.first return true if SAFE_READONLY_COMMANDS.include?(cmd_name) begin safe = make_safe(command, project_root: Dir.pwd) command.strip == safe.strip rescue SecurityError false end end |
.make_safe(command, project_root: Dir.pwd) ⇒ String
Process ‘command` and return a (possibly rewritten) safe version. Raises SecurityError when the command cannot be made safe.
77 78 79 |
# File 'lib/clacky/tools/security.rb', line 77 def make_safe(command, project_root: Dir.pwd) Replacer.new(project_root).make_command_safe(command) end |