{"id":"GHSA-wwv5-g3v4-889x","title":"Tornado: Incomplete fix for CVE-2026-35536: cookie attribute injection re-opened via the legacy case-insensitive `**kwargs` path in `set_…","summary":"Tornado: Incomplete fix for CVE-2026-35536: cookie attribute injection re-opened via the legacy case-insensitive `**kwargs` path in `set_cookie`","severity":"low","vendor":"tornado","product":"tornado","ecosystem":"pip","affected":["tornado >= 6.5.5, < 6.5.8"],"patched":["tornado 6.5.8"],"published":"2026-09-01","updated":"2026-09-10","sourceUpdated":"2026-09-10T03:50:54.582059531Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-wwv5-g3v4-889x","references":[{"url":"https://github.com/tornadoweb/tornado/security/advisories/GHSA-wwv5-g3v4-889x"},{"url":"https://github.com/tornadoweb/tornado/pull/3704"},{"url":"https://github.com/tornadoweb/tornado/pull/3706"},{"url":"https://github.com/tornadoweb/tornado/commit/6ef836e43e1278530041376adb32504daa977b91"},{"url":"https://github.com/tornadoweb/tornado/commit/da284767eae8e1f0484f123b8c3225f6465b09c7"},{"url":"https://github.com/tornadoweb/tornado"},{"url":"https://github.com/tornadoweb/tornado/releases/tag/v6.5.8"},{"url":"https://github.com/advisories/GHSA-wwv5-g3v4-889x"}],"tags":["osv","pip","ghsa"],"cwe":["CWE-74"],"ingestedAt":"2026-09-01T20:32:02.223Z","slug":"GHSA-wwv5-g3v4-889x","body":"## Overview\n\n## Summary\nThe CVE-2026-35536 fix added a validation loop that rejects `[\\x00-\\x20\\x3b\\x7f]`, but only for the\nhardcoded **lowercase** keys `name`/`domain`/`path`/`samesite`. The still-live deprecated `**kwargs` path\nwrites attacker-supplied attribute values straight into the `Morsel` with no validation, and because\n`Morsel.__setitem__` is case-insensitive, a capitalized kwarg (`Domain=`, `Path=`, `SameSite=`, `Max-Age=`)\nroutes to the same reserved attribute while bypassing the loop — re-opening `;`-delimited attribute injection.\n\n```python\nself.set_cookie(\"sid\", \"abc\", Domain=\"evil.com; Secure; SameSite=None\")\n#  -> Set-Cookie: sid=abc; Domain=evil.com; Secure; SameSite=None; Path=/\n# Sanity (the canonical lowercase named arg IS blocked):\nself.set_cookie(\"sid\", \"abc\", domain=\"evil.com; Secure\")   # -> http.cookies.CookieError\n```\n\nThe patch's regression test (`SetCookieForbiddenCharHandler`) only exercises the four named params, never the\n`**kwargs` path, so the gap is not regression-covered.\n\n## Affected code\n- `tornado/web.py` → `RequestHandler.set_cookie`: the validation loop covers only the lowercase named args;\n  the trailing `if kwargs:` loop does `morsel[k] = v` with no character validation.\n\n## Steps to reproduce\n`GET /upper` (uses `Domain=` kwarg) emits `Set-Cookie: c_upper=v; Domain=evil.com; Secure; SameSite=None; Path=/`; `GET /lower` (uses lowercase\n`domain=`) returns a `CookieError`.\n\n## Impact\nInjection of independent cookie attributes (force/drop `Secure`/`HttpOnly`/`SameSite`, rebind `Domain`/`Path`)\n— the same impact CVE-2026-35536 closed, via the sibling path the patch missed. Conditional on the app using\na capitalized/legacy keyword.\n\n## Suggested remediation\nApply the same `[\\x00-\\x20\\x3b\\x7f]` validation to every entry in the `**kwargs` loop (after normalizing the\nkey case), or remove the deprecated kwargs path; add a regression test for capitalized kwargs.\n\n## Credit\nReported as part of an incomplete-patch measurement study (responsible disclosure).\n\n## Affected packages\n\n- `tornado >= 6.5.5, < 6.5.8`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `tornado 6.5.8`","depth":"sunlit","depthScore":14,"depthScoreParts":{"impact":13.8,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}