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 `rm` → `mv <file> <trash>`   so the file is recoverable.
3. Rewrite `curl ... | bash` → save     script to a file for manual
                                        review instead of exec.
4. 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.

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

Classes: Blocked, Replacer

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

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.)

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/clacky/tools/security.rb', line 77

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.

Parameters:

  • command (String)

    command to check

  • project_root (String) (defaults to: Dir.pwd)

    path treated as the allowed root for writes

Returns:

  • (String)

    safe command to execute



70
71
72
# File 'lib/clacky/tools/security.rb', line 70

def make_safe(command, project_root: Dir.pwd)
  Replacer.new(project_root).make_command_safe(command)
end