---
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'
---

## Overview

## Summary
The CVE-2026-35536 fix added a validation loop that rejects `[\x00-\x20\x3b\x7f]`, but only for the
hardcoded **lowercase** keys `name`/`domain`/`path`/`samesite`. The still-live deprecated `**kwargs` path
writes attacker-supplied attribute values straight into the `Morsel` with no validation, and because
`Morsel.__setitem__` is case-insensitive, a capitalized kwarg (`Domain=`, `Path=`, `SameSite=`, `Max-Age=`)
routes to the same reserved attribute while bypassing the loop — re-opening `;`-delimited attribute injection.

```python
self.set_cookie("sid", "abc", Domain="evil.com; Secure; SameSite=None")
#  -> Set-Cookie: sid=abc; Domain=evil.com; Secure; SameSite=None; Path=/
# Sanity (the canonical lowercase named arg IS blocked):
self.set_cookie("sid", "abc", domain="evil.com; Secure")   # -> http.cookies.CookieError
```

The patch's regression test (`SetCookieForbiddenCharHandler`) only exercises the four named params, never the
`**kwargs` path, so the gap is not regression-covered.

## Affected code
- `tornado/web.py` → `RequestHandler.set_cookie`: the validation loop covers only the lowercase named args;
  the trailing `if kwargs:` loop does `morsel[k] = v` with no character validation.

## Steps to reproduce
`GET /upper` (uses `Domain=` kwarg) emits `Set-Cookie: c_upper=v; Domain=evil.com; Secure; SameSite=None; Path=/`; `GET /lower` (uses lowercase
`domain=`) returns a `CookieError`.

## Impact
Injection of independent cookie attributes (force/drop `Secure`/`HttpOnly`/`SameSite`, rebind `Domain`/`Path`)
— the same impact CVE-2026-35536 closed, via the sibling path the patch missed. Conditional on the app using
a capitalized/legacy keyword.

## Suggested remediation
Apply the same `[\x00-\x20\x3b\x7f]` validation to every entry in the `**kwargs` loop (after normalizing the
key case), or remove the deprecated kwargs path; add a regression test for capitalized kwargs.

## Credit
Reported as part of an incomplete-patch measurement study (responsible disclosure).

## Affected packages

- `tornado >= 6.5.5, < 6.5.8`

## Remediation

Upgrade to a patched release:

- `tornado 6.5.8`
