Commit: patch 9.2.0496: [security]: Code Injection in cucumber filetype plugin

2 views
Skip to first unread message

Christian Brabandt

unread,
May 17, 2026, 3:45:13 PM (2 days ago) May 17
to vim...@googlegroups.com
patch 9.2.0496: [security]: Code Injection in cucumber filetype plugin

Commit: https://github.com/vim/vim/commit/a65a52d684bc58535ad28a4ae824d22e76399934
Author: Christian Brabandt <c...@256bit.org>
Date: Sun May 17 19:39:24 2026 +0000

patch 9.2.0496: [security]: Code Injection in cucumber filetype plugin

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 <c...@256bit.org>

diff --git a/runtime/ftplugin/cucumber.vim b/runtime/ftplugin/cucumber.vim
index f4848d1c6..9723898d1 100644
--- a/runtime/ftplugin/cucumber.vim
+++ b/runtime/ftplugin/cucumber.vim
@@ -2,6 +2,8 @@
" Language: Cucumber
" Maintainer: Tim Pope <vimN...@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
diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim
index 9aa99a97d..70ac40106 100644
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index 0a9e81906..2e76cbe48 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 496,
/**/
495,
/**/
Reply all
Reply to author
Forward
0 new messages