From d883881dfd546a59763be7bb253ac1165b6b5256 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 | 13 ++++++++++--- runtime/autoload/pythoncomplete.vim | 13 ++++++++++--- runtime/doc/filetype.txt | 15 ++++++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt index 3b18d3d..b225819 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 f0f3aad..5dcfb73 100644 --- a/runtime/autoload/python3complete.vim +++ b/runtime/autoload/python3complete.vim @@ -128,11 +128,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)) @@ -296,13 +305,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 ecc3664..6f72b11 100644 --- a/runtime/autoload/pythoncomplete.vim +++ b/runtime/autoload/pythoncomplete.vim @@ -145,11 +145,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)) @@ -314,13 +323,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 a5f2a1c..8ebe52a 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -641,7 +641,20 @@ By default the following options are set, in accordance with PEP8: > To disable this behaviour, 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. RPM SPEC *ft-spec-plugin* -- 2.52.0