Class: Clacky::Tools::Security::Replacer

Inherits:
Object
  • Object
show all
Defined in:
lib/clacky/tools/security.rb

Overview

Internal class that owns per-project state (trash dir, log dir, …). Extracted almost verbatim from the old SafeShell::CommandSafetyReplacer.

Constant Summary collapse

SECRET_WRITE_PATTERNS =

Block writes that would clobber credentials / secrets. These are the only paths truly dangerous to write to by accident:

- ~/.ssh/*          (SSH private keys)
- ~/.aws/*          (AWS credentials)
- any *.env file    (API keys, DB URLs, etc.)

Paths in / outside the project root, Gemfile, README, package.json, etc. are all allowed — the agent is expected to edit them normally.

[
  %r{(?:\A|/)\.ssh/},
  %r{(?:\A|/)\.aws/},
  /(?:\A|\/)\.env(?:\.|\z)/,
  /\.env\z/
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(project_root) ⇒ Replacer

Returns a new instance of Replacer.



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/clacky/tools/security.rb', line 95

def initialize(project_root)
  @project_root = File.expand_path(project_root)

  trash_directory = Clacky::TrashDirectory.new(@project_root)
  @trash_dir  = trash_directory.trash_dir
  @backup_dir = trash_directory.backup_dir

  @project_hash = trash_directory.generate_project_hash(@project_root)
  @safety_log_dir = File.join(Dir.home, ".clacky", "safety_logs", @project_hash)
  FileUtils.mkdir_p(@safety_log_dir) unless Dir.exist?(@safety_log_dir)
  @safety_log_file = File.join(@safety_log_dir, "safety.log")
end

Instance Method Details

#make_command_safe(command) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/clacky/tools/security.rb', line 108

def make_command_safe(command)
  command = command.strip

  # Use a UTF-8-scrubbed copy ONLY for regex checks.  The original
  # bytes are returned unchanged so the shell receives exact paths
  # (e.g. GBK-encoded Chinese filenames in zip archives).
  @safe_check_command = Clacky::Utils::Encoding.safe_check(command)

  case @safe_check_command
  when /pkill.*clacky|killall.*clacky|kill\s+.*\bclacky\b/i
    raise SecurityError, "Killing the clacky server process is not allowed. To restart, use: kill -USR1 $CLACKY_MASTER_PID"
  when /clacky\s+server/
    raise SecurityError, "Managing the clacky server from within a session is not allowed. To restart, use: kill -USR1 $CLACKY_MASTER_PID"
  when /^rm\s+/
    replace_rm_command(command)
  when /^chmod\s+x/
    replace_chmod_command(command)
  when /^curl.*\|\s*(sh|bash)/
    replace_curl_pipe_command(command)
  when /^sudo\s+/
    block_sudo_command(command)
  when />\s*\/dev\/null\s*$/
    allow_dev_null_redirect(command)
  when /^(mv|cp|mkdir|touch|echo)\s+/
    validate_and_allow(command)
  else
    validate_general_command(@safe_check_command)
    command
  end
end