mirror of
https://github.com/neovim/neovim.git
synced 2026-07-17 06:31:16 +00:00
vim-patch:9.2.0597: [security]: possible code execution with python complete (#40117)
Problem: [security]: another possible code execution with python complete
(David Carliez)
Solution: Strip default expressions and annotations from generated
source for pythoncomplete and python3complete.
Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-65p9-mwwx-7468
c8c63673bc
Co-authored-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"pythoncomplete.vim - Omni Completion for python
|
||||
" Maintainer: <vacancy>
|
||||
" Previous Maintainer: Aaron Griffin <aaronmgriffin@gmail.com>
|
||||
" Version: 0.9
|
||||
" Last Updated: 2020 Oct 9
|
||||
" Version: 0.10
|
||||
" Last Updated: 2026 Jun 04
|
||||
"
|
||||
" Changes
|
||||
" TODO:
|
||||
@@ -15,6 +15,11 @@
|
||||
" v 0.10 by Vim project
|
||||
" * disables importing local modules, unless the global Vim variable
|
||||
" g:pythoncomplete_allow_import is set to non-zero
|
||||
" * strip default values and annotations from function parameter lists
|
||||
" before exec(), and whitelist class base lists to dotted names: the
|
||||
" previous code passed buffer-supplied expressions to exec() which
|
||||
" Python evaluates at definition time, allowing arbitrary code
|
||||
" execution via crafted def/class headers
|
||||
"
|
||||
" v 0.9
|
||||
" * Fixed docstring parsing for classes and functions
|
||||
@@ -95,6 +100,24 @@ function! s:DefPython()
|
||||
python << PYTHONEOF
|
||||
import sys, tokenize, cStringIO, types
|
||||
from token import NAME, DEDENT, NEWLINE, STRING
|
||||
import re
|
||||
|
||||
# Used by Class.get_code(): a base class expression is only included in the
|
||||
# code passed to exec() if it is a pure dotted name (e.g. "Base", "mod.Base",
|
||||
# "pkg.sub.Cls"). Anything containing calls, subscripts, "=", ":" or other
|
||||
# operators is dropped, since exec()-ing it would evaluate buffer-supplied
|
||||
# expressions. See the security note in the file header.
|
||||
_DOTTED_NAME_RE = re.compile(r'^[A-Za-z_]\w*(\s*\.\s*[A-Za-z_]\w*)*$')
|
||||
|
||||
def _strip_param(p):
|
||||
# Return the bare parameter name from a parameter spec harvested by
|
||||
# _parenparse(), discarding any default value or annotation. Default
|
||||
# values and annotations would otherwise be evaluated by exec() at
|
||||
# function-definition time. Star prefixes ("*args", "**kw") and bare
|
||||
# "*" / "/" are preserved as written.
|
||||
p = p.split('=', 1)[0]
|
||||
p = p.split(':', 1)[0]
|
||||
return p.strip()
|
||||
|
||||
debugstmts=[]
|
||||
def dbg(s): debugstmts.append(s)
|
||||
@@ -365,7 +388,13 @@ class Class(Scope):
|
||||
return c
|
||||
def get_code(self):
|
||||
str = '%sclass %s' % (self.currentindent(),self.name)
|
||||
if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers)
|
||||
# Only include base class expressions that are pure dotted names.
|
||||
# Anything else (calls, subscripts, conditionals, ...) is dropped
|
||||
# because exec() would evaluate it at class-definition time. See
|
||||
# the security note in the file header.
|
||||
safe_supers = [s.strip() for s in self.supers
|
||||
if _DOTTED_NAME_RE.match(s.strip())]
|
||||
if len(safe_supers) > 0: str += '(%s)' % ','.join(safe_supers)
|
||||
str += ':\n'
|
||||
if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
|
||||
if len(self.subscopes) > 0:
|
||||
@@ -382,8 +411,14 @@ class Function(Scope):
|
||||
def copy_decl(self,indent=0):
|
||||
return Function(self.name,self.params,indent, self.docstr)
|
||||
def get_code(self):
|
||||
# Strip default values and annotations from each parameter before
|
||||
# joining: exec() evaluates these at definition time and a hostile
|
||||
# buffer could otherwise execute arbitrary code via crafted def
|
||||
# headers. See file header for details.
|
||||
safe_params = [_strip_param(p) for p in self.params]
|
||||
safe_params = [p for p in safe_params if p]
|
||||
str = "%sdef %s(%s):\n" % \
|
||||
(self.currentindent(),self.name,','.join(self.params))
|
||||
(self.currentindent(),self.name,','.join(safe_params))
|
||||
if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
|
||||
str += "%spass\n" % self.childindent()
|
||||
return str
|
||||
|
||||
Reference in New Issue
Block a user