]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0496: [security]: Code Injection in cucumber filetype plugin v9.2.0496
authorChristian Brabandt <cb@256bit.org>
Sun, 17 May 2026 19:39:24 +0000 (19:39 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 17 May 2026 19:39:24 +0000 (19:39 +0000)
Problem:  [security]: Code Injection in cucumber filetype plugin
          (Christopher Lusk)
Solution: Use rubys Regexp.new() with the untrusted pattern

Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-4473-94jm-w5x9

Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/ftplugin/cucumber.vim
src/testdir/test_filetype.vim
src/version.c

index f4848d1c60d5a3f6403b8c0b3b71440b117926b7..9723898d15bc9d94948b5fc385d4987ff97af829 100644 (file)
@@ -2,6 +2,8 @@
 " Language:    Cucumber
 " Maintainer:  Tim Pope <vimNOSPAM@tpope.org>
 " Last Change: 2016 Aug 29
+" 2026 May 26 by Vim Project: prevent Code Injection
+" https://github.com/vim/vim/security/advisories/GHSA-4473-94jm-w5x9
 
 " Only do this when not done yet for this buffer
 if (exists("b:did_ftplugin"))
@@ -96,7 +98,8 @@ function! s:stepmatch(receiver,target)
   catch
   endtry
   if has("ruby") && pattern !~ '\\\@<!#{'
-    ruby VIM.command("return #{if (begin; Kernel.eval('/'+VIM.evaluate('pattern')+'/'); rescue SyntaxError; end) === VIM.evaluate('a:target') then 1 else 0 end}")
+    " Use Regexp.new, so the pattern stays untrusted data and cannot inject Ruby
+    ruby VIM.command("return #{if (begin; Regexp.new(VIM.evaluate('pattern')); rescue RegexpError; end) === VIM.evaluate('a:target') then 1 else 0 end}")
   else
     return 0
   endif
index 9aa99a97d70f47abc18449a6fa783771f7e03bc3..70ac4010612af26137e6eff98f52b7e30d9fcabf 100644 (file)
@@ -3477,4 +3477,34 @@ func Test_app_file()
   filetype off
 endfunc
 
+func Test_cucumber_code_injection()
+  CheckFeature ruby
+  filetype plugin on
+
+  call mkdir('Xcucu/features/step_definitions', 'pR')
+  call writefile([
+        \ 'Feature: demo',
+        \ '  Scenario: trigger',
+        \ '    Given xyzzy',
+        \ ], 'Xcucu/features/test.feature')
+  let marker = getcwd() . '/Xcucu/MARKER'
+  " Malicious step: terminates the regex literal, injects Ruby system(),
+  " comments the trailing slash.  With the fix, the pattern is passed to
+  " Regexp.new() instead of Kernel.eval() and the payload is inert.
+  call writefile([
+        \ 'Given /xyzzy/; system("touch ' . marker . '"); #/ do',
+        \ 'end',
+        \ ], 'Xcucu/features/step_definitions/poc.rb')
+
+  new Xcucu/features/test.feature
+  call assert_equal('cucumber', &filetype)
+  call cursor(3, 1)
+  " Triggers s:jump -> s:steps -> s:stepmatch on every discovered step,
+  " including the malicious one.  Suppress preview and error messages.
+  silent! normal [d
+  call assert_false(filereadable(marker), 'Ruby injection executed')
+  bwipe!
+  filetype plugin off
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 0a9e8190635f2f30a98beaa6e902c34896fe9c2a..2e76cbe480fc1359f5e84b527ac17f4acb226bb1 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    496,
 /**/
     495,
 /**/