diff --git a/meta-python/recipes-devtools/python/python3-virtualenv/CVE-2026-113776.patch b/meta-python/recipes-devtools/python/python3-virtualenv/CVE-2026-113776.patch
new file mode 100644
index 0000000000..c1e034edb5
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-virtualenv/CVE-2026-113776.patch
@@ -0,0 +1,133 @@
+From 602b26623a724827ff171bf11fef4ad186d01f74 Mon Sep 17 00:00:00 2001
+From: Bernát Gábor <gaborjbernat@gmail.com>
+Date: Wed, 30 Sep 2026 23:01:05 -0700
+Subject: [PATCH] Merge commit from fork
+
+instantiate_template replaced one placeholder at a time across the whole
+template, so a placeholder name inside an already quoted value, such as a
+parent directory named __VIRTUAL_NAME__, was rewritten from inside the
+finished literal. The second value's quotes closed the first, and the rest
+of the path ran as code in bash, fish, xonsh and activate_this.py; in csh
+the prompt display tokens broke activate.csh the same way.
+
+Build every token's final text first and substitute them in one regex pass,
+with the csh display tokens joining that pass through substitutions().
+
+CVE: CVE-2026-113776
+Upstream-Status: Backport [https://github.com/pypa/virtualenv/commit/602b26623a724827ff171bf11fef4ad186d01f74]
+
+Backport Changes:
+- Omit the Csh display-token override and its specific test: 21.2.1 has
+  no separate display-token substitution or prompt-literal helpers.
+  Its Csh activator uses the corrected shared template substitution.
+- Exercise upstream's placeholder-in-parent-directory regression through
+  the existing Bash relocation fixture, since 21.2.1 lacks the later
+  prompt-test fixture. Add direct one-pass substitution checks for the
+  existing Bash, Csh, Fish and Python activators, and retain the 21.2.1
+  special-character fixture's other characters.
+
+Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
+---
+ src/virtualenv/activation/via_template.py | 21 +++++++++++++++++----
+ tests/conftest.py                         |  3 ++-
+ tests/unit/activation/test_activator.py   | 16 ++++++++++++++++
+ tests/unit/activation/test_bash.py        |  1 +
+ 4 files changed, 36 insertions(+), 5 deletions(-)
+
+diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py
+index 926c2b0..3af9dc5 100644
+--- a/src/virtualenv/activation/via_template.py
++++ b/src/virtualenv/activation/via_template.py
+@@ -1,6 +1,7 @@
+ from __future__ import annotations
+ 
+ import os
++import re
+ import shlex
+ import sys
+ from abc import ABC, abstractmethod
+@@ -84,10 +85,22 @@ class ViaTemplateActivator(Activator, ABC):
+         # read content as binary to avoid platform specific line normalization (\n -> \r\n)
+         binary = read_binary(self.__module__, template)
+         text = binary.decode("utf-8", errors="strict")
+-        for key, value in replacements.items():
+-            value_uni = self._repr_unicode(creator, value)
+-            text = text.replace(key, self.quote(value_uni))
+-        return text
++        substitutions = self.substitutions(replacements, creator)
++        # one pass over the template, so a placeholder name inside an already quoted value (a path component named
++        # __VIRTUAL_NAME__) stays data instead of being rewritten by a later key from inside the finished literal
++        pattern = re.compile("|".join(re.escape(key) for key in sorted(substitutions, key=len, reverse=True)))
++        return pattern.sub(lambda match: substitutions[match.group()], text)
++
++    def substitutions(self, replacements: dict[str, str], creator: Creator) -> dict[str, str]:
++        """Map each placeholder to the final text that replaces it in the template.
++
++        :param replacements: the raw placeholder values
++        :param creator: the creator of the environment
++
++        :returns: the placeholder to substituted text mapping
++
++        """
++        return {key: self.quote(self._repr_unicode(creator, value)) for key, value in replacements.items()}
+ 
+     @staticmethod
+     def _repr_unicode(creator: Creator, value: str) -> str:  # noqa: ARG004
+diff --git a/tests/conftest.py b/tests/conftest.py
+index 3ec4147..f27c450 100644
+--- a/tests/conftest.py
++++ b/tests/conftest.py
+@@ -279,7 +279,8 @@ def is_inside_ci():
+ 
+ @pytest.fixture(scope="session")
+ def special_char_name():
+-    base = "'\";&&e-$ èрт