diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-111749.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-111749.patch
new file mode 100644
index 0000000000..475229e180
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-111749.patch
@@ -0,0 +1,205 @@
+From 6a07c171ef9f3be583e2907dcd1931372efca8d6 Mon Sep 17 00:00:00 2001
+From: Sam Bull <git@sambull.org>
+Date: Wed, 9 Sep 2026 02:21:06 +0100
+Subject: [PATCH] Fix host-only state on cookie expiration (#13674) (#13676)
+
+(cherry picked from commit 9332972cfcb0f5a2b6677e7307819ab8f13b5d09)
+
+CVE: CVE-2026-111749
+Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/6a07c171ef9f3be583e2907dcd1931372efca8d6]
+
+Backport Changes:
+- Refresh the runtime hunks for aiohttp 3.13.5 and its existing
+  JSON cookie persistence backport; retain Set/Tuple type spelling.
+- Omit annotation changes for the public host_only_cookies property
+  in CookieJar, DummyCookieJar, AbstractCookieJar and the test jar.
+  That property and its documentation were introduced in 3.14.
+- Omit the absent public-property test update and use private
+  _host_only_cookies assertions in the regression and JSON tests.
+  Adapt both existing JSON scope assertions to the private state.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ CHANGES/13674.bugfix.rst |  1 +
+ aiohttp/cookiejar.py     | 25 +++++++++-------
+ tests/test_cookiejar.py  | 64 ++++++++++++++++++++++++++++++++++++++--
+ 3 files changed, 77 insertions(+), 13 deletions(-)
+ create mode 100644 CHANGES/13674.bugfix.rst
+
+diff --git a/CHANGES/13674.bugfix.rst b/CHANGES/13674.bugfix.rst
+new file mode 100644
+index 0000000..5152bce
+--- /dev/null
++++ b/CHANGES/13674.bugfix.rst
+@@ -0,0 +1 @@
++Fixed host-only cookie state being lost on expiration -- by :user:`Dreamsorcerer`.
+diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py
+index b07d1fd..c5e5b09 100644
+--- a/aiohttp/cookiejar.py
++++ b/aiohttp/cookiejar.py
+@@ -139,7 +139,8 @@ class CookieJar(AbstractCookieJar):
+         self._morsel_cache: DefaultDict[Tuple[str, str], Dict[str, Morsel[str]]] = (
+             defaultdict(dict)
+         )
+-        self._host_only_cookies: Set[Tuple[str, str]] = set()
++        # Cookie identity is (domain, path, name).
++        self._host_only_cookies: Set[Tuple[str, str, str]] = set()
+         self._unsafe = unsafe
+         self._quote_cookie = quote_cookie
+         if treat_as_secure_origin is None:
+@@ -186,7 +187,7 @@ class CookieJar(AbstractCookieJar):
+                     if attr_val:
+                         morsel_data[attr] = attr_val
+                 # Persist or it reloads as a domain cookie and leaks to subdomains.
+-                if (domain, name) in self._host_only_cookies:
++                if (domain, path, name) in self._host_only_cookies:
+                     morsel_data["host_only"] = True
+                 if (exp := self._expirations.get((domain, path, name))) is not None:
+                     morsel_data["expires_timestamp"] = exp
+@@ -340,7 +341,7 @@ class CookieJar(AbstractCookieJar):
+ 
+     def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None:
+         for domain, path, name in to_del:
+-            self._host_only_cookies.discard((domain, name))
++            self._host_only_cookies.discard((domain, path, name))
+             self._cookies[(domain, path)].pop(name, None)
+             self._morsel_cache[(domain, path)].pop(name, None)
+             self._expirations.pop((domain, path, name), None)
+@@ -377,18 +378,12 @@ class CookieJar(AbstractCookieJar):
+                 domain = ""
+                 del cookie["domain"]
+ 
+-            if not domain and hostname is not None:
+-                # Set the cookie's domain to the response hostname
+-                # and set its host-only-flag
+-                self._host_only_cookies.add((hostname, name))
+-                domain = cookie["domain"] = hostname
+-
+             if domain and domain[0] == ".":
+                 # Remove leading dot
+                 domain = domain[1:]
+                 cookie["domain"] = domain
+ 
+-            if hostname and not self._is_domain_match(domain, hostname):
++            if domain and hostname and not self._is_domain_match(domain, hostname):
+                 # Setting cookies for different domains is not allowed
+                 continue
+ 
+@@ -404,6 +399,14 @@ class CookieJar(AbstractCookieJar):
+                 cookie["path"] = path
+             path = path.rstrip("/")
+ 
++            if not domain and hostname is not None:
++                self._host_only_cookies.add((hostname, path, name))
++                domain = cookie["domain"] = hostname
++            else:
++                # A cookie with an explicit Domain attribute replaces any
++                # host-only cookie with the same (domain, path, name) identity.
++                self._host_only_cookies.discard((domain, path, name))
++
+             if max_age := cookie["max-age"]:
+                 try:
+                     delta_seconds = int(max_age)
+@@ -493,7 +496,7 @@ class CookieJar(AbstractCookieJar):
+             for name, cookie in self._cookies[p].items():
+                 domain = cookie["domain"]
+ 
+-                if (domain, name) in self._host_only_cookies and domain != hostname:
++                if domain != hostname and p + (name,) in self._host_only_cookies:
+                     continue
+ 
+                 # Skip edge case when the cookie has a trailing slash but request doesn't.
+diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py
+index ba7ee32..f82861e 100644
+--- a/tests/test_cookiejar.py
++++ b/tests/test_cookiejar.py
+@@ -1625,6 +1625,44 @@ async def test_shared_cookie_with_multiple_domains() -> None:
+     assert "universal" in jar._morsel_cache[("", "")]
+ 
+ 
++async def test_host_only_marker_survives_same_name_expiry_on_other_path() -> None:
++    """Expiring a same-name cookie on another path must not clear host-only state."""
++    jar = CookieJar()
++    origin = URL("http://auth.example.com/")
++    subdomain = URL("http://evil.auth.example.com/")
++
++    jar.update_cookies_from_headers(["sid=secret; Path=/"], origin)
++    assert "sid" not in jar.filter_cookies(subdomain)
++
++    # Attacker-controlled descendant expires a same-name cookie on its own path.
++    jar.update_cookies_from_headers(
++        ["sid=gone; Domain=auth.example.com; Path=/attacker; Max-Age=0"],
++        subdomain,
++    )
++
++    assert ("auth.example.com", "", "sid") in jar._host_only_cookies
++    assert "sid" not in jar.filter_cookies(subdomain)
++    assert jar.filter_cookies(origin)["sid"].value == "secret"
++
++
++async def test_explicit_domain_replacement_clears_host_only_marker() -> None:
++    """A replacing cookie with an explicit Domain is a domain cookie."""
++    jar = CookieJar()
++    origin = URL("http://example.com/")
++    subdomain = URL("http://sub.example.com/")
++
++    jar.update_cookies_from_headers(["sid=hostonly; Path=/"], origin)
++    assert ("example.com", "", "sid") in jar._host_only_cookies
++    assert "sid" not in jar.filter_cookies(subdomain)
++
++    jar.update_cookies_from_headers(
++        ["sid=domainwide; Domain=example.com; Path=/"], origin
++    )
++
++    assert jar._host_only_cookies == set()
++    assert jar.filter_cookies(subdomain)["sid"].value == "domainwide"
++
++
+ # === Security tests for restricted unpickler and JSON save/load ===
+ 
+ 
+@@ -1775,7 +1813,7 @@ async def test_save_load_json_preserves_host_only_scope(tmp_path: Path) -> None:
+     jar_load = CookieJar()
+     jar_load.load(file_path=file_path)
+ 
+-    assert jar_load.host_only_cookies == frozenset({("auth.example.com", "sid")})
++    assert jar_load._host_only_cookies == {("auth.example.com", "", "sid")}
+     assert "sid" not in jar_load.filter_cookies(subdomain)
+     assert "sid" in jar_load.filter_cookies(issuer)
+ 
+@@ -1796,10 +1834,32 @@ async def test_save_load_json_domain_cookie_still_matches_subdomain(
+     jar_load = CookieJar()
+     jar_load.load(file_path=file_path)
+ 
+-    assert jar_load.host_only_cookies == frozenset()
++    assert jar_load._host_only_cookies == set()
+     assert "sid" in jar_load.filter_cookies(subdomain)
+ 
+ 
++async def test_save_load_json_host_only_per_path(tmp_path: Path) -> None:
++    """Verify save/load keeps host-only state per (domain, path, name)."""
++    file_path = tmp_path / "per_path.json"
++    origin = URL("https://example.com/")
++    subdomain = URL("https://sub.example.com/")
++
++    jar_save = CookieJar()
++    jar_save.update_cookies_from_headers(
++        ["sid=hostonly; Path=/", "sid=domainwide; Domain=example.com; Path=/api"],
++        origin,
++    )
++    jar_save.save(file_path=file_path)
++
++    jar_load = CookieJar()
++    jar_load.load(file_path=file_path)
++
++    assert jar_load._host_only_cookies == {("example.com", "", "sid")}
++    assert "sid" not in jar_load.filter_cookies(subdomain)
++    filtered = jar_load.filter_cookies(URL("https://sub.example.com/api/x"))
++    assert filtered["sid"].value == "domainwide"
++
++
+ async def test_save_load_json_preserves_max_age_deadline(tmp_path: Path) -> None:
+     """Verify save/load restores the absolute deadline without resetting it."""
+     file_path = tmp_path / "max_age.json"
+-- 
+2.35.6
diff --git a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
index 40ac847c01..17398ab323 100644
--- a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
+++ b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
@@ -25,6 +25,7 @@ SRC_URI += " \
     file://CVE-2026-111066_p1.patch \
     file://CVE-2026-111066_p2.patch \
     file://CVE-2026-111750.patch \
+    file://CVE-2026-111749.patch \
 "
 
 CVE_PRODUCT = "aiohttp:aiohttp"
