From df0792b6079cf1b527557ef20971df58c3c3ba6f Mon Sep 17 00:00:00 2001 From: RHEL Packaging Agent Date: Wed, 5 Aug 2026 11:04:32 +0000 Subject: [PATCH] Fix CVE-2026-52858: possible code execution with python3complete Backport two upstream commits to fix CVE-2026-52858, which allowed possible code execution via import/from statements in python3complete and pythoncomplete omni-completion plugins. Patch 9.2.0561 disables execution of import/from statements by default and adds a g:pythoncomplete_allow_import opt-in variable. Patch 9.2.0568 fixes a bug where the vim module was not imported in evalsource() scope, causing the opt-in variable to silently have no effect. CVE: CVE-2026-52858 Upstream patches: - https://github.com/vim/vim/commit/4b850457e12e1a678dd209f2868154f7553cbf8d.patch - https://github.com/vim/vim/commit/868ad62cb8bf8038322eab2badd31bd98b02b9df.patch Resolves: RHEL-186651 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir --- ...ecurity-possible-code-execution-with.patch | 160 ++++++++++++++++++ ...ythoncomplete-g-pythoncomplete_allow.patch | 84 +++++++++ vim.spec | 14 +- 3 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 0001-patch-9.2.0561-security-possible-code-execution-with.patch create mode 100644 0001-patch-9.2.0568-pythoncomplete-g-pythoncomplete_allow.patch diff --git a/0001-patch-9.2.0561-security-possible-code-execution-with.patch b/0001-patch-9.2.0561-security-possible-code-execution-with.patch new file mode 100644 index 00000000..569487e6 --- /dev/null +++ b/0001-patch-9.2.0561-security-possible-code-execution-with.patch @@ -0,0 +1,160 @@ +From 3c2146726eba3d6e01138dd5a0af974cff902a6d Mon Sep 17 00:00:00 2001 +From: Christian Brabandt +Date: Fri, 29 May 2026 19:05:53 +0000 +Subject: [PATCH] patch 9.2.0561: [security]: possible code execution with + python3complete + +Problem: [security]: possible code execution with python3complete +Solution: Disable execution of import/from statements + +Github Security Advisory: +https://github.com/vim/vim/security/advisories/GHSA-52mc-rq6p-rc7c + +Signed-off-by: Christian Brabandt +--- + runtime/autoload/README.txt | 1 + + runtime/autoload/python3complete.vim | 17 ++++++++++++++--- + runtime/autoload/pythoncomplete.vim | 17 ++++++++++++++--- + runtime/doc/filetype.txt | 15 ++++++++++++++- + 4 files changed, 43 insertions(+), 7 deletions(-) + +diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt +index 3b18d3dde..b22581963 100644 +--- a/runtime/autoload/README.txt ++++ b/runtime/autoload/README.txt +@@ -17,6 +17,7 @@ htmlcomplete.vim HTML + javascriptcomplete.vim Javascript + phpcomplete.vim PHP + pythoncomplete.vim Python ++python3complete.vim Python + rubycomplete.vim Ruby + syntaxcomplete.vim from syntax highlighting + xmlcomplete.vim XML (uses files in the xml directory) +diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim +index ea0a33136..aba341229 100644 +--- a/runtime/autoload/python3complete.vim ++++ b/runtime/autoload/python3complete.vim +@@ -14,6 +14,10 @@ + " i.e. "import url" + " Continue parsing on invalid line?? + " ++" v 0.10 by Vim project ++" * disables importing local modules, unless the global Vim variable ++" g:pythoncomplete_allow_import is set to non-zero ++" + " v 0.9 + " * Fixed docstring parsing for classes and functions + " * Fixed parsing of *args and **kwargs type arguments +@@ -132,11 +136,20 @@ class Completer(object): + + def evalsource(self,text,line=0): + sc = self.parser.parse(text,line) ++ try: allow_imports = int( ++ vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) ++ except Exception: ++ allow_imports = 0 + src = sc.get_code() + dbg("source: %s" % src) + try: exec(src,self.compldict) + except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) + for l in sc.locals: ++ # Executing import/from statements harvested from the buffer runs ++ # arbitrary package code; only do so when the user opted in. ++ if not allow_imports and (l.startswith('import') ++ or l.startswith('from ')): ++ continue + try: exec(l,self.compldict) + except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) + +@@ -300,13 +313,11 @@ class Scope(object): + def get_code(self): + str = "" + if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' +- for l in self.locals: +- if l.startswith('import'): str += l+'\n' + str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' + for sub in self.subscopes: + str += sub.get_code() + for l in self.locals: +- if not l.startswith('import'): str += l+'\n' ++ if not l.startswith('import') and not l.startswith('from '): str += l+'\n' + + return str + +diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim +index aa28bb721..10147767e 100644 +--- a/runtime/autoload/pythoncomplete.vim ++++ b/runtime/autoload/pythoncomplete.vim +@@ -12,6 +12,10 @@ + " i.e. "import url" + " Continue parsing on invalid line?? + " ++" v 0.10 by Vim project ++" * disables importing local modules, unless the global Vim variable ++" g:pythoncomplete_allow_import is set to non-zero ++" + " v 0.9 + " * Fixed docstring parsing for classes and functions + " * Fixed parsing of *args and **kwargs type arguments +@@ -146,11 +150,20 @@ class Completer(object): + + def evalsource(self,text,line=0): + sc = self.parser.parse(text,line) ++ try: allow_imports = int( ++ vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) ++ except Exception: ++ allow_imports = 0 + src = sc.get_code() + dbg("source: %s" % src) + try: exec(src) in self.compldict + except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) + for l in sc.locals: ++ # Executing import/from statements harvested from the buffer runs ++ # arbitrary package code; only do so when the user opted in. ++ if not allow_imports and (l.startswith('import') ++ or l.startswith('from ')): ++ continue + try: exec(l) in self.compldict + except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) + +@@ -315,13 +328,11 @@ class Scope(object): + def get_code(self): + str = "" + if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' +- for l in self.locals: +- if l.startswith('import'): str += l+'\n' + str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' + for sub in self.subscopes: + str += sub.get_code() + for l in self.locals: +- if not l.startswith('import'): str += l+'\n' ++ if not l.startswith('import') and not l.startswith('from '): str += l+'\n' + + return str + +diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt +index 4876e3d75..4b57e839f 100644 +--- a/runtime/doc/filetype.txt ++++ b/runtime/doc/filetype.txt +@@ -740,7 +740,20 @@ By default the following options are set, in accordance with PEP8: > + To disable this behavior, set the following variable in your vimrc: > + + let g:python_recommended_style = 0 +- ++< ++Python omni-completion |compl-omni| is provided by python3complete.vim (or ++pythoncomplete.vim) for Vim builds with the |+python|/|+python3| interpreter. ++By default it does not inspect the import / from statements found in the ++buffer. This means completion of names defined in the buffer itself (classes, ++functions, variables) works, but completion of members of imported modules is ++not offered. ++ ++To enable completion of imported module members, set: > ++ let g:pythoncomplete_allow_import = 1 ++< ++WARNING: enabling this causes omni-completion to execute the import statements ++found in the buffer through Python's import machinery, which runs the imported ++modules' top-level code. Only enable this for code you trust. + + QF QUICKFIX *qf.vim* *ft-qf-plugin* + diff --git a/0001-patch-9.2.0568-pythoncomplete-g-pythoncomplete_allow.patch b/0001-patch-9.2.0568-pythoncomplete-g-pythoncomplete_allow.patch new file mode 100644 index 00000000..e1a3b233 --- /dev/null +++ b/0001-patch-9.2.0568-pythoncomplete-g-pythoncomplete_allow.patch @@ -0,0 +1,84 @@ +From 9fbc96ad3e9d286ec5edf8c7700da8554dd39afb Mon Sep 17 00:00:00 2001 +From: thinca +Date: Sun, 31 May 2026 12:33:07 +0000 +Subject: [PATCH] patch 9.2.0568: pythoncomplete: g:pythoncomplete_allow_import + had no effect + +Problem: The security patch 9.2.0561 added a vim.eval() call inside + Completer.evalsource() to honor g:pythoncomplete_allow_import. + But the 'vim' module is only imported inside the outer + vimcomplete() / vimpy3complete() function, not at the script's + top level, so referring to it from a Completer method raises + NameError. The surrounding bare 'except' silently swallows + the error and leaves allow_imports at 0, meaning the opt-in + never takes effect -- 'import os' (and any other + buffer-level import) is always skipped, no candidates are + produced for 'os.<...>' and + Test_popup_and_preview_autocommand() fails on the Windows + CI matrix (Linux skips the test because Python 2 is absent). +Solution: Re-import 'vim' at the top of evalsource() in both + pythoncomplete.vim and python3complete.vim so the eval reads + the global, and set g:pythoncomplete_allow_import = 1 in the + test (it is the opt-in intended for callers that trust the + buffer contents) (thinca). + +closes: #20386 + +Signed-off-by: thinca +Signed-off-by: Christian Brabandt +--- + runtime/autoload/python3complete.vim | 3 +++ + runtime/autoload/pythoncomplete.vim | 3 +++ + src/testdir/test_popup.vim | 4 ++++ + 3 files changed, 10 insertions(+) + +diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim +index aba341229..1c432f3c8 100644 +--- a/runtime/autoload/python3complete.vim ++++ b/runtime/autoload/python3complete.vim +@@ -135,6 +135,9 @@ class Completer(object): + self.parser = PyParser() + + def evalsource(self,text,line=0): ++ # vim is imported locally in vimpy3complete(); re-import here so the ++ # vim.eval() below works (otherwise NameError, silently caught). ++ import vim + sc = self.parser.parse(text,line) + try: allow_imports = int( + vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) +diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim +index 10147767e..b4340f7ae 100644 +--- a/runtime/autoload/pythoncomplete.vim ++++ b/runtime/autoload/pythoncomplete.vim +@@ -149,6 +149,9 @@ class Completer(object): + self.parser = PyParser() + + def evalsource(self,text,line=0): ++ # vim is imported locally in vimcomplete(); re-import here so the ++ # vim.eval() below works (otherwise NameError, silently caught). ++ import vim + sc = self.parser.parse(text,line) + try: allow_imports = int( + vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) +diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim +index 879d1fa3e..be27097a1 100644 +--- a/src/testdir/test_popup.vim ++++ b/src/testdir/test_popup.vim +@@ -724,6 +724,9 @@ func Test_popup_and_preview_autocommand() + au! + au BufAdd * nested tab sball + augroup END ++ " Let pythoncomplete follow the buffer's 'import os' (off by default ++ " since v9.2.0561) so 'os.' can be completed. ++ let g:pythoncomplete_allow_import = 1 + set omnifunc=pythoncomplete#Complete + call setline(1, 'import os') + " make the line long +@@ -746,6 +749,7 @@ func Test_popup_and_preview_autocommand() + augroup END + augroup! MyBufAdd + bw! ++ unlet g:pythoncomplete_allow_import + endfunc + + func Test_popup_and_previewwindow_dump() diff --git a/vim.spec b/vim.spec index dae49610..1cf48738 100644 --- a/vim.spec +++ b/vim.spec @@ -51,7 +51,7 @@ Summary: The VIM editor URL: http://www.vim.org/ Name: vim Version: %{baseversion}.%{patchlevel} -Release: 20%{?dist} +Release: 21%{?dist} Epoch: 2 # swift.vim contains Apache 2.0 with runtime library exception: # which is taken as Apache-2.0 WITH Swift-exception - reported to legal as https://gitlab.com/fedora/legal/fedora-license-data/-/issues/188 @@ -181,6 +181,12 @@ Patch3029: 0001-patch-9.2.0653-security-out-of-bounds-write-in-tree_.patch # https://redhat.atlassian.net/browse/RHEL-186663 # https://github.com/vim/vim/commit/f08ab2f4d7d2947c8dd6c179ae08ee6146a2694b Patch3030: 0001-patch-9.2.0495-security-runtime-netrw-code-injectio.patch +# RHEL-186651 CVE-2026-52858 possible code execution with python3complete +# https://redhat.atlassian.net/browse/RHEL-186651 +# https://github.com/vim/vim/commit/4b850457e12e1a678dd209f2868154f7553cbf8d +# https://github.com/vim/vim/commit/868ad62cb8bf8038322eab2badd31bd98b02b9df +Patch3031: 0001-patch-9.2.0561-security-possible-code-execution-with.patch +Patch3032: 0001-patch-9.2.0568-pythoncomplete-g-pythoncomplete_allow.patch # uses autoconf in spec file @@ -529,6 +535,8 @@ perl -pi -e "s,bin/nawk,bin/awk,g" runtime/tools/mve.awk %patch -P 3028 -p1 -b .runtime-ccomplete %patch -P 3029 -p1 -b .tree-count-words-oob %patch -P 3030 -p1 -b .netrw-hist-inject +%patch -P 3031 -p1 -b .python3complete-import +%patch -P 3032 -p1 -b .pythoncomplete-allow-import %build cd src @@ -1159,6 +1167,10 @@ touch %{buildroot}/%{_datadir}/%{name}/vimfiles/doc/tags %changelog +* Wed Aug 05 2026 RHEL Packaging Agent - 2:9.1.083-21 +- RHEL-186651 CVE-2026-52858 vim: possible code execution with + python3complete + * Wed Aug 05 2026 RHEL Packaging Agent - 2:9.1.083-20 - RHEL-186663 CVE-2026-47162 vim: code injection via NetrwBookHistSave()