Module: RubyLLM::Toolbox::Tools::GitHelpers
- Included in:
- ApplyPatch, GitAdd, GitBlame, GitBranch, GitCheckout, GitCommit, GitDiff, GitGrep, GitLog, GitShow, GitStatus
- Defined in:
- lib/ruby_llm/toolbox/tools/git_helpers.rb
Overview
Shared behavior for the git tools. Mixed into each so they all run git the same hardened way.
Hardening matters here because git executes commands configured *by the repository* in several places, which turns “read-only” commands into RCE when operating on an untrusted checkout:
- core.fsmonitor runs a command during `git status`
- diff.external / textconv run commands during `git diff`
The read tools are on by default, so the runner neutralizes those (‘-c core.fsmonitor=`, and diff adds –no-ext-diff –no-textconv). The pager and credential prompts are also disabled so nothing can hang.
Constant Summary collapse
- GIT_ENV =
{ "GIT_PAGER" => "cat", "GIT_TERMINAL_PROMPT" => "0" }.freeze
- REF_RE =
A ref/branch the model supplies: no leading dash (option injection), and only the characters git refs legitimately use.
%r{\A[A-Za-z0-9][\w./\-]*\z}
Instance Method Summary collapse
-
#git_result(out, err, status) ⇒ Object
Maps a finished git run onto the toolbox return contract.
-
#repo_relative(path) ⇒ Object
Resolve a path param to a repo-relative path, rejecting jail escapes.
- #run_git(*args, stdin: nil) ⇒ Object
- #valid_ref?(ref) ⇒ Boolean
Instance Method Details
#git_result(out, err, status) ⇒ Object
Maps a finished git run onto the toolbox return contract.
33 34 35 36 37 38 39 40 |
# File 'lib/ruby_llm/toolbox/tools/git_helpers.rb', line 33 def git_result(out, err, status) return error("git timed out after #{config.command_timeout}s", code: :timeout) if status == :timeout return out if status.exitstatus&.zero? = (err.empty? ? out : err).strip code = .match?(/not a git repository/i) ? :not_a_repo : :git_error error("git failed: #{}", code: code) end |
#repo_relative(path) ⇒ Object
Resolve a path param to a repo-relative path, rejecting jail escapes. Returns nil for a blank path. Raises PathJail::Jailbreak on escape.
48 49 50 51 52 53 54 |
# File 'lib/ruby_llm/toolbox/tools/git_helpers.rb', line 48 def repo_relative(path) return nil if path.nil? || path.to_s.empty? jail = Safety::PathJail.new(config.fs_root) real = jail.resolve(path) Pathname.new(real).relative_path_from(Pathname.new(jail.root)).to_s end |
#run_git(*args, stdin: nil) ⇒ Object
27 28 29 30 |
# File 'lib/ruby_llm/toolbox/tools/git_helpers.rb', line 27 def run_git(*args, stdin: nil) argv = ["git", "-c", "core.fsmonitor=", "-C", config.fs_root, "--no-pager", *args] ProcessRunner.capture(argv, env: git_env, stdin: stdin, timeout: config.command_timeout, unsetenv_others: true) end |
#valid_ref?(ref) ⇒ Boolean
42 43 44 |
# File 'lib/ruby_llm/toolbox/tools/git_helpers.rb', line 42 def valid_ref?(ref) ref.to_s.match?(REF_RE) end |