new file mode 100644
@@ -0,0 +1,124 @@
+From e3173ef521c287be53f1976b28c2fee28b6de5c7 Mon Sep 17 00:00:00 2001
+From: Kevin Deldycke <kevin@deldycke.com>
+Date: Fri, 10 Apr 2026 18:12:24 +0200
+Subject: [PATCH] Add path normalization edge-cases, verify `shlex.split`
+ behavior
+
+Also move to Python comments details relevant to developers instead of docstrings
+Follow up to #3245
+
+CVE: CVE-2026-7246
+Upstream-Status: Backport [https://github.com/pallets/click/commit/b55294797ef32e22eb41e7d9657edb8faefa4976]
+
+Backport Changes:
+- Click 8.1.7 uses Editor.edit_file(filename), not the newer
+ Editor.edit_files(filenames) API. Apply the editor comment
+ follow-up and edge-case tests to the single-file API.
+- Omit pager changes and pager-only follow-up tests because
+ Click 8.1.7 uses older pager APIs and CVE-2026-7246 affects
+ click.edit(), not pager execution.
+- Omit cosmetic editor test ID renames.
+
+(cherry picked from commit b55294797ef32e22eb41e7d9657edb8faefa4976)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ src/click/_termui_impl.py | 12 ++++------
+ tests/test_termui.py | 48 +++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 52 insertions(+), 8 deletions(-)
+
+diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py
+index c50b37b..3589160 100644
+--- a/src/click/_termui_impl.py
++++ b/src/click/_termui_impl.py
+@@ -501,14 +501,7 @@ class Editor:
+ return "vi"
+
+ def edit_file(self, filename: str) -> None:
+- """Open a file in the user's editor.
+-
+- The editor command is split into an ``argv`` list with
+- :func:`shlex.split` in POSIX mode; see :func:`pager` for rationale.
+-
+- .. seealso::
+- :issue:`1026` and :pr:`1477`.
+- """
++ """Open a file in the user's editor."""
+ import shlex
+ import subprocess
+
+@@ -520,6 +513,9 @@ class Editor:
+ environ.update(self.env)
+
+ try:
++ # Split in POSIX mode (the default) for the same reasons as
++ # upstream pager(): strips quotes from tokens and preserves
++ # quoted Windows paths. See issue #1026 and PR #1477.
+ c = subprocess.Popen(
+ args=shlex.split(editor) + [filename],
+ env=environ,
+diff --git a/tests/test_termui.py b/tests/test_termui.py
+index 7cea338..eda9a80 100644
+--- a/tests/test_termui.py
++++ b/tests/test_termui.py
+@@ -421,6 +421,48 @@ def test_fast_edit(runner):
+ ["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl", "f.txt"],
+ id="escaped space in unix path (issue 1026)",
+ ),
++ pytest.param(
++ " vim ",
++ "f.txt",
++ ["vim", "f.txt"],
++ id="leading and trailing whitespace",
++ ),
++ pytest.param(
++ "vim\t--clean",
++ "f.txt",
++ ["vim", "--clean", "f.txt"],
++ id="tab-separated tokens",
++ ),
++ pytest.param(
++ "'/Applications/My Editor.app/Contents/MacOS/editor'",
++ "f.txt",
++ ["/Applications/My Editor.app/Contents/MacOS/editor", "f.txt"],
++ id="single-quoted path with spaces",
++ ),
++ pytest.param(
++ '"my editor" --wait --new-window',
++ "file 1.txt",
++ ["my editor", "--wait", "--new-window", "file 1.txt"],
++ id="quoted editor with flags and filename with spaces",
++ ),
++ pytest.param(
++ "vim -u NONE -N",
++ "f.txt",
++ ["vim", "-u", "NONE", "-N", "f.txt"],
++ id="multiple short flags",
++ ),
++ pytest.param(
++ "editor",
++ 'file"name.txt',
++ ["editor", 'file"name.txt'],
++ id="filename with double quote",
++ ),
++ pytest.param(
++ "editor",
++ "file'name.txt",
++ ["editor", "file'name.txt"],
++ id="filename with single quote",
++ ),
+ ],
+ )
+ def test_editor_path_normalization(editor_cmd, filename, expected_args):
+@@ -484,6 +526,12 @@ def test_editor_nonexistent_exception():
+ Editor(editor="nonexistent").edit_file("f.txt")
+
+
++def test_editor_unclosed_quote():
++ """An unclosed quote in the editor command raises ValueError."""
++ with pytest.raises(ValueError, match="No closing quotation"):
++ Editor(editor='"unclosed').edit_file("f.txt")
++
++
+ @pytest.mark.parametrize(
+ ("prompt_required", "required", "args", "expect"),
+ [
new file mode 100644
@@ -0,0 +1,201 @@
+From 1ad206945a88122e121f850b4942eff473e31cf5 Mon Sep 17 00:00:00 2001
+From: Kevin Deldycke <kevin@deldycke.com>
+Date: Wed, 4 Mar 2026 14:51:58 +0400
+Subject: [PATCH] Document and fix command string sanitizing with `shlex.split`
+
+Removes last use of `shell=True` use for command invokation for defense-in-depth.
+Refs: #1026, #1477 and #2775
+
+CVE: CVE-2026-7246
+Upstream-Status: Backport [https://github.com/pallets/click/commit/b96c2601af4e01341b4d2c0db494ebee4aef8f42]
+
+Backport Changes:
+- Click 8.1.7 uses Editor.edit_file(filename), not the newer
+ Editor.edit_files(filenames) API. Apply the argv-list change
+ to one filename without adding the multi-file API.
+- Adapt editor tests from b96c2601 to the single-file API. Keep
+ portable normalization, quoting, failure, environment, and
+ Windows cases.
+- Omit CHANGES.rst because release notes are not needed for
+ the source backport.
+- Omit pager changes and pager-only tests because Click 8.1.7
+ uses older pager APIs and CVE-2026-7246 affects click.edit(),
+ not pager execution.
+- Omit the unrelated _translate_ch_to_exc() return cleanup.
+
+(cherry picked from commit b96c2601af4e01341b4d2c0db494ebee4aef8f42)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ src/click/_termui_impl.py | 14 ++++-
+ tests/test_termui.py | 115 ++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 128 insertions(+), 1 deletion(-)
+
+diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py
+index f744657..c50b37b 100644
+--- a/src/click/_termui_impl.py
++++ b/src/click/_termui_impl.py
+@@ -501,6 +501,15 @@ class Editor:
+ return "vi"
+
+ def edit_file(self, filename: str) -> None:
++ """Open a file in the user's editor.
++
++ The editor command is split into an ``argv`` list with
++ :func:`shlex.split` in POSIX mode; see :func:`pager` for rationale.
++
++ .. seealso::
++ :issue:`1026` and :pr:`1477`.
++ """
++ import shlex
+ import subprocess
+
+ editor = self.get_editor()
+@@ -511,7 +520,10 @@ class Editor:
+ environ.update(self.env)
+
+ try:
+- c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True)
++ c = subprocess.Popen(
++ args=shlex.split(editor) + [filename],
++ env=environ,
++ )
+ exit_code = c.wait()
+ if exit_code != 0:
+ raise ClickException(
+diff --git a/tests/test_termui.py b/tests/test_termui.py
+index 7cfa939..7cea338 100644
+--- a/tests/test_termui.py
++++ b/tests/test_termui.py
+@@ -1,10 +1,12 @@
+ import platform
+ import time
++from unittest.mock import patch
+
+ import pytest
+
+ import click._termui_impl
+ from click._compat import WIN
++from click._termui_impl import Editor
+
+
+ class FakeClock:
+@@ -369,6 +371,119 @@ def test_fast_edit(runner):
+ assert result == "aTest\nbTest\n"
+
+
++@pytest.mark.parametrize(
++ ("editor_cmd", "filename", "expected_args"),
++ [
++ pytest.param(
++ "myeditor --wait --flag",
++ "file1.txt",
++ ["myeditor", "--wait", "--flag", "file1.txt"],
++ id="editor with args",
++ ),
++ pytest.param(
++ "vi",
++ 'file"; rm -rf / ; echo "',
++ ["vi", 'file"; rm -rf / ; echo "'],
++ id="shell metacharacters in filename",
++ ),
++ # Issue #1026: editor path with spaces must be quoted.
++ pytest.param(
++ '"C:\\Program Files\\Sublime Text 3\\sublime_text.exe"',
++ "f.txt",
++ ["C:\\Program Files\\Sublime Text 3\\sublime_text.exe", "f.txt"],
++ id="quoted windows path with spaces (issue 1026)",
++ ),
++ # PR #1477: pager/editor command with flags, like ``less -FRSX``.
++ pytest.param(
++ "less -FRSX",
++ "f.txt",
++ ["less", "-FRSX", "f.txt"],
++ id="command with flags (pr 1477)",
++ ),
++ # Issue #1026: quoted command with ``--wait`` flag.
++ pytest.param(
++ '"my command" --option value arg',
++ "f.txt",
++ ["my command", "--option", "value", "arg", "f.txt"],
++ id="quoted command with args (issue 1026)",
++ ),
++ # PR #1477: unquoted Unix path.
++ pytest.param(
++ "/usr/bin/vim",
++ "f.txt",
++ ["/usr/bin/vim", "f.txt"],
++ id="unix absolute path",
++ ),
++ # Issue #1026: macOS path with escaped space.
++ pytest.param(
++ "/Applications/Sublime\\ Text.app/Contents/SharedSupport/bin/subl",
++ "f.txt",
++ ["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl", "f.txt"],
++ id="escaped space in unix path (issue 1026)",
++ ),
++ ],
++)
++def test_editor_path_normalization(editor_cmd, filename, expected_args):
++ with patch("subprocess.Popen") as mock_popen:
++ mock_popen.return_value.wait.return_value = 0
++ Editor(editor=editor_cmd).edit_file(filename)
++
++ mock_popen.assert_called_once()
++ args = mock_popen.call_args[1].get("args") or mock_popen.call_args[0][0]
++ assert args == expected_args
++ assert mock_popen.call_args[1].get("shell") is None
++
++
++@pytest.mark.skipif(not WIN, reason="Windows-specific editor paths")
++@pytest.mark.parametrize(
++ ("editor_cmd", "expected_cmd"),
++ [
++ pytest.param(
++ "notepad",
++ ["notepad"],
++ id="plain notepad",
++ ),
++ pytest.param(
++ '"C:\\Program Files\\Sublime Text 3\\sublime_text.exe" --wait',
++ ["C:\\Program Files\\Sublime Text 3\\sublime_text.exe", "--wait"],
++ id="quoted path with flag",
++ ),
++ ],
++)
++def test_editor_windows_path_normalization(editor_cmd, expected_cmd):
++ """Verify that Popen receives unquoted Windows editor paths."""
++ with patch("subprocess.Popen") as mock_popen:
++ mock_popen.return_value.wait.return_value = 0
++ Editor(editor=editor_cmd).edit_file("f.txt")
++
++ args = mock_popen.call_args[1].get("args") or mock_popen.call_args[0][0]
++ assert args == expected_cmd + ["f.txt"]
++ assert mock_popen.call_args[1].get("shell") is None
++
++
++def test_editor_env_passed_through():
++ with patch("subprocess.Popen") as mock_popen:
++ mock_popen.return_value.wait.return_value = 0
++ Editor(editor="vi", env={"MY_VAR": "1"}).edit_file("f.txt")
++
++ env = mock_popen.call_args[1].get("env")
++ assert env is not None
++ assert env["MY_VAR"] == "1"
++
++
++def test_editor_failure_exception():
++ with patch("subprocess.Popen") as mock_popen:
++ mock_popen.return_value.wait.return_value = 1
++ with pytest.raises(click.ClickException, match="Editing failed"):
++ Editor(editor="vi").edit_file("f.txt")
++
++
++def test_editor_nonexistent_exception():
++ with patch("subprocess.Popen", side_effect=OSError("not found")):
++ with pytest.raises(click.ClickException, match="not found"):
++ Editor(editor="nonexistent").edit_file("f.txt")
++
++
+ @pytest.mark.parametrize(
+ ("prompt_required", "required", "args", "expect"),
+ [
@@ -12,7 +12,10 @@ SRC_URI[sha256sum] = "ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b
inherit pypi setuptools3 ptest
-SRC_URI += "file://run-ptest"
+SRC_URI += "file://run-ptest \
+ file://CVE-2026-7246.patch \
+ file://CVE-2026-7246-regression.patch \
+ "
CVE_PRODUCT = "palletsprojects:click"
@@ -36,6 +39,7 @@ CLEANBROKEN = "1"
RDEPENDS:${PN} += "\
python3-io \
python3-threading \
+ python3-shell \
"
BBCLASSEXTEND = "native nativesdk"