On Mon Jun 22, 2026 at 2:27 PM CEST, Hitendra Prajapati via 
lists.openembedded.org wrote:
> Pick patch from [1], [2] & [3] also mentioned at NVD report in [4,5 & 6]
>
> [1] https://github.com/vim/vim/commit/4b850457e12e1a678dd209f2868154f7553cbf8d
> [2] https://github.com/vim/vim/commit/63680c6d3d52477817b49cd1a66e7aabe8a7aa19
> [3] https://github.com/vim/vim/commit/c8c63673bc4253212820626aeeb75999d9a539d2
> [4] https://nvd.nist.gov/vuln/detail/CVE-2026-52858

Hello,

CVE-2026-52858 impacts wrynose and a patch must be sent for wrynose
before the fix can be merged on scarthgap.

I'm sorry but your whole set of vim patches is not acceptable on
scarthgap :(

Regards,


> [5] https://nvd.nist.gov/vuln/detail/CVE-2026-52859
> [6] https://nvd.nist.gov/vuln/detail/CVE-2026-52860
>
> Signed-off-by: Hitendra Prajapati <[email protected]>
> ---
>  .../vim/files/CVE-2026-52858.patch            | 167 +++++++
>  .../vim/files/CVE-2026-52859.patch            | 274 +++++++++++
>  .../vim/files/CVE-2026-52860.patch            | 446 ++++++++++++++++++
>  meta/recipes-support/vim/vim.inc              |   3 +
>  4 files changed, 890 insertions(+)
>  create mode 100644 meta/recipes-support/vim/files/CVE-2026-52858.patch
>  create mode 100644 meta/recipes-support/vim/files/CVE-2026-52859.patch
>  create mode 100644 meta/recipes-support/vim/files/CVE-2026-52860.patch
>
> diff --git a/meta/recipes-support/vim/files/CVE-2026-52858.patch 
> b/meta/recipes-support/vim/files/CVE-2026-52858.patch
> new file mode 100644
> index 0000000000..c7474f792b
> --- /dev/null
> +++ b/meta/recipes-support/vim/files/CVE-2026-52858.patch
> @@ -0,0 +1,167 @@
> +From 4b850457e12e1a678dd209f2868154f7553cbf8d Mon Sep 17 00:00:00 2001
> +From: Christian Brabandt <[email protected]>
> +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 <[email protected]>
> +
> +Upstream-Status: Backport from 
> [https://github.com/vim/vim/commit/4b850457e12e1a678dd209f2868154f7553cbf8d]
> +CVE: CVE-2026-52858
> +Signed-off-by: Hitendra Prajapati <[email protected]>
> +---
> + 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 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 ea0a331..aba3412 100644
> +--- a/runtime/autoload/python3complete.vim
> ++++ b/runtime/autoload/python3complete.vim
> +@@ -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
> + 
> +diff --git a/runtime/autoload/pythoncomplete.vim 
> b/runtime/autoload/pythoncomplete.vim
> +index aa28bb7..1014776 100644
> +--- a/runtime/autoload/pythoncomplete.vim
> ++++ b/runtime/autoload/pythoncomplete.vim
> +@@ -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
> + 
> +diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
> +index 597141d..c8572fe 100644
> +--- a/runtime/doc/filetype.txt
> ++++ b/runtime/doc/filetype.txt
> +@@ -739,7 +739,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*
> + 
> +-- 
> +2.34.1
> +
> diff --git a/meta/recipes-support/vim/files/CVE-2026-52859.patch 
> b/meta/recipes-support/vim/files/CVE-2026-52859.patch
> new file mode 100644
> index 0000000000..f734d7efe2
> --- /dev/null
> +++ b/meta/recipes-support/vim/files/CVE-2026-52859.patch
> @@ -0,0 +1,274 @@
> +From 63680c6d3d52477817b49cd1a66e7aabe8a7aa19 Mon Sep 17 00:00:00 2001
> +From: Christian Brabandt <[email protected]>
> +Date: Sat, 30 May 2026 16:34:40 +0000
> +Subject: [PATCH] patch 9.2.0565: [security]: out-of-bounds read in
> + update_snapshot()
> +
> +Problem:  Out-of-bounds read in update_snapshot() when a terminal cell
> +          fills all VTERM_MAX_CHARS_PER_CELL slots (a base character
> +          plus five combining marks): the loop over cell.chars[] has no
> +          upper bound and libvterm leaves the array unterminated when full, 
> so
> +          it reads past the array and appends out-of-bounds values to a
> +          buffer sized for only VTERM_MAX_CHARS_PER_CELL characters.
> +Solution: Bound the loop with i < VTERM_MAX_CHARS_PER_CELL, mirroring
> +          the loop in handle_pushline() (Christian Brabandt).
> +
> +Signed-off-by: Christian Brabandt <[email protected]>
> +
> +Upstream-Status: Backport from 
> [https://github.com/vim/vim/commit/63680c6d3d52477817b49cd1a66e7aabe8a7aa19]
> +CVE: CVE-2026-52859
> +Signed-off-by: Hitendra Prajapati <[email protected]>
> +---
> + src/terminal.c                          |   3 +-
> + src/testdir/samples/combining_chars.txt | 200 ++++++++++++++++++++++++
> + src/testdir/test_terminal3.vim          |  15 ++
> + 3 files changed, 217 insertions(+), 1 deletion(-)
> + create mode 100644 src/testdir/samples/combining_chars.txt
> +
> +diff --git a/src/terminal.c b/src/terminal.c
> +index 78990ac..527f1b9 100644
> +--- a/src/terminal.c
> ++++ b/src/terminal.c
> +@@ -2080,7 +2080,8 @@ update_snapshot(term_T *term)
> +                         int     i;
> +                         int     c;
> + 
> +-                        for (i = 0; (c = cell.chars[i]) > 0 || i == 0; ++i)
> ++                        for (i = 0; i < VTERM_MAX_CHARS_PER_CELL &&
> ++                                ((c = cell.chars[i]) > 0 || i == 0); ++i)
> +                             ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c,
> +                                          (char_u *)ga.ga_data + ga.ga_len);
> +                     }
> +diff --git a/src/testdir/samples/combining_chars.txt 
> b/src/testdir/samples/combining_chars.txt
> +new file mode 100644
> +index 0000000..d9a3c17
> +--- /dev/null
> ++++ b/src/testdir/samples/combining_chars.txt
> +@@ -0,0 +1,200 @@
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> ++á́́́́ጁ
> +diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim
> +index cb1946f..c02801e 100644
> +--- a/src/testdir/test_terminal3.vim
> ++++ b/src/testdir/test_terminal3.vim
> +@@ -1051,4 +1051,19 @@ func Test_terminal_max_combining_chars()
> +   exe buf . "bwipe!"
> + endfunc
> + 
> ++func Test_terminal_output_combining_chars()
> ++  CheckUnix
> ++  new
> ++  let cmd = "cat samples/combining_chars.txt"
> ++  let buf = term_start(cmd, {'curwin': 1, 'term_finish': 'open', 
> 'term_rows': 10, 'term_cols': 30})
> ++  call WaitForAssert({-> assert_match('finished', term_getstatus(buf))})
> ++  call TermWait(buf)
> ++  let lines = getbufline(buf, 1, '$')
> ++  " get byte lengths to confirm combining chars present
> ++  let lens = map(copy(lines), 'len(v:val)')
> ++  let expected = repeat([11], 190) + repeat([14], 10)
> ++  call assert_equal(expected, lens)
> ++  bw!
> ++endfunc
> ++
> + " vim: shiftwidth=2 sts=2 expandtab
> +-- 
> +2.34.1
> +
> diff --git a/meta/recipes-support/vim/files/CVE-2026-52860.patch 
> b/meta/recipes-support/vim/files/CVE-2026-52860.patch
> new file mode 100644
> index 0000000000..2f4ffdeacf
> --- /dev/null
> +++ b/meta/recipes-support/vim/files/CVE-2026-52860.patch
> @@ -0,0 +1,446 @@
> +From c8c63673bc4253212820626aeeb75999d9a539d2 Mon Sep 17 00:00:00 2001
> +From: Christian Brabandt <[email protected]>
> +Date: Thu, 4 Jun 2026 21:06:09 +0000
> +Subject: [PATCH] patch 9.2.0597: [security]: possible code execution with
> + python complete
> +
> +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
> +
> +Signed-off-by: Christian Brabandt <[email protected]>
> +
> +Upstream-Status: Backport from 
> [https://github.com/vim/vim/commit/c8c63673bc4253212820626aeeb75999d9a539d2]
> +CVE: CVE-2026-52860
> +Signed-off-by: Hitendra Prajapati <[email protected]>
> +---
> + runtime/autoload/python3complete.vim        |  43 +++-
> + runtime/autoload/pythoncomplete.vim         |  43 +++-
> + src/testdir/Make_all.mak                    |   2 +
> + src/testdir/test_plugin_python3complete.vim | 224 ++++++++++++++++++++
> + 4 files changed, 304 insertions(+), 8 deletions(-)
> + create mode 100644 src/testdir/test_plugin_python3complete.vim
> +
> +diff --git a/runtime/autoload/python3complete.vim 
> b/runtime/autoload/python3complete.vim
> +index aba3412..a031424 100644
> +--- a/runtime/autoload/python3complete.vim
> ++++ b/runtime/autoload/python3complete.vim
> +@@ -1,8 +1,8 @@
> + "python3complete.vim - Omni Completion for python
> + " Maintainer: <vacancy>
> + " Previous Maintainer: Aaron Griffin <[email protected]>
> +-" Version: 0.9
> +-" Last Updated: 2022 Mar 30
> ++" Version: 0.10
> ++" Last Updated: 2026 Jun 04
> + "
> + " Roland Puntaier: this file contains adaptations for python3 and is 
> parallel to pythoncomplete.vim
> + "
> +@@ -17,6 +17,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
> +@@ -100,6 +105,24 @@ warnings.simplefilter(action='ignore', 
> category=FutureWarning)
> + 
> + import sys, tokenize, io, 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)
> +@@ -347,7 +370,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:
> +@@ -364,8 +393,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
> +diff --git a/runtime/autoload/pythoncomplete.vim 
> b/runtime/autoload/pythoncomplete.vim
> +index 1014776..39b1efd 100644
> +--- a/runtime/autoload/pythoncomplete.vim
> ++++ b/runtime/autoload/pythoncomplete.vim
> +@@ -1,8 +1,8 @@
> + "pythoncomplete.vim - Omni Completion for python
> + " Maintainer: <vacancy>
> + " Previous Maintainer: Aaron Griffin <[email protected]>
> +-" 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)
> +@@ -362,7 +385,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:
> +@@ -379,8 +408,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
> +diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
> +index 0d4aeb0..87545b7 100644
> +--- a/src/testdir/Make_all.mak
> ++++ b/src/testdir/Make_all.mak
> +@@ -247,6 +247,7 @@ NEW_TESTS = \
> +     test_plugin_helptoc \
> +     test_plugin_man \
> +     test_plugin_matchparen \
> ++    test_plugin_python3complete \
> +     test_plugin_tar \
> +     test_plugin_termdebug \
> +     test_plugin_tohtml \
> +@@ -520,6 +521,7 @@ NEW_TESTS_RES = \
> +     test_plugin_helptoc.res \
> +     test_plugin_man.res \
> +     test_plugin_matchparen.res \
> ++    test_plugin_python3complete.res \
> +     test_plugin_tar.res \
> +     test_plugin_termdebug.res \
> +     test_plugin_tohtml.res \
> +diff --git a/src/testdir/test_plugin_python3complete.vim 
> b/src/testdir/test_plugin_python3complete.vim
> +new file mode 100644
> +index 0000000..e2b0c66
> +--- /dev/null
> ++++ b/src/testdir/test_plugin_python3complete.vim
> +@@ -0,0 +1,224 @@
> ++" Tests for the Python omni-completion plugin 
> (runtime/autoload/python3complete.vim).
> ++"
> ++CheckFeature python3
> ++
> ++" Run omni-completion against the given buffer contents and assert that the
> ++" marker file was not created.  Pre-patch behaviour exec()s reconstructed
> ++" def/class headers, which evaluates the buffer-supplied expression and
> ++" creates the marker file.  Post-patch, the expressions are stripped.
> ++func s:CompleteAndExpectNoMarker(buffer_lines, marker_path, msg)
> ++  call delete(a:marker_path)
> ++  defer delete(a:marker_path)
> ++  let g:pythoncomplete_allow_import = 0
> ++  new
> ++  setfiletype python
> ++  call setline(1, a:buffer_lines)
> ++  call cursor(line('$'), col([line('$'), '$']))
> ++
> ++  " The PoC trigger -- direct invocation of the omnifunc with an empty base.
> ++  " This is the same path Vim takes for CTRL-X CTRL-O.
> ++  silent! call python3complete#Complete(0, '')
> ++
> ++  call assert_false(filereadable(a:marker_path),
> ++        \ a:msg . ' (marker ' . a:marker_path . ' was created)')
> ++
> ++  bwipe!
> ++  unlet! g:pythoncomplete_allow_import
> ++endfunc
> ++
> ++func Test_python3complete_no_exec_via_function_default()
> ++  let marker = tempname()
> ++  call s:CompleteAndExpectNoMarker([
> ++        \ 'def f(x=open(' . string(marker) . ', "w").close()):',
> ++        \ '    pass',
> ++        \ 'f.',
> ++        \ ], marker,
> ++        \ 'function default expression was evaluated during 
> omni-completion')
> ++endfunc
> ++
> ++func Test_python3complete_no_exec_via_function_annotation()
> ++  let marker = tempname()
> ++  call s:CompleteAndExpectNoMarker([
> ++        \ 'def f(x: open(' . string(marker) . ', "w").close()):',
> ++        \ '    pass',
> ++        \ 'f.',
> ++        \ ], marker,
> ++        \ 'function annotation expression was evaluated during 
> omni-completion')
> ++endfunc
> ++
> ++func Test_python3complete_no_exec_via_class_base()
> ++  let marker = tempname()
> ++  " "or object" gives the class a valid base after the side-effecting
> ++  " open().close() expression returns None.  Without "or object" the
> ++  " exec would raise TypeError, but the file would still be created
> ++  " before the exception -- the assertion would still hold.  Using
> ++  " "or object" keeps the buffer parseable as valid Python.
> ++  call s:CompleteAndExpectNoMarker([
> ++        \ 'class Foo(open(' . string(marker) . ', "w").close() or object):',
> ++        \ '    pass',
> ++        \ 'Foo.',
> ++        \ ], marker,
> ++        \ 'class base expression was evaluated during omni-completion')
> ++endfunc
> ++
> ++func Test_python3complete_no_exec_with_multiple_params()
> ++  " The strip must apply to every parameter, not just the first.
> ++  let marker = tempname()
> ++  call s:CompleteAndExpectNoMarker([
> ++        \ 'def f(a, b=1, c=open(' . string(marker) . ', "w").close(), 
> d=2):',
> ++        \ '    pass',
> ++        \ 'f.',
> ++        \ ], marker,
> ++        \ 'non-first parameter default was evaluated during 
> omni-completion')
> ++endfunc
> ++
> ++func Test_python3complete_no_exec_via_starargs_default()
> ++  " "*args" and "**kw" must still be preserved after stripping; ensure a
> ++  " default following them is also stripped.
> ++  let marker = tempname()
> ++  call s:CompleteAndExpectNoMarker([
> ++        \ 'def f(*args, key=open(' . string(marker) . ', "w").close(), 
> **kw):',
> ++        \ '    pass',
> ++        \ 'f.',
> ++        \ ], marker,
> ++        \ 'keyword-only default after *args was evaluated during 
> omni-completion')
> ++endfunc
> ++
> ++func Test_python3complete_normal_completion_still_works()
> ++  " Positive control: completion against a buffer with a legitimate class
> ++  " must still produce completion items.  The stripping logic should not
> ++  " break the normal completion path.
> ++  let g:pythoncomplete_allow_import = 0
> ++
> ++  new
> ++  setfiletype python
> ++  call setline(1, [
> ++        \ 'class MyHelper:',
> ++        \ '    def alpha(self): pass',
> ++        \ '    def beta(self): pass',
> ++        \ 'h = MyHelper()',
> ++        \ 'h.',
> ++        \ ])
> ++  call cursor(5, 3)
> ++
> ++  " First call returns the column to start completion at; second returns
> ++  " the list of completion items.
> ++  let start = python3complete#Complete(1, '')
> ++  call assert_true(start >= 0,
> ++        \ 'python3complete#Complete(1, "") returned ' . start)
> ++
> ++  let items = python3complete#Complete(0, '')
> ++  " Items should be a list (possibly empty if the parser can't resolve "h",
> ++  " but should not be a parse error from our stripping changes).
> ++  call assert_equal(type([]), type(items),
> ++        \ 'python3complete#Complete(0, "") did not return a list')
> ++
> ++  bwipe!
> ++  unlet! g:pythoncomplete_allow_import
> ++endfunc
> ++
> ++func Test_python3complete_inherited_completion_via_dotted_base()
> ++  " Positive control for the class-base whitelist: a dotted-name base class
> ++  " (the common, safe case) must still be carried into the reconstructed
> ++  " source so that completion on a subclass can resolve inherited members.
> ++  let g:pythoncomplete_allow_import = 0
> ++
> ++  new
> ++  setfiletype python
> ++  call setline(1, [
> ++        \ 'class Base:',
> ++        \ '    def shared(self): pass',
> ++        \ 'class Derived(Base):',
> ++        \ '    def own(self): pass',
> ++        \ 'd = Derived()',
> ++        \ 'd.',
> ++        \ ])
> ++  call cursor(6, 3)
> ++
> ++  let items = python3complete#Complete(0, '')
> ++  call assert_equal(type([]), type(items),
> ++        \ 'completion against a subclass with a dotted base did not return 
> a list')
> ++
> ++  bwipe!
> ++  unlet! g:pythoncomplete_allow_import
> ++endfunc
> ++
> ++" Build a tiny Python module that creates a marker file as a side effect of
> ++" being imported, add its directory to sys.path, run omni-completion against
> ++" a buffer containing `import vimtest_marker_mod`, and report whether the
> ++" marker file was created.  Used by the two allow_import tests below.
> ++func s:RunImportCompletion(allow_import_value)
> ++  let g:pythoncomplete_allow_import = a:allow_import_value
> ++  let marker = tempname()
> ++  let module_dir = tempname()
> ++  call mkdir(module_dir, 'R')
> ++
> ++  call writefile([
> ++        \ 'open(' . string(marker) . ', "w").close()',
> ++        \ ], module_dir . '/vimtest_marker_mod.py')
> ++
> ++  defer delete(marker)
> ++
> ++  " Pass module_dir to Python via a g: variable so vim.eval() can read it.
> ++  let g:pythoncomplete_test_module_dir = module_dir
> ++  py3 << EOF
> ++import sys, vim
> ++_p = vim.eval('g:pythoncomplete_test_module_dir')
> ++if _p not in sys.path:
> ++    sys.path.insert(0, _p)
> ++# Drop any cached copy so the module body re-runs and the marker side
> ++# effect fires on import.
> ++sys.modules.pop('vimtest_marker_mod', None)
> ++EOF
> ++
> ++  new
> ++  setfiletype python
> ++  call setline(1, [
> ++        \ 'import vimtest_marker_mod',
> ++        \ 'vimtest_marker_mod.',
> ++        \ ])
> ++  call cursor(2, 2)
> ++
> ++  silent! call python3complete#Complete(0, '')
> ++
> ++  let ran = filereadable(marker)
> ++
> ++  bwipe!
> ++  unlet g:pythoncomplete_allow_import
> ++
> ++  " Teardown: restore sys.path, drop the cached module so a subsequent
> ++  " test run starts clean, clean up the temp module dir.
> ++  py3 << EOF
> ++import sys, vim
> ++_p = vim.eval('g:pythoncomplete_test_module_dir')
> ++if _p in sys.path:
> ++    sys.path.remove(_p)
> ++sys.modules.pop('vimtest_marker_mod', None)
> ++EOF
> ++  unlet g:pythoncomplete_test_module_dir
> ++  call delete(module_dir, 'rf')
> ++  call delete(marker)
> ++  unlet! g:pythoncomplete_allow_import
> ++
> ++  return ran
> ++endfunc
> ++
> ++func Test_python3complete_allow_import_off_blocks_imports()
> ++  " GHSA-52mc-rq6p-rc7c mitigation: with the default flag value (0), an
> ++  " `import` line harvested from the buffer must NOT be exec()'d.  The
> ++  " marker module's side effect (creating a file when its body runs) is
> ++  " the observable proof that the exec did or did not happen.
> ++  call assert_false(s:RunImportCompletion(0),
> ++        \ 'g:pythoncomplete_allow_import=0 did not block the buffer import')
> ++endfunc
> ++
> ++func Test_python3complete_allow_import_on_runs_imports()
> ++  " Symmetric positive control: with the flag set to non-zero, the harvested
> ++  " import IS exec()'d and the module loads.  Without this control the
> ++  " negative test above could pass for unrelated reasons (e.g. completion
> ++  " failing to parse the buffer at all).
> ++  call assert_true(s:RunImportCompletion(1),
> ++        \ 'g:pythoncomplete_allow_import=1 did not run the buffer import')
> ++endfunc
> ++
> ++" vim: shiftwidth=2 sts=2 expandtab
> +-- 
> +2.34.1
> +
> diff --git a/meta/recipes-support/vim/vim.inc 
> b/meta/recipes-support/vim/vim.inc
> index 95d00a09bd..4311c36f5b 100644
> --- a/meta/recipes-support/vim/vim.inc
> +++ b/meta/recipes-support/vim/vim.inc
> @@ -33,6 +33,9 @@ SRC_URI = 
> "git://github.com/vim/vim.git;branch=master;protocol=https \
>             file://CVE-2026-45130.patch \
>             file://CVE-2026-46483.patch \
>             file://CVE-2026-28420.patch \
> +           file://CVE-2026-52858.patch \
> +           file://CVE-2026-52859.patch \
> +           file://CVE-2026-52860.patch \
>             "
>  
>  PV .= ".1683"


-- 
Yoann Congal
Smile ECS

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#239667): 
https://lists.openembedded.org/g/openembedded-core/message/239667
Mute This Topic: https://lists.openembedded.org/mt/119922659/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to