From a9a47d49850cf2bc2f4aadb4b005f97ccb20403f Mon Sep 17 00:00:00 2001 From: ianballou Date: Tue, 1 Sep 2026 19:30:55 +0000 Subject: [PATCH 1/2] Fixes #39842 - Pass editor args as an array --- lib/hammer_cli/utils.rb | 4 +++- test/unit/utils_test.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/hammer_cli/utils.rb b/lib/hammer_cli/utils.rb index 0279b72ce..1524a7199 100644 --- a/lib/hammer_cli/utils.rb +++ b/lib/hammer_cli/utils.rb @@ -1,4 +1,5 @@ require 'highline' +require 'shellwords' require 'tempfile' class String @@ -91,7 +92,8 @@ def self.open_in_editor(content, content_type: '', tempdir: '/tmp', suffix: '.tm Tempfile.open([content_type, suffix], tempdir) do |f| f.write(content) f.rewind - system("#{ENV['EDITOR'] || 'vi'} #{f.path}") + editor = Shellwords.split(ENV['EDITOR'] || 'vi') + system(*editor, f.path) result = f.read end result diff --git a/test/unit/utils_test.rb b/test/unit/utils_test.rb index 4e2f06614..27ceb0645 100644 --- a/test/unit/utils_test.rb +++ b/test/unit/utils_test.rb @@ -195,4 +195,39 @@ class X assert_equal(arr, [:a, 1, 2, 3, :b, :c]) end end + + describe 'open_in_editor' do + it 'uses array-based system call to prevent shell injection' do + ENV.stubs(:[]).with('EDITOR').returns('vi') + HammerCLI.expects(:system).with('vi', anything).once.returns(true) + HammerCLI.open_in_editor('test content') + end + + it 'splits EDITOR with arguments into array form' do + ENV.stubs(:[]).with('EDITOR').returns('code --wait') + HammerCLI.expects(:system).with('code', '--wait', anything).once.returns(true) + HammerCLI.open_in_editor('test content') + end + + it 'falls back to vi when EDITOR is not set' do + ENV.stubs(:[]).with('EDITOR').returns(nil) + HammerCLI.expects(:system).with('vi', anything).once.returns(true) + HammerCLI.open_in_editor('test content') + end + + it 'does not pass shell metacharacters to a shell interpreter' do + ENV.stubs(:[]).with('EDITOR').returns('vi; echo INJECTED') + # Shellwords.split treats the semicolon as a literal character, + # producing ["vi;", "echo", "INJECTED"] - no shell is invoked + HammerCLI.expects(:system).with('vi;', 'echo', 'INJECTED', anything).once.returns(true) + HammerCLI.open_in_editor('test content') + end + + it 'returns original content when editor does not modify the file' do + ENV.stubs(:[]).with('EDITOR').returns('true') + HammerCLI.stubs(:system).returns(true) + result = HammerCLI.open_in_editor('original content') + assert_equal 'original content', result + end + end end From 3a93da31e88f0ec9c2c4f9913db49c4ad262b826 Mon Sep 17 00:00:00 2001 From: Odilon Sousa Date: Thu, 1 Oct 2026 10:05:44 -0300 Subject: [PATCH 2/2] Bump to 5.0.1 --- lib/hammer_cli/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hammer_cli/version.rb b/lib/hammer_cli/version.rb index 08a97dfc0..08b4d3c56 100644 --- a/lib/hammer_cli/version.rb +++ b/lib/hammer_cli/version.rb @@ -1,5 +1,5 @@ module HammerCLI def self.version - @version ||= Gem::Version.new "5.0.0" + @version ||= Gem::Version.new "5.0.1" end end