VYPR
High severityNVD Advisory· Published Jun 11, 2026· Updated Jun 11, 2026

CVE-2026-52858

CVE-2026-52858

Description

Vim's Python omni-completion executes import statements from the buffer, allowing code execution via a crafted .py file; fixed in 9.2.0561.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Vim's Python omni-completion executes import statements from the buffer, allowing code execution via a crafted .py file; fixed in 9.2.0561.

Vulnerability

Vim versions prior to 9.2.0561 with the +python3 or +python interpreter enabled are vulnerable. The Python omni-completion scripts (python3complete.vim and pythoncomplete.vim) parse the current buffer and regenerate Python source code that includes import X and from X import Y statements. These are executed via Python's exec() function with the buffer's working directory on sys.path, allowing arbitrary package code to run. The vulnerability is triggered when a user opens a .py file and invokes omni-completion (CTRL-X CTRL-O) in insert mode [1][2].

Exploitation

An attacker crafts a .py file containing import evil_pkg and provides a sibling package evil_pkg/ with an __init__.py containing malicious code. When the victim opens the file in Vim and presses CTRL-X CTRL-O, the completer executes the import statement, loading and running the attacker's package code [2]. No special privileges are required beyond the ability to supply the malicious file to the victim.

Impact

Successful exploitation results in arbitrary code execution with the victim's privileges. The attacker gains access to the user's credentials, files, and network capabilities. Realistic delivery includes reviewing third-party code contributions or opening malicious files from untrusted sources [2].

Mitigation

Upgrade to Vim version 9.2.0561 or later, which disables the execution of import/from statements by default and introduces the g:pythoncomplete_allow_import option to opt in [1][3]. Users who cannot upgrade may set let g:pythoncomplete_allow_import = 0 in their vimrc to block the vulnerable behavior. The Vim project has patched the issue and released the fix [2][3].

AI Insight generated on Jun 11, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Vim/Vimreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: <9.2.0561

Patches

1
4b850457e12e

patch 9.2.0561: [security]: possible code execution with python3complete

https://github.com/vim/vimChristian BrabandtMay 29, 2026via nvd-ref
5 files changed · +45 7
  • runtime/autoload/python3complete.vim+14 3 modified
    @@ -14,6 +14,10 @@
     "   i.e. "import url<c-x,c-o>"
     " 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
     
    
  • runtime/autoload/pythoncomplete.vim+14 3 modified
    @@ -12,6 +12,10 @@
     "   i.e. "import url<c-x,c-o>"
     " 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
     
    
  • runtime/autoload/README.txt+1 0 modified
    @@ -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)
    
  • runtime/doc/filetype.txt+14 1 modified
    @@ -976,7 +976,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*
     
    
  • src/version.c+2 0 modified
    @@ -729,6 +729,8 @@ static char *(features[]) =
     
     static int included_patches[] =
     {   /* Add new patch number below this line */
    +/**/
    +    561,
     /**/
         560,
     /**/
    

Vulnerability mechanics

Root cause

"The Python omni-completion scripts re-emit and execute `import` and `from` statements from the buffer through Python's import machinery without user consent, allowing arbitrary package code to run."

Attack vector

An attacker crafts a malicious `.py` file containing `import evil_pkg` and places a sibling `evil_pkg/__init__.py` with arbitrary Python code in the same directory. When a victim opens the file in Vim (with `+python3` or `+python` enabled) and invokes omni-completion via `CTRL-X CTRL-O` in insert mode, the completer parses the buffer, regenerates the `import` statement, and executes it through Python's import machinery. Because the buffer's working directory is on `sys.path`, the attacker's package top-level code runs with the victim's privileges [ref_id=2].

Affected code

The vulnerability resides in `runtime/autoload/python3complete.vim` and `runtime/autoload/pythoncomplete.vim`. The `Completer.evalsource()` method and `Scope.get_code()` method both re-emit `import` and `from` statements harvested from the buffer, which are then passed to Python's `exec()` built-in. The patch also updates `runtime/doc/filetype.txt` to document the new opt-in variable `g:pythoncomplete_allow_import`.

What the fix does

The patch introduces a new global Vim variable `g:pythoncomplete_allow_import` that defaults to `0`. In `evalsource()`, the completer now checks this variable before executing any `import` or `from` statement harvested from the buffer; if the variable is falsy, those statements are skipped. Additionally, `get_code()` no longer includes `import` or `from` lines in the regenerated source string passed to `exec()`. This prevents untrusted import statements from being executed unless the user explicitly opts in [patch_id=5620674].

Preconditions

  • configVim must be built with the +python3 or +python interpreter feature enabled
  • inputThe user must open a .py file in a directory that contains a sibling Python package (e.g., a directory with an __init__.py)
  • inputThe user must invoke omni-completion (CTRL-X CTRL-O) while editing the buffer

Generated on Jun 11, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.