" Maintainer: <vacancy>
" Previous Maintainer: Aaron Griffin <aaronmgriffin@gmail.com>
" Version: 0.10
-" Last Updated: 2026 Jun 04
+" Last Updated: 2026 Jun 21
"
" Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim
"
" previous code passed buffer-supplied expressions to exec() which
" Python evaluates at definition time, allowing arbitrary code
" execution via crafted def/class headers
+" * use repr() on doc strings to prevent code execution
"
" v 0.9
" * Fixed docstring parsing for classes and functions
def get_code(self):
str = ""
- if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n'
+ if len(self.docstr) > 0: str += repr(self.docstr)+'\n'
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n'
for sub in self.subscopes:
str += sub.get_code()
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.docstr) > 0: str += self.childindent()+repr(self.docstr)+'\n'
if len(self.subscopes) > 0:
for s in self.subscopes: str += s.get_code()
else:
safe_params = [p for p in safe_params if p]
str = "%sdef %s(%s):\n" % \
(self.currentindent(),self.name,','.join(safe_params))
- if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
+ if len(self.docstr) > 0: str += self.childindent()+repr(self.docstr)+'\n'
str += "%spass\n" % self.childindent()
return str
" Maintainer: <vacancy>
" Previous Maintainer: Aaron Griffin <aaronmgriffin@gmail.com>
" Version: 0.10
-" Last Updated: 2026 Jun 04
+" Last Updated: 2026 Jun 21
"
" Changes
" TODO:
" previous code passed buffer-supplied expressions to exec() which
" Python evaluates at definition time, allowing arbitrary code
" execution via crafted def/class headers
+" * use repr() on doc strings to prevent code execution
"
" v 0.9
" * Fixed docstring parsing for classes and functions
def get_code(self):
str = ""
- if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n'
+ if len(self.docstr) > 0: str += repr(self.docstr)+'\n'
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n'
for sub in self.subscopes:
str += sub.get_code()
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.docstr) > 0: str += self.childindent()+repr(self.docstr)+'\n'
if len(self.subscopes) > 0:
for s in self.subscopes: str += s.get_code()
else:
safe_params = [p for p in safe_params if p]
str = "%sdef %s(%s):\n" % \
(self.currentindent(),self.name,','.join(safe_params))
- if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
+ if len(self.docstr) > 0: str += self.childindent()+repr(self.docstr)+'\n'
str += "%spass\n" % self.childindent()
return str
\ 'g:pythoncomplete_allow_import=1 did not run the buffer import')
endfunc
+func Test_python3complete_no_exec_via_class_docstring()
+ " A class-body docstring is emitted verbatim between triple quotes by
+ " get_code() and runs at class-definition time during exec(). A single-
+ " quoted source docstring lets an embedded """ survive doc()'s leading/
+ " trailing quote strip and break out of the generated literal.
+ let marker = tempname()
+ call s:CompleteAndExpectNoMarker([
+ \ 'class Foo:',
+ \ ' ''x"""+open("' . marker . '", "w").close()+"""y''',
+ \ ' pass',
+ \ 'Foo.',
+ \ ], marker,
+ \ 'class docstring expression was evaluated during omni-completion')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 699,
/**/
698,
/**/